관리 메뉴

IT 컴퓨터공학 자료실

중급편 6. 함수 포인터의 사용법 본문

컴퓨터공학/포인터

중급편 6. 함수 포인터의 사용법

윤맨1 2015. 6. 30. 11:19

                                            포인터 토대가 된 책

                                              함수 포인터 사용

포인터로 가리킬 수있는 것은 일반 데이터뿐만 아니다. 프로그램의 단편 인 함수조차도 포인터로 포인터 변수에 저장할 수있다. 예를 들어, 조건에 따라 덧셈을 할 것인가 뺄셈을 할 것인지 변화라는 프로그램이 있다고하자.

int add (int a, int b) {
        return a + b;
}

int sub (int a, int b) {
        return a - b;
}

void main () {
        int cond;
        int x, y;
        .......
        if (cond == 0) {
                printf ( "result = % d \ n", add (x, y));
        } else if (cond == 1) {
                printf ( "result = % d \ n", sub (x, y));
        } else {
                printf ( "I do NOT know! \ n");
        }
}

이것은 함수 포인터를 사용하면 하나의 함수 호출에서 어느 적절한 함수가 호출되는 것을 실현할 수있다.

void main () {
        int (* func [2]) (int, int);   / * 함수 포인터 배열의 선언 * /
        int cond x, y;
        func [0] = add;              / * 할당 * /
        func [1] = sub;
        ............
        if (cond> = 0 && cond <= 1) {
                printf ( "result = % d \ n", (* func [cond) (x, y));
        } else {
                printf ( "I do NOT know! \ n");
        }
}

이것은이 예제와 같은 간단한 예제에서는별로 메리트가 없지만, 인터프리터의 경우는 이른바 '내장 함수'는 대부분이 함수 포인터를 사용하여 구현된다. 또는 이른바 '크리티컬 섹션 "조금이라도 동작이 빠른 것이 요구된다 부분에서 굳이이 기술이 사용될 수도있다. 그러나 초보자는이 함수 포인터의 이해가 어렵고, 이해 할 수없는 경우도 있으므로, 사용에는 일정한 규모가 필요하다.

또한 윈도우 시스템에서 많이 사용되는 "콜백 함수"(이벤트가 발생했을 때 실행되는 함수)와 신호 처리기 (signal을 받았을 때 동작하는 함수) 등의 이른바 '이벤트 드리븐'구현 의 경우에는 실제로 작동하는 함수의 포인터를 그것을 등록하는 함수에 대한 인수로서 건네 준다. 예를 들어, signal (2)의 선언은 다음과 같이되어있다.

void (* signal (int signum, void (* handler) (int))) (int);

이것은 다음과 같이 사용한다.

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void ctrl_C_handler (int signo) {
        write (2, "Ctrl + C pressed \ n", 15);
}

void main ()
{
        signal (SIGINT, ctrl_C_handler);
        while (1);
}

이것은 1 번째 Ctrl + C를 누를 때 메시지가 표시됩니다. (두 번째는 끝난다. 이것은 UNIX 신호의 사양에 의한 다.) 이때 signal 시스템 콜의 두번째는 역시 함수 포인터이다.

즉, 선언은 다음과 같다.

함수의 반환 형식 (* 포인터 이름) (인수 목록);

추가 (*)가이 함수 포인터임을 나타내고, 인수 목록은 형식을 명시 할 뿐이므로 실제로는 형태만을 명시하면된다. 첫 번째 예라고

        int (* func [2]) (int x, int y);
        int (* func [2]) (int, int);

하지만 같은 것이다. 그러나 이러한 선언은 반환 형식 인수의 개수와 형태는 포인터의 형식 검사와 마찬가지로 엄격한 검사된다. 그래서,

        int (* func) (int x);
        int sub1 (int);
        int sub2 (int, int);
        float sub3 (int);

        func = sub1;   / * 형식이 일치하고 있기 때문에 OK * / 
        func = sub2;   / * 형식이 일치하지 않기 때문에 컴파일 오류 * / 
        func = sub3;   / * 형식이 일치하지 않기 때문에 컴파일 오류 * /

라는 식으로된다. 이를 방지하기 위해서는 당연히 캐스트를 이용할 수있다.

        int (* func) (int x);
        float sub3 (int);

        func = (int (*) (int)) sub3;

상당히 변칙적 인 캐스팅되지만, 가능하다. (단, 실행 결과에 의미가 있을지는 보장하지 않는다.)