69.2 함수 포인터를 구조체 멤버로 사용하기
지금까지 구조체에는 일반 자료형으로 된 멤버를 넣었습니다. 하지만 함수 포인터도 포인터이므로 구조체 멤버로 넣을 수 있습니다.
함수 포인터를 구조체 멤버로 사용하려면 구조체를 정의할 때 멤버로 지정해주면 됩니다.
struct 구조체이름 { 반환값자료형 (*함수포인터이름)(매개변수자료형1, 매개변수자료형2); };
그럼 int형 반환값, int형 매개변수가 두 개인 함수를 구조체 멤버로 넣어보겠습니다.
int add(int a, int b) // int형 반환값, int형 매개변수 두 개 { return a + b; }
add 함수를 담을 수 있는 구조체 멤버는 이렇게 만듭니다.
struct Calc { // ↓ 반환값 자료형 int (*fp)(int, int); // 함수 포인터를 구조체 멤버로 지정 }; // ↑ ↖ 매개변수 자료형 // 멤버 이름
이제 구조체 멤버에 함수의 메모리 주소를 저장하고 사용해보겠습니다. 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.
struct_member_function_pointer.c
#include <stdio.h> struct Calc { int (*fp)(int, int); // 함수 포인터를 구조체 멤버로 지정 }; int add(int a, int b) // int형 반환값, int형 매개변수 두 개 { return a + b; } int main() { struct Calc c; c.fp = add; // add 함수의 메모리 주소를 구조체 c의 멤버에 저장 printf("%d\n", c.fp(10, 20)); // 30: 구조체 멤버로 add 함수 호출 return 0; }
실행 결과
30
구조체를 정의할 때 멤버 부분에 함수 포인터를 만들어줍니다. 여기서는 int형 반환값과 int형 매개변수 두 개를 가지고 있는 함수 포인터 fp를 넣었습니다.
struct Calc { int (*fp)(int, int); // 함수 포인터를 구조체 멤버로 지정 };
구조체 변수를 선언한 뒤 .(점)으로 함수 포인터 멤버에 접근하여 add 함수의 메모리 주소를 저장합니다(만약 구조체 포인터에 메모리를 할당했다면 c->fp = add가 되겠죠?).
struct Calc c; c.fp = add; // add 함수의 메모리 주소를 구조체 c의 멤버에 저장
이제 구조체 멤버 fp에 괄호를 붙여서 함수를 호출할 수 있습니다.
- 변수.함수포인터(매개변수1, 매개변수2);
printf("%d\n", c.fp(10, 20)); // 30: 구조체 멤버로 add 함수 호출
참고 | C 언어와 객체지향
보통 C 언어는 객체지향 문법을 지원하지 않는다고 알려져 있습니다. 하지만 C 언어에서도 구조체와 함수 포인터를 활용하면 충분히 객체지향으로 프로그래밍을 할 수 있습니다. 다음은 리눅스 커널에서 함수 포인터로 객체지향을 구현한 코드입니다.
include/linux/blkdev.h
struct block_device_operations { int (*open) (struct block_device *, fmode_t); void (*release) (struct gendisk *, fmode_t); int (*rw_page)(struct block_device *, sector_t, struct page *, int rw); int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long); long (*direct_access)(struct block_device *, sector_t, void **, unsigned long *pfn, long size); unsigned int (*check_events) (struct gendisk *disk, unsigned int clearing); int (*media_changed) (struct gendisk *); void (*unlock_native_capacity) (struct gendisk *); int (*revalidate_disk) (struct gendisk *); int (*getgeo)(struct block_device *, struct hd_geometry *); void (*swap_slot_free_notify) (struct block_device *, unsigned long); struct module *owner; };