함수 포인터는 반환형+(*함수이름)+인자로 이루어진다. 함수 이름을 진한색으로 표시한 이유는 단순 함수 이름이 아니라 (*)의 함수 이름이기 때문이다.
printf의 함수 원형(프로토타입)
int printf (const char*, ...);
↓↓↓↓
타입 추출 (함수 이름만 제거)
int () (const char*, ...);
↓↓↓↓
int (*) (const char*, ...);
symbol table로 나타내면
반환형 |
함수 이름 |
주소 |
int (*) (const char*, ...) |
printf |
?? |
즉, printf의 함수 타입을 추출해 보면 int (*) (const char*, ...);란 결과를 얻을 수 있다.
자 그럼 내가 만든 함수에서 return 값으로 printf 함수 자체를 반환하면 어떻게 함수 구현을 해야 할까?
일단, 함수의 기본을 보면 예를 들어서 인자는 없고 int형 test함수를 만들고 싶다고 생각했다면 int test(void) { ~~~}를 떠올릴 수 있다. 여기서 반환형은 int, 함수 이름은 test, 인자는 (void) 이다.
방금 printf 함수의 반환형 int (*) (const char*, ...)라고 하였다. 그렇다면 return printf을 하여서 나오는 반환형은 저 부분이 되어야 하니까
int (*) (const char*, ...) { ~~ return printf } 가 되게 된다. 여기서 함수 이름을 test로 한다고 하면
int (*test (void)) (const char*, ...) {~~ return printf }
여기서 test(void) 함수 대신 뭐 p를 쓴다던가 단순 변수를 쓰게 되면 그냥 변수가 돼버린다. 주의!!!
이렇게 printf 함수를 반환하는 test함수를 만들었다면, 이걸 main에서 호출해 보자. 방금 만든 test함수의 원형을 살펴보면
맨 처음에 했던 타입 추출하는 방법대로 함수 이름 부분을 (*)로 바꿔주면 된다.
즉, int (*(*)(void)) (const char*, ...) 가 함수 타입 추출이다. 그렇다면 여기에 call이란 함수 포인터 변수를 만들어 준다면?
int (*(*call)(void)) (const char*, ...)
int main()
{
int (*(*call)(void) (const char*, ...);
call = test;
}
이렇게 해주면 main에서 내가 만들어준 함수인 test를 호출하고 test함수에서 printf 함수를 리턴하는 것까지 에러, 경고 없이 컴파일이 된다.