본문 바로가기

C/기초와 문법

[C] 함수 포인터와 역호출(Callback) 구조

반응형

🔊 해당 포스팅은 인프런의 널널한 개발자님의 독하게 시작하는 C 프로그래밍 강의를 듣고 개인적인 복습 목적 하에 작성된 글입니다. 해당 포스팅에 사용된 모든 자료는 필자가 직접 재구성하였음을 알립니다.
 

C언어를 배워보자!


이번 포스팅에서는 함수의 고급이론 중 하나로 함수 포인터에 대한 개념에 대해 배워보고 이 함수 포인터를 자주 사용하는 유즈 케이스 중 하나로 역호출(Callback) 구조에 대해서도 배워보자.

1. 함수 포인터

이제 포인터라는 것에 대해서는 매우 익숙할 것이다. 그동안 정수, 실수, 문자, 문자열, 배열에 대한 포인터도 배웠지만 함수에 대한 포인터에 대해서도 배워볼 차례다. 함수도 문자열, 배열과 마찬가지로 정의한 함수 이름에 메모리 주소가 담긴다. 그래서 함수 포인터를 정의하면 함수의 이름을 메모리 주소로 저장할 수 있는 포인터 변수라고 한다. 함수 포인터를 정의할 때는 아래와 같은 규칙을 따른다. 만약 정의한 함수가 Add 라는 두 정수를 인자로 받아 두 정수의 합을 반환한다고 해보자.

 

여기서 pAdd 가 함수 포인터 변수가 된다

 

포인터 변수를 정의하는 규칙 중에서 [호출규칙] 이라는 것이 있는데, 사실 이거는 생략이 가능하다. 그리고 64비트 운영체제에서는 사실상 호출 규칙이 __fastcall 로 하나이다. 이 __fastcall 말고도 32비트 운영체제에서는 __cdecl, __stdcall 이 있는데, 이에 대한 설명은 강의 원본을 참조하도록 하자.

 

함수 포인터를 사용하는 예시코드는 아래와 같다.

 

#include <stdio.h>

int GetMax(int a, int b, int c) {
    int nMax = a;
    if (b > nMax) nMax = b;
    if (c > nMax) nMax = c;
    return nMax;
}

int main(void) {
    int(*pGetMax)(int, int, int) = GetMax;
    printf("Result: %d\n", pGetMax(10, 11, 12));
    return 0;
}

2. 함수 포인터는 언제 쓸까? 역호출(Callback) 구조 때!

위에서 함수 포인터를 정의하는 방법에 대해 배웠다. 그런데 이 함수 포인터를 배우는 이유가 무엇인가? 이거를 언제 써먹는가?에 대해서 궁금해할 수 있다. 결론적으로 함수 포인터는 주로 역호출 구조 때 사용하게 된다. 먼저 역호출 구조가 무엇인지 알아보자.

 

역호출 구조란 흔히 개발 용어로는 Callback(콜백) 구조라고도 한다. 콜백 구조는 프레임워크랑 연관이 되어 나오는 개념인데, 여기서 프레임워크라면 자바에서는 Spring, 파이썬에서는 FastAPI, 머신러닝에서는 Scikit-learn, Pytorch 등 사용자가 편하게 쓰도록 고수준으로 래핑해놓은 모든 도구들을 의미한다. 즉, 남이 개발해놓은 소스코드이다.

 

프레임워크를 활용해서 보통 개발할 때는, 개발자가 직접 작성하는 소스코드에서 외부(프레임워크) 소스코드 중 일부를 가져와 사용하게 된다. 예를 들어, 아래 그림처럼 프레임워크의 소스코드를 가져다 사용하는 코드를 개발하고 있다고 해보자.

 

콜백 구조 설명

 

위 그림은 Python 문법을 예시로 들었다. 문법을 몰라도 괜찮다. 핵심은 내가 작성하는 소스코드에서 프레임워크에서 제공해주는 Add 함수를 사용하는 것이다. 그리고 내 소스코드에서 정의한 myFunc 이라는 함수를 Add 함수의 인자로 전달을 한다. 그리고 난 뒤, myFunc 이라는 함수가 실질적으로 호출되는 것은 프레임워크 소스코드 내부에서 호출되는 것이다. 

 

즉, 내가 작성한 함수 myFunc 이라는 함수는 단순히 인자로만 전달하고, 해당 함수가 실질적으로 호출되는 것은 프레임워크 소스코드 내부에서 실행되는 것이다. 바로 이런 구조일 때, 함수 포인터 변수가 사용되는 것이다.

 

하지만 이런 콜백 구조는 문제가 내가 전달한 myFunc 이라는 함수가 프레임워크 내부에서 실질적으로 호출될 때 구체적으로 어떤 로직으로 호출되는지, 또 myFunc 함수가 몇 번 호출되는 것인지를 알기가 힘들다. 즉, 프레임워크에서 제공해주는 함수인 Add 라는 함수의 소스코드를 직접 까봐야 동작을 알게 되는 것이다. 다만 Add 라는 함수는 내가 직접 작성한 함수가 아니라 해당 프레임워크를 개발한 어떤 누군가가 개발한 것이기 때문에 남 코드를 읽어서 이해해야 하는 부분을 감수해야 한다.


이렇게 해서 독하게 시작하는 C 프로그래밍 강의를 모두 완강했다. 강의를 듣는 초반에는 블로깅을 따로 하지 않고 들었는데, 중간에 내 머릿속 지식이 다 휘발되는 것을 느꼈다.. 그래서 중간에 홀딩하고 그동안 배운 내용 강의를 다시 들으면서 학습한 내용을 기록했다. 역시 학습에는 기록을 하면서 내가 새로 배운 지식이 구조화되고 정리되는 듯한 느낌이 든다. 이제 다음으로는 C언어로 배우는 선형 자료구조 강의를 듣고 기록해보려고 한다.

반응형