본문 바로가기
코스웨어/10년 스마트폰BSP

[BSP]업무일지 - 서동준 - 20100607

by 알 수 없는 사용자 2010. 6. 8.
728x90
반응형

함수의 인수 전달 방법
1) 값 전달 호출 (call-by-value)
2) 주소 전달 호출 (call-by-address)

1) 값 전달 호출
호출자가 실인수를 이용하여 피호출자의 형식인수에게 값을 건네주는 방법
실인수는 변수나 상수 또는 수식이 될 수 있다.
실인수는 피호출 함수의 실행에 의해서 영향을 받지 않는다.

2) 주소 전달 호출
호출자가 실인수를 이용하여 변수의 주소값을 피호출자의 형식인수에게 값을 넘겨주는 방법
실인수는 변수의 주소값이어야 한다.
피호출자가 변수의 주소를 이용해서 실인수의 값을 수정할 수 있다.

예제6) 주소전달 호출을 이용하여 두 변수에 들어 있는 값을 바꾸는 프로그램
#include <stdio.h>

void swap(int *x, int *y);

int main()
{
    int x = 7;
    int y = 9;

    printf("%16s %3s = %10d, %3s = %10d\n", "A: initial value", "x", x, "y", y);
    printf("%16s %3s = %10p, %3s = %10p\n\n", "", "&x", &x, "&y", &y);

    swap(&x, &y);

    printf("%-16s %3s = %10d, %3s = %10d\n", "G: end of main", "x", x, "y", y);
    printf("%-16s %3s = %10p, %3s = %10p\n", "", "&x", &x, "&y", &y);

    return 0;
}

void swap(int *px, int *py)
{
    int temp;
    printf("%-16s %3s = %10p, %3s = %10p\n", "    C: ", "px", px, "py", py);
    printf("%-16s %3s = %10d, %3s = %10d\n\n", "    D: ", "*px", *px, "*py", *py);

    temp = *px;
    *px = *py;
    *py = temp;

    printf("%-16s %3s = %10p, %3s = %10p\n", "    E: ", "px", px, "py", py);
    printf("%-16s %3s = %10d, %3s = %10d\n\n", "    F: ", "*px", *px, "*py", *py);

    return;
}

Result--------------------------------------------------------------------------------------------------------------

A: initial value   x =          7,   y =          9
                  &x = 0xbf89d6bc,  &y = 0xbf89d6b8

    C:            px = 0xbf89d6bc,  py = 0xbf89d6b8
    D:           *px =          7, *py =          9

    E:            px = 0xbf89d6bc,  py = 0xbf89d6b8
    F:           *px =          9, *py =          7

G: end of main     x =          9,   y =          7
                  &x = 0xbf89d6bc,  &y = 0xbf89d6b8

Result--------------------------------------------------------------------------------------------------------------
설명6) 
swap() 함수에서 값(value)을 받아온다면 서로 값을 바꾼 후, 그 값을 다시 main() 함수의 x, y변수에 대입해 주어야 하나, 모든 함수는 하나의 값만 반환할 수 있으므로 x, y 중 하나에만 값을 대입할 수 있다. 

또한 swap() 함수가 종료가 되면 할당받은 메모리 영역은 모두 시스템(운영체제)에 반납된다. 따라서 위와 같이 main() 함수에 선언된 두 변수의 주소(reference)를 받아오면 그 주소가 가리키는 곳에 직접 접근하여 값을 넣어 줄 수 있으며, 값을 반환해 줄 필요도 없다.

#참고) 프로그램 구조상 두 변수의 값을 서로 바꾸기 위해서는 반드시 임시로 값을 저장하기 위한 하나의 변수가 더 필요하다.


예제7) 두 수의 합을 함수의 인수로 받는 프로그램
#include <stdio.h>

void sum(int a, int b, int *c);

int main()
{
    int a = 4;
    int b = 5;
    int c;

    sum(a, b, &c);
    printf("%s %d\t%s %d\t%s %d\n", "a:", a, "b:", b, "c:", c);

    return 0;
}

void sum(int ax, int bx, int *cx)
{
    *cx = ax + bx;
    return ;
}

Result--------------------------------------------------------------------------------------------------------------

a: 4    b: 5    c: 9

Result--------------------------------------------------------------------------------------------------------------
설명7) 
위와 같이 두 수의 합을 구하는 함수를 작성하는 방법으로는 두 가지 방법이 있다.
1) a, b만 인자로 받아 그 합을 리턴하여 c에 대입하는 방법
2) a, b는 값으로, c는 주소로 넘겨받아 a, b의 합을 c의 주소가 가리키는 곳으로 직접 접근하여 대입하는 방법


#재귀 함수 (recursive function)
함수의 실행부 내에서 그 자신을 호출하는 함수
프로그램 내의 모든 반복문은 재귀호출로 바꿀 수 있으며, 그 역으로 모든 재귀호출은 반복문으로 고쳐 쓸 수 있다.

장점
1) 프로그램을 매우 간결하게 작성할 수 있다.

단점
1) 메모리 사용량 증가
2) 가독성 낮음

예제8) 양의 정수 n을 입력받아 재귀호출을 이용하여 n부터 1까지 출력하는 프로그램
#include <stdio.h>

void recursive_print(int);

int main()
{
    int num;

    printf("Please enter a positive integer: ");
    scanf("%d", &num);

    recursive_print(num);

    return 0;
}

void recursive_print(int n)
{
    if (n <= 0) return;
    else
    {
        printf("recursive_print: n = %d\n", n);
        recursive_print(n - 1);
    }

    return ;
}

Result--------------------------------------------------------------------------------------------------------------

Please enter a positive integer: 5
recursive_print: n = 5
recursive_print: n = 4
recursive_print: n = 3
recursive_print: n = 2
recursive_print: n = 1

Result--------------------------------------------------------------------------------------------------------------


#난수 생성함수 rand()

rand() 함수를 호출하면 매번 같은 난수가 반환된다. 매번 다른 난수를 생성하기 위해서 srand() 함수에 종자 값(seed value)을 주는 방법을 사용한다.
보통 종자 값으로 time(NULL) 함수를 이용한다. time() 함수는 1970년 1월 1일로부터 지금까지 경과된 시간을 초로 계산하여 반환한다.


#함수 포인터
변수나 파일을 포인터 변수를 이용하여 접근하듯이, 함수도 포인터를 이용하여 호출할 수 있다. 함수 포인터 (function pointer)는 함수의 실행코드의 시작주소를 가지고 있으며, 함수 포인터를 이용하여 함수를 호출할 수 있다. 배열이름이 메모리 주소값을 가지는 상수이듯ㅇ, 함수이름도 함수의 시작코드의 주소값을 가지는 주소값 상수로 볼 수 있다. 함수 포인터 변수의 선언 형식은 다음과 같다.

반환자료형 (* 함수포인터변수이름) (인수1, 인수2, ..., 인수n);

void test(int);

일반 포인터 변수 선언시 변수명만 제외하고 모두 동일하듯이 함수 포인터 변수 선언 역시 함수명을 제외한 나머지 부분은 동일하다.

1) 함수명을 제외한 나머지 부분 쓰기 : void      (int)
2) 원하는 함수 포인터 변수명 쓰기    : void   fp (int)
3) 포인터형이므로 앞에 * 쓰기          : void  *fp (int)   <----- 괄호를 쓰지 않으면 void형 포인터를 리턴하는 함수로 인식
4) 괄호 쓰기                                    : void (*fp) (int)


실습1) 함수포인터 활용 예제
#include <stdio.h>

void test(int);

int main()
{
    void (*fp)(int);
    fp = test;

    printf("Address of %8s : %p\n", "main()", main);
    printf("Address of %8s : %p\n", "printf()", printf);
    printf("Address of %8s : %p\n\n", "scanf()", scanf);
    printf("Address of %-8s : %p\n", "test()", test);
    printf("value   of %-8s : %p\n\n", "fp", fp);

    printf("Result of %-38s : ", "test(100)"); test(100);
    printf("Result of %-38s : ", "fp(100)"); fp(100);
    printf("Result of %-38s : ", "((void(*)(int))0x8048576)(100)"); ((void(*)(int))0x8048576)(100);

    printf("Result of %-38s : ", "((int(*)(const char *, ...))0x8048370)");
    ((int(*)(const char *, ...))0x8048370)("Calling printf()... [Ok]\n");

    return 0;
}

void test(int A)
{
    printf("%d\n", A);

    return ;
}

Result--------------------------------------------------------------------------------------------------------------

Address of   main() : 0x8048444
Address of printf() : 0x8048370
Address of  scanf() : 0x8048380

Address of test()   : 0x8048576
value   of fp       : 0x8048576

Result of test(100)                              : 100
Result of fp(100)                                : 100
Result of ((void(*)(int))0x8048576)(100)         : 100
Result of ((int(*)(const char *, ...))0x8048370) : Calling printf()... [OK]

Result--------------------------------------------------------------------------------------------------------------




실습2) 테스트
#include <stdio.h>

void test(int);

int main()
{
    void (*fp)(int);
    fp = test;

    printf("%8s : %p\n", "&test", &test);
    printf("%8s : %p\n", "test", test);
    printf("%8s : %p\n\n", "*test", *test);

    printf("%8s : %p\n", "&fp", &fp);
    printf("%8s : %p\n", "fp", fp);
    printf("%8s : %p\n\n", "*fp", *fp);

    printf("Result of %-38s : ", "test(100)"); test(100);
    printf("Result of %-38s : ", "(&test)(100)"); (&test)(100);
    printf("Result of %-38s : ", "(*test)(100)"); (*test)(100);
    printf("Result of %-38s : ", "((void(*)(int))0x80485b0)(100)"); ((void(*)(int))0x80485b0)(100);

    printf("\n");

    printf("Result of %-38s : ", "fp(100)"); fp(100);
    printf("Result of %-38s : ", "(*fp)(100)"); (*fp)(100);
    //printf("Result of %-38s : ", "(&fp)(100)"); (&fp)(100);   // error

    return 0;
}

void test(int A)
{
    printf("%d\n", A);

    return ;
}

Result--------------------------------------------------------------------------------------------------------------

 &test   : 0x80485b0
  test   : 0x80485b0
 *test   : 0x80485b0

     &fp : 0xbfb6e9fc
      fp : 0x80485b0
     *fp : 0x80485b0

Result of test(100)                              : 100
Result of (&test)(100)                           : 100
Result of (*test)(100)                           : 100
Result of ((void(*)(int))0x80485b0)(100)         : 100

Result of fp(100)                                : 100
Result of (*fp)(100)                             : 100

Result--------------------------------------------------------------------------------------------------------------
설명2)
test, &test,*test의 값을 확인해보면 모두 같음을 알 수 있다.
변수를 선언하면 심볼테이블에 등록되듯이 함수 역시 선언하면 심볼테이블에 등록이 된다.
등록할 때에 타입과 이름, 주소가 필요하다. 따라서 이름은 함수명에서 괄호를 뺀 test가 되고 주소는 test() 함수의 주소가 된다.
그리고 타입은 void(*)(int)가 된다. 타입이 포인터이므로 이름 test에 들어있는 주소를 따라가면 역시 함수의 시작주소가 나온다.
그러므로 test와 &test, *test의 값이 모두 같다.

728x90