구조체 내의 함수 포인터
Coddy C 여정의 Object Oriented Programming 섹션에 포함된 레슨 — 61개 중 38번째.
지금까지 우리는 함수 포인터를 독립적인 변수로 사용하거나 인수로 전달해 왔습니다. 다형성을 향한 다음 단계는 함수 포인터를 구조체 내부에 직접 포함하여, 구조체에 고유한 동작(behavior)을 부여하는 것입니다.
구조체에 함수 포인터가 포함되어 있으면, 각 인스턴스는 서로 다른 함수를 가질 수 있습니다. 이는 같은 타입의 두 객체가 함수 포인터를 호출할 때 서로 다르게 동작할 수 있음을 의미합니다:
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // 멤버인 함수 포인터
} Printer;Printer 구조체는 이제 두 개의 멤버를 가집니다: 데이터(name)와 동작(print). 이를 사용하려면, 초기화 중에 포인터에 함수를 할당합니다:
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // 출력: !!! Hello !!!
return 0;
}구조체를 통해 함수를 호출하는 방식에 주목하세요: p.print("Hello"). 이는 다른 언어에서 객체의 메서드를 호출하는 것과 매우 유사해 보입니다. 구조체는 데이터와 그 데이터를 처리하는 함수를 모두 가지고 있습니다.
이 패턴은 C 언어에서 다형성의 기초입니다. 서로 다른 인스턴스에 서로 다른 함수를 할당할 수 있어, 동일한 struct 타입에서 서로 다른 동작을 생성할 수 있습니다.
챌린지
쉬움구조체가 함수 포인터를 통해 어떻게 자체적인 동작을 가질 수 있는지 보여주는 Notifier 시스템을 만들어 보겠습니다.
코드를 정리하기 위해 두 개의 파일을 생성합니다:
notifier.h:const char*매개변수를 받고 아무것도 반환하지 않는NotifyFunc라는 함수 포인터 타입을 정의합니다. 그런 다음name(const char*)과NotifyFunc타입의notify함수 포인터를 포함하는Notifier구조체를 정의합니다.main.c: 헤더 파일을 포함하고 다음 두 가지 알림 함수를 구현합니다:alert_notify— 메시지를[ALERT] message형식으로 출력합니다.info_notify— 메시지를[INFO] message형식으로 출력합니다.
Notifier인스턴스를 생성하고, 입력에 따라 적절한 함수를 연결한 다음, 구조체의 함수 포인터를 통해 알림을 호출합니다.
프로그램은 두 개의 입력을 받습니다: 알림 유형 (1은 alert, 2는 info)과 표시할 메시지입니다.
유형에 따라 해당 함수를 노티파이어의 notify 멤버에 할당한 다음, 구조체를 통해 함수를 호출하여 메시지를 표시하세요.
유형이 1이고 메시지가 "System starting"일 때의 출력 예시:
[ALERT] System starting유형이 2이고 메시지가 "All systems normal"일 때의 출력 예시:
[INFO] All systems normal헤더 파일에 include guard를 사용하는 것을 잊지 마세요. 그리고 구조체 멤버를 통해 함수를 호출하세요: n.notify(message)
치트 시트
함수 포인터는 구조체 내부에 직접 포함될 수 있으며, 각 인스턴스에 고유한 동작을 부여합니다. 이는 C 언어에서 다형성을 구현하는 기초가 됩니다.
함수 포인터를 포함한 구조체 정의하기
먼저 함수 포인터 타입을 정의한 다음, 이를 구조체 멤버로 포함시킵니다.
typedef void (*PrintFunc)(const char*);
typedef struct {
const char* name;
PrintFunc print; // Function pointer as a member
} Printer;구조체 사용하기
초기화 중에 포인터에 함수를 할당한 다음, 구조체를 통해 해당 함수를 호출합니다.
void loud_print(const char* msg) {
printf("!!! %s !!!\n", msg);
}
int main() {
Printer p;
p.name = "Alert";
p.print = loud_print;
p.print("Hello"); // Output: !!! Hello !!!
return 0;
}p.print("Hello") 구문은 구조체를 통해 함수를 호출하며, 이는 다른 언어에서 객체의 메서드를 호출하는 것과 유사합니다.
다형성 동작
동일한 구조체 타입의 서로 다른 인스턴스에 서로 다른 함수를 할당할 수 있으므로, 동일한 구조체 타입에서 다양한 동작을 만들어낼 수 있습니다.
직접 해보기
#include <stdio.h>
#include "notifier.h"
// TODO: alert_notify 함수 구현하기
// 출력 형식: [ALERT] message
// TODO: info_notify 함수 구현하기
// 출력 형식: [INFO] message
int main() {
int type;
char message[256];
// 입력 읽기
scanf("%d", &type);
getchar(); // 개행 문자 소비
fgets(message, sizeof(message), stdin);
// message 끝에 개행 문자가 있다면 제거
int len = 0;
while (message[len] != '\0') len++;
if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
// TODO: Notifier 인스턴스 생성
// TODO: type에 따라 (1은 alert, 2는 info),
// notifier의 notify 멤버에 적절한 함수 할당
// TODO: 구조체의 함수 포인터를 통해 알림 호출
// 사용법: n.notify(message)
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.