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;
};