함수 포인터는 선언이 복잡해 보여도 결국 호출 가능한 함수의 주소를 다루는 문법입니다.
int (*fp)(int, int)선언이 한 번에 안 읽힌다- 함수 자체를 넘기는 콜백 패턴을 다시 확인하고 싶다
switch대신 함수 포인터 배열을 써도 되는지 판단하고 싶다
숏컷 코드
c
int add(int a, int b) { return a + b; }
int (*fp)(int, int) = add;
printf("%d\n", fp(2, 3));문법
int (*fp)(int, int)는 "정수 두 개를 받아 정수를 반환하는 함수의 포인터"입니다.
fp는 이름(*fp)는 포인터라는 뜻(int, int)는 인자 목록- 맨 앞
int는 반환형
선언이 복잡해지면 typedef로 감싸는 편이 훨씬 읽기 쉽습니다.
c
typedef int (*BinaryOp)(int, int);사용 예시
함수 포인터의 가장 흔한 용도는 콜백입니다.
c
typedef void (*Logger)(const char *msg);
void log_stdout(const char *msg) {
printf("[LOG] %s\n", msg);
}
void process(int *data, int n, Logger logger) {
for (int i = 0; i < n; i++) {
if (data[i] < 0 && logger != NULL) {
logger("negative value");
}
}
}같은 시그니처의 함수를 목록으로 모아 dispatch table처럼 쓸 수도 있습니다.
c
typedef int (*BinaryOp)(int, int);
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
BinaryOp ops[] = {add, sub, mul};
printf("%d\n", ops[1](10, 3));체크포인트
- 선언이 복잡하면
typedef로 감싸는 편이 읽기 쉽습니다. - 알고리즘은 고정하고 행동만 바꾸고 싶으면 콜백이 잘 맞습니다.
- 같은 시그니처의 연산 여러 개를 선택하는 구조면 dispatch table이 자연스럽습니다.
- 호출 전에는
NULL여부를 확인하는 편이 안전합니다. qsort는 함수 포인터 API를 이해할 때 가장 대표적인 표준 라이브러리 예시입니다.
주의할 점
함수 포인터는 시그니처가 정확히 맞아야 합니다. 반환형이나 인자 타입이 다른 함수를 억지로 캐스팅해서 넘기면 호출 시점에 정의되지 않은 동작으로 이어질 수 있습니다.
참고 링크
1 sources