우리가 흔히 쓰는 변수 포인터(int *ptr)는 데이터가 저장된 주소를 가리킵니다. 마찬가지로 함수 포인터는 함수 코드가 저장된 메모리 주소를 가리키는 변수입니다. 함수 포인터를 사용하면 함수를 변수처럼 주고받거나, 실행 중에 어떤 함수를 호출할지 결정할 수 있습니다.
선언 및 사용법
void (*TaskPt)(void);
void: 이 함수 포인터가 가리키는 함수는 리턴값이 없습니다.
(*TaskPt): TaskPt라는 이름의 포인터 변수입니다.
(void): 이 함수 포인터가 가리키는 함수는 입력 매개변수(인자)가 없습니다.
해석: "TaskPt는 입력도 없고 출력도 없는 함수를 가리키는 포인터다."
TaskPt = &CallMe; // TaskPt points to CallMe
CallMe라는 함수의 주소를 TaskPt에 저장합니다. 이제 TaskPt는 CallMe 함수를 가리킵니다. 함수 이름 자체(CallMe)가 주소를 의미하므로 &를 생략하고 TaskPt = CallMe;라고 써도 됩니다.
(*TaskPt)(); // call the function to which it points
TaskPt가 가리키는 함수(CallMe)를 실행합니다. *를 통해 포인터를 역참조하여 함수를 찾아가고 ()를 통해 실행합니다. 단순히 TaskPt();라고 써도 실행됩니다.
콜백은 "나중에 다시 불러줘(Call me back)"라는 개념입니다. 내가 함수 A를 호출하면서 "이 작업이 끝나거나 특정 이벤트가 발생하면, 내가 넘겨준 함수 B를 실행해 줘"라고 부탁하는 방식입니다. 이때 넘겨주는 함수 B가 바로 콜백 함수입니다.
모듈 A (User Code): 사용자가 작성한 코드입니다.
모듈 B (OS): 운영체제나 라이브러리입니다.
과정:
사용자는 CallMe라는 함수를 먼저 작성합니다.
int count;
void CallMe(void){
count++;
}
일상생활 예시
상황: 식당에 가서 대기 명단에 이름을 적습니다.
일반 호출: 줄 서서 계속 기다리다가 내 차례가 되면 들어갑니다. (Blocking)
콜백: 전화번호(함수 포인터)를 남기고 쇼핑하러 갑니다. 자리가 나면(조건 충족) 직원이 전화를 겁니다(콜백 실행).
후킹은 함수 포인터를 활용한 또 다른 강력한 기법입니다. 운영체제나 프로그램의 중간 과정에 사용자가 만든 함수를 "끼워 넣는(Hook)" 것을 말합니다. 낚싯바늘(Hook)로 중간에 무언가를 낚아채는 것과 비슷합니다.
후킹의 용도
운영체제(OS)는 중요한 지점마다 사용자가 원하는 코드를 실행할 수 있도록 "빈자리(Hook point)"를 만들어 둡니다.
전략적 위치 (Strategic Places):
사용 방법:
1. 사용자가 함수를 작성합니다 (예: MyDebugFunction).
2. OS의 등록 함수(예: OS_AddHook)를 호출하면서 내 함수의 주소를 넘깁니다.
3. 해당 이벤트가 발생할 때마다 OS는 MyDebugFunction을 실행합니다.
장점: 디버깅할 때 매우 유용합니다. 예를 들어, 스케줄러가 실행될 때마다 로그를 남기는 함수를 끼워 넣으면 시스템의 동작을 추적하기 쉽습니다.
#include <stdio.h>
// 1. 함수 포인터 타입 정의 (입력 X, 리턴 X)
typedef void (*TaskPointer)(void);
// 전역 변수로 훅(Hook)을 저장할 변수 선언 (초기엔 아무것도 없음)
TaskPointer ButtonPressHook = NULL;
// --- [OS / 라이브러리 영역] ---
// 버튼이 눌렸을 때 시스템 내부에서 호출되는 함수
void OS_HandleButtonPress() {
printf("[OS] 하드웨어 버튼 감지됨!\n");
// 만약 사용자가 등록한 훅이 있다면 실행 (이게 바로 후킹!)
if (ButtonPressHook != NULL) {
printf("[OS] 사용자 훅 실행...\n");
ButtonPressHook(); // 사용자의 함수를 대신 실행해줌 (콜백)
}
}
// 사용자가 훅을 등록할 수 있게 해주는 함수
void OS_SetButtonHook(TaskPointer userFunction) {
ButtonPressHook = userFunction;
}
// --- [사용자 영역] ---
// 사용자가 만든 콜백 함수
void MyLedOnFunction() {
printf(">> [User] LED를 켭니다!\n");
}
int main() {
// 1. 훅 등록 전: 버튼 눌러도 OS 메시지만 나옴
printf("--- 훅 등록 전 ---\n");
OS_HandleButtonPress();
// 2. 훅 등록 (함수 포인터 전달)
OS_SetButtonHook(&MyLedOnFunction);
// 3. 훅 등록 후: 버튼 누르면 내 함수도 같이 실행됨
printf("\n--- 훅 등록 후 ---\n");
OS_HandleButtonPress();
return 0;
}
실행결과
--- 훅 등록 전 ---
[OS] 하드웨어 버튼 감지됨!
--- 훅 등록 후 ---
[OS] 하드웨어 버튼 감지됨!
[OS] 사용자 훅 실행...
>> [User] LED를 켭니다!