[C, C++] 함수포인터와 콜백함수

이 글은 포인터의 개념을 알고 있다는 가정 하에 작성되었습니다.

서론

함수포인터는 입사 후 외부업체의 API를 사용하면서 처음 접했다. 함수포인터를 학습하다 보니 콜백함수가 등장했고, 콜백함수를 학습하다 보니 API에 함수포인터가 왜 사용되는지 알게 되었다. 알고 보니 API에 흔하게 사용되었다.

함수포인터

포인터는 변수의 주소값을 가진다. 그리고 함수포인터는 함수의 주소값을 가진다. 주체만 다를 뿐이지 주소를 저장한다는 포인터의 본래 목적은 같다. 이 함수포인터는 다른 함수의 파라미터로 사용될 수 있는데, 이것이 응용되어 콜백함수라는 개념에 사용된다. 따라서 콜백함수를 알려면 함수포인터를 먼저 알아야 한다.

함수포인터 구현

변수포인터가 참조할 변수와 자료형이 일치해야 하듯, 함수포인터도 참조할 함수와 자료형이 일치해야 한다.

주소를 참조할 기본함수 정의

int sum(int a, int b){
    return a+b;
}

함수포인터 선언

기본함수인 sum과 자료형을 일치시킨다. 리턴과 파라미터 모두 일치시켜야 한다.

int(*fp)(int, int); // 반환 자료형(*이름)(인자 자료형, 인자 자료형)

함수포인터 사용

sum 함수의 주소값을 함수포인터 fp가 참조한다.

fp = sum;

전체 코드

#include <stdio.h>

int sum(int a, int b){
    return a+b;
}

int main(){
    int(*fp)(int, int);
    fp = sum;
    printf("%d", fp(1,2)); // print : 3
}

콜백함수

일반적으로 함수들은 내가 원하는 시점에 호출한다. 그러나 때로는 인터럽트가 들어와서 내가 원하지 않는 타이밍에 함수가 호출될 수도 있다. 이때 호출되어지는 함수를 콜백함수라고 한다. 예를 들어, 폰으로 게임을 하고 있다. 이 게임은 내가 호출한 것이다. 그런데 갑자기 전화가 오면서 게임 화면을 가려버렸다. 이 전화는 내가 의도한 것이 아니라 신호에 의해 OS가 스스로 실행한 것이다. 이때의 전화가 콜백함수다.

콜백함수 구현

위의 예와 같이 콜백함수를 가장 흔하게 볼 수 있는 곳은 Application과 OS간 호출이다. Application에 콜백함수를 구현하고 OS에 콜백함수를 호출하는 함수를 구현한다. 그리고 OS에서 특정 이벤트가 발생 시 콜백함수가 호출되어진다. 콜백함수는 함수포인터를 이용해서 구현된다.

void UserFunc(){
    printf("A");
}

void SystemFunc(void (*fp)()){
    if(event) fp();
}

int main(){
    SystemFunc(UserFunc);
}

UserFunc는 Application에 해당하고 SystemFunc는 OS에 해당한다. SystemFunc가 UserFunc를 호출하기 위해서 함수포인터가 사용되었다. 그런데 굳이 번거롭게 함수포인터를 사용해가며 UserFunc를 밖으로 빼야 하는가? UserFunc의 내용을 SystemFunc에 넣으면 되지 않는가?

서론에서 했던 이야기

API에는 콜백함수가 흔하게 사용된다. 이는 소프트웨어 유지보수를 위한 것이다. 예를 들어 한 SI 소프트웨어 회사에 A를 출력하는 제품을 의뢰하여 아래와 같은 코드를 받았다. SystemFunc()의 내부는 알 수 없지만 이 함수를 사용하면 A를 출력해준다고 한다.

int main(){
    SystemFunc(); // 이 함수를 사용하면 A가 출력됩니다.
}

그런데 제품의 요구사항이 바뀌어서 A가 아닌 B를 출력해야 한다. 다시 SI회사에 제품을 의뢰한다. 마찬가지로 SystemFunc()의 내부는 모르겠지만 B를 출력해준다고 한다.

int main(){
    SystemFunc(); // 이 함수를 사용하면 B가 출력됩니다.
}

그런데 제품의 요구사항이 또 바뀌어서 이번엔 C를 출력해야 한다. 가만보니 계속해서 요구사항이 바뀔 것 같다. 다시 SI회사에 제품을 의뢰하지만 이번엔 출력내용을 우리가 수정할 수 있도록 해달라고 한다. SI회사는 출력 내용을 수정할 수 있는 UserFunc() 함수를 보내왔고 이 함수가 콜백함수다. SystemFunc()의 내부는 굳이 공개할 필요도 없고 공개하고 싶지도 않기 때문에 UserFunc()함수를 만들어 준 것이다.

void UserFunc(){
    // 출력하고 싶은 내용을 여기에 작성해 주세요
}

void SystemFunc(void (*fp)()){ // API 내부에 숨겨진 함수
    if(event) fp();
}

int main(){
    SystemFunc(UserFunc);
}

Application-OS, Application-API 이외에도 Client-Server 등 콜백함수는 다양한곳에서 응용된다. 그리고 이 콜백함수의 기반에는 함수포인터가 있다.

참고

https://blog.naver.com/tipsware/221286052738

https://m.blog.naver.com/PostView.nhn?blogId=wwwkasa&logNo=80188333982&proxyReferer=https:%2F%2Fwww.google.com%2F

Leave a Comment