C

C기초 플러스 chap.9 함수

Barbarian developer 2024. 8. 11.

제군들!! 우린 거의 다 왔다!!

이 고비만 넘기면 포인터란놈을 맞이하러 간다!!

모자란 머리로 여기까지 온것 칭찬한다!!

함수놈의 머리를 부수고 우린 포인터를 잡으러 간다!!

베헤에에엥라라아아아앙!


아 그런데 이쯤 되니, 내가 우리 바바리안들을 모두 끌고 가기 힘들어 져서 새로운 선생을 모셔왔다!!인사해라!!

우리보다 더 바보인 해골바가지들을 교육하는 암흑 마녀다!!

암흑마녀는 주석에 설명을 표시한다고 한다!! 주석을 잘봐야 한다!!

그럼 나도 수업 들으러 간다!! 

베헤헤헤에에에엘라!!!!


함수의 정의

C 언어로 프로그래밍을 하다 보면 들어가는 값만 바뀔 뿐 같은 코드가 계속 반복되는 경우가 많습니다. 특히 같은 코드를 반복해서 작성하면 코드도 길어지고 중간에 실수할 가능성이 높아집니다.

C 언어에서는 함수(function)라는 기능을 제공하는데 특정 용도의 코드들을 한 곳에 모아놓은 것을 뜻합니다. 그래서 처음 한 번만 작성해놓으면 나중에 필요할 때 계속 불러 쓸 수 있습니다. 예를 들어 지금까지 사용했던 printf, scanf 등도 모두 C 언어에서 미리 만들어둔 함수입니다.

함수는 다양한 사용 방법이 있으므로 이제부터 각 유닛에서 함수의 사용법을 자세히 알아보겠습니다.

 

 

함수를 정의하는 방법은 다음과 같이 반환값 자료형, 함수 이름, ( ) (괄호)순으로 적어준 뒤 { } (중괄호) 안에 원하는 코드를 작성하면 됩니다(함수 이름을 짓는 방법은 변수 이름을 짓는 방법과 같습니다).

반환값자료형 함수이름()
{
    코드;
}

다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

function.c

#include <stdio.h>

void hello()    // 반환값이 없는 hello 함수 정의
{
    printf("Hello, world!\n");    // Hello, world! 출력
}

int main()
{
    hello();    // hello 함수 호출

    return 0;
}

실행 결과

Hello, world!

이제 함수를 만들려면 main 함수 바깥에서 작성해야 합니다. 다음과 같이 main 함수 위에서 반환값의 자료형은 void, 이름은 hello인 함수를 정의했습니다. 그리고 중괄호 안에서 printf "Hello, world!" 문자열을 출력했습니다.

void hello()    // 반환값이 없는 hello 함수 정의
{
    printf("Hello, world!\n");    // Hello, world! 출력
}

여기서 함수의 반환값 부분에 void를 적어주면 함수의 반환값이 없다는 뜻이 됩니다. 즉, void는 함수의 반환값이 없음을 나타낼 때와 포인터로 사용할 때의 의미가 다릅니다. 이 부분을 잘 기억해두세요.

함수를 만들었으니 사용을 해봐야겠죠? main 함수 안에서 hello();와 같이 함수 이름과 ()를 적어준 뒤 ; (세미콜론)을 붙이면 함수를 사용할 수 있습니다.

int main()
{
    hello();    // hello 함수 호출

    return 0;
}

이렇게 함수를 사용하는 방법을 "함수를 호출(call)한다"라고 부르기도 합니다.

다음은 main 함수와 hello 함수의 실행 순서입니다.

  1. main 함수 실행
  2. hello 함수 호출
  3. hello 함수 실행
  4. printf 함수 실행 및 "Hello world!" 출력
  5. hello 함수 종료
  6. 0 반환 및 main 함수 종료

 그림 45‑1 main 함수와 hello 함수의 실행 순서

 

지역변수

함수 안에 선언된 변수를 지역 변수라고 부르는데 이 지역 변수의 특징은 함수가 끝나면 사라진다는 점입니다.

void hello()
{
    int num1 = 10;    // 지역 변수. hello 함수가 끝나면 사라짐

    printf("Hello, %d", num1);
}

int main()
{
    hello();
    printf("%d", num1);    // 컴파일 에러: hello 함수의 지역 변수 num1은
                           // 함수 hello 안에서만 사용할 수 있음

    return 0;
}

hello 함수 안에 선언된 지역 변수 num1 hello 함수 안에서만 사용할 수 있고, 함수 바깥에서는 사용할 수 없습니다. 

//함수의 전달인자
/* lethead2.c */
#include <stdio.h>
#include <string.h>            /* streln()을 사용하기 위해 */
#define NAME "GIGATHINK, INC."
#define ADDRESS "101 Megabuck Plaza"
#define PLACE "Megapolis, CA 94904"
#define WIDTH 40
#define SPACE ' '

void show_n_char(char ch, int num);

int main(void)
{
    int spaces;
    
    show_n_char('*', WIDTH);   /* 기호 상수를 전달인자로 사용한다. */
    putchar('\n');              
    show_n_char(SPACE, 12);    /* 기호 상수를 전달인자로 사용한다. */
    printf("%s\n", NAME);
    spaces = (WIDTH - strlen(ADDRESS)) / 2;
                                /* 몇 개의 스페이가 필요한지    */
                                /* 프로그램이 계산하게 한다.      */
    show_n_char(SPACE, spaces); /* 변수를 전달인자로 사용한다.   */
    printf("%s\n", ADDRESS);
    show_n_char(SPACE, (WIDTH - strlen(PLACE)) / 2);
                                /* 표현식을 전달인자로 사용한다.    */
    printf("%s\n", PLACE);
    show_n_char('*', WIDTH);
    putchar('\n');
    
    return 0;
}

/* show_n_char() 함수의 */
void show_n_char(char ch, int num)
{
    int count;
    
    for (count = 1; count <= num; count++)
        putchar(ch);
}

return값을 가진 함수.

//return을 이용하여 함수로부터 값 리턴하기
/* lesser.c -- 둘 중에서 죽은 것을 구한다. */
#include <stdio.h>
int imin(int, int);

int main(void)
{
    int evil1, evil2;       //int형 변수 선언
    
    printf("두 정수를 입력하시오(끝내려면 q):\n");
    while (scanf("%d %d", &evil1, &evil2) == 2)   //입력 값을 변수에 저장
    {
        printf("(%d,%d)에서 작은 것은 %d.\n",      //변수를 출력하고, 
               evil1, evil2, imin(evil1,evil2));    //함수를 출력
        printf("두 정수를 입력하시오(끝내려면 q):\n");
    }
    printf("종료!.\n");
    
    return 0;
}

int imin(int n,int m)
{
    int min;        //int 형 변수 선언
    
    if (n < m)      //조건 : n보다 m이 적다면,
        min = n;    //참일때 : n을 min에 대입한다. 
    else            
        min = m;    //거짓일 때 : n을 min에 대입한다.
    
    return min;     //min의 값을 int imin으로 리턴한다.
}

 

  • return값을 쓰는 함수는 결과 값을 return해주면 된다.
  • 그럼 main함수에서 그 값을 출력하면 된다.

부정확한 함수의 예

//ANSI C 함수 프로토타입
// misuse.c -- 부정확하게 함수를 사용한다.
#include <stdio.h>
int imax();      /* ANSI C 이전 형식의 함수 선언 */
int main(void)
{
    printf("(%d,%d)에서 큰 것은 %d.\n", //함수의 매개변수를 두개로 정했으므로, 전달인자를 두개를 줘야 한다.
           3, 5, imax(3));             //그런데 여기선 전달인자를 하나만 입려했기 때문에 에러가 발생한다. 이것을 데이터형 불일치 에러라고 한다.
    printf("(%d,%d)에서 큰 것은 %d.\n", //여기선 함수의 매개와 전달인자를 입력했지만.
           3, 5, imax(3.0, 5.0));      //그런데 여기선 함수는 전달형인데, 전달인자가 실수형이라 오류가 발생한다.  
    return 0;
}

//함수의 정의
int imax(n, m)                  
int n, m;
{
    return (n > m ? n : m);     // 리턴에다 표현식을 써도 된다!
                                // '?'=3항 연산자라고 함. '?'왼쪽에 조건을 걸고, 
                                // 오른쪽에 참일 땐 n값을 출력하게, 거짓일 땐 m값을 출력한다.
}

  • 이렇게 잘못된 부정확 함수 사용하게 되면 값깨진 채로 출력되버린다.

 

//ANSI C해결책
//proto.c --함수 프로토타입을 사용한다.
#include <stdio.h>
int imax(int, int);       //매개변수에 전달인자 int형을 넣어준다.
int main(void)
{
    printf("(%d,%d)에서 큰 것은%d\n",
           3, 5, imax(3,5));        //데이터형 불일치 해소
    printf("(%d,%d)에서 큰 것은%d\n",
           3, 5, imax(3.0, 5.0));   //에러메지는 뜨지 않았지만, double형에서 int형으로 바꼈기 때문에
    return 0;                       //소수점 이하의 수는 모두 잘린다.
}

int imax(int n, int m)
{
    return (n > m ? n : m);
}

  • 위와 같이 바꿔주면 해결된다.

&연산자

//주소알아내기: &연산자
/* loccheck.c  -- 변수들이 어디에 저장되는지 확인한다  */
#include <stdio.h>
void mikado(int);                      /* 함수선언  */
int main(void)
{
    int pooh = 2, bah = 5;             /* main의 지역변수   */
    
    printf("main()에 있는 pooh = %d, &pooh = %p\n", // &는 어떤 변수가 저장되어 있는 주소를 알아낸다.
           pooh, &pooh);                           // 포인터의 포맷지정자는 %p이다.
    printf("mian()에 있는 bah = %d, &bah = %p\n", //
           bah, &bah);                          
    mikado(pooh);                                  
    
    return 0;
}

void mikado(int bah)                   /* 함수정의   */ //함수의 배개변수에 intbath를 선언했다.
{
    int pooh = 10;                     /* mikado()의 지역변수 */
    
    printf("mikado()에 있는, pooh = %d, &pooh = %p\n", //
           pooh, &pooh);
    printf("mikado()에 있는, bah = %d, &bah = %p\n", //
           bah, &bah);
}

  • 이 예제를 통해 우린 %p와 &변수 를사용하면 해당 변수의 값이 저장되어 있는 위치를 알 수 있다.
  • 방금 너희들은 포인터를 사용한거다. 변수의 값이 저장되어 있는 위치를 불러 오는 것이 포인터다.
  • 이걸 어디 써먹는지 궁금한 바바리안 들이 많을 것이다. 자, 다음 예제를 통해 알려주겠다. 

호출 함수에 있는 변수 바꾸기(포인터 사용)

//호출 함수에 있는 변수 바꾸기
/* swap1.c -- 맞교환 함수 제1 버전 */
#include <stdio.h>
void interchange(int u, int v); /* 함수 선언 */

int main(void)
{
    int x = 5, y = 10;          //전달인자 변수 데이터현 선언
    
    printf("교환전 x = %d, y = %d.\n", x , y); //포맷 지정자와 전달인자 변수 일치.
    interchange(x, y); //함수 사용(x,y 바꾸기) 시도.
    printf("교환후 x = %d, y = %d.\n", x, y); //포맷 지정자와 전달인자 변수 일치.
    
    return 0;
}

void interchange(int u, int v)  /* 함수정의  */ 
{
    int temp;
    
    temp = u;
    u = v;
    v = temp;
}

  • 자, 겉보기엔 뭐가 문제인지 모를거다.
  • 바바리안들이 딱 보고 이해하기엔 조금 어려울수 있다. 당황하지 마라.
  • 그래서 친히 다음 예제를 통해 디버깅을 시켜주겠다.

 

//swap2.c -- swap1.c 에서 무엇이 잘못되었는지 조사한다.
#include <stdio.h>
void interchange(int u, int v);

int main(void)
{
    int x = 5, y = 10; //전달인자 변수 데이터현 선언
    
    printf("교환전 x = %d, y = %d.\n", x , y);//포맷 지정자와 전달인자 변수 일치.
    interchange(x, y);//함수 사용(x,y 바꾸기)
    printf("교환후 x = %d, y = %d.\n", x, y);//포맷 지정자와 전달인자 변수 일치.
    
    return 0;
}
//함수의 정의
void interchange(int u, int v) 
{
    int temp; //temp 정수형 변수 선언
    
    printf("교환전 u = %d, v = %d.\n", u , v); 
    temp = u;
    u = v;
    v = temp;
    printf("교환후 u = %d, v = %d.\n", u, v);
}
  •  interchange()함수안에서는 u와 v값이 맞교환 되었다. 하지만, main에서는 다른 변수를 사용한다.
  • 그래서 u와 v의 값을 맞교환하는 것은 x와 y에 영향을 주지 못한다.
  • 리턴 값을 주는 함수가 아니기에, 지역변수에 불과한 u와 v가 전달인자인 x와 y값에 영향력을 끼치지 못하기 때문이다.
  • 그럼 return값을 준다면 어떻게 될까?
  • return값을 준다해도 크게 달라질건 없다. 
  • return은 값을 한개밖에 돌려주지 못하기 때문에, u나 v중 하나만 변경이 가능하다.
  • 따라서 우리는 포인터를 쓸수 밖에 없다는 것이다.

//함수 간의 커뮤니케이션에 포인터 사용하기
//swqp.c--포인터를 사용하여 맞교환을 바르게 수행한다.
#include <stdio.h>
void interchange(int * u, int * v); //' * '는 이 변소가 포인터라는 것을 나타낸다.

int main(void)
{
    int x=5, y =10;

    printf("교환전 x =%d, y= %d\n", x, y);
    interchange(&x,&y); //함수에 주소(가르키는 값)를 전달한다.
    printf("교환후 x=%d, y=%d\n", x, y);

    return 0;
}

void interchange(int * u, int * v)
{
    int temp;

    temp = *u; // temp는 u가 가르키는 값을 얻는다.
    *u=*v; //y는 v가 가르키는 값을 얻고,
    *v=temp; //v는 다시 temp가 가르키는 값을 얻게된다.
}

 

댓글