본문 바로가기
SW/C,C++

[C/C++] 콜백함수 (Callback Function)

by FastBench 2024. 4. 1.

콜백 함수란?

콜백 함수는 다른 코드의 인자로 전달되어, 그 코드에 의해 어느 시점에 호출되는 함수인데

특히 비동기적 작업, 이벤트 리스닝, 또는 특정 조건 하에서 실행되어야 할 코드를 처리할 때 유용하다.

C언어에서 콜백 함수는 함수 포인터를 통해 구현되는데, 함수 포인터는 함수의 주소를 저장하는 변수로, 이를 통해 함수를 다른 함수의 매개변수로 전달하거나 변수에 저장한 함수를 호출할 수 있다.

콜백 함수의 구현

콜백 함수를 사용하는 기본적인 방법은 다음과 같다

  1. 함수 포인터 선언: 콜백으로 사용될 함수의 타입에 맞는 함수 포인터를 먼저 선언한다. 이는 콜백 함수의 인터페이스(시그니처)를 정의한다.
  2. 콜백 함수 정의: 이후에 해당 함수 포인터 타입에 맞게 콜백 함수를 정의한다. 콜백 함수는 선언된 함수 포인터 타입의 시그니처를 따라야 한다.
  3. 콜백 함수 전달: 콜백 함수를 인자로 받는 함수에, 함수 포인터를 전달한다.
  4. 콜백 함수 호출: 조건이 충족될 때, 전달받은 함수 포인터를 통해 콜백 함수를 호출한다.

 

콜백함수 예시 qsort 

qsort 표준 라이브러리 함수를 사용하는 경우, 사용자 정의 비교 함수를 콜백으로 전달하여 배열을 정렬할 수 있다.

#include <stdio.h>
#include <stdlib.h>

// 비교 함수 정의
int compare(const void *a, const void *b) {
    const int *ia = (const int *)a;
    const int *ib = (const int *)b;
    return *ia - *ib; // 오름차순 정렬
}

int main() {
    int arr[] = {5, 2, 9, 1, 5, 6};
    int arr_size = sizeof(arr) / sizeof(arr[0]);

    // qsort 함수를 사용하여 배열 정렬
    // qsort는 배열, 요소 개수, 요소 크기, 비교 함수(콜백)를 매개변수로 받습니다.
    qsort(arr, arr_size, sizeof(int), compare);

    // 정렬된 배열 출력
    printf("Sorted array: ");
    for(int i = 0; i < arr_size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

qsort 함수에 compare 함수의 포인터를 전달하는데, qsort는 배열의 각 요소를 비교할 때 이 콜백 함수를 사용하여 정렬 순서를 결정한다.

 

compare 함수는 직접적으로 함수 포인터로 선언되지 않는다.

대신, compare 함수의 이름을 사용하여 qsort 함수에 인자로 전달한다.

 

C언어에서 함수 이름은 함수의 주소를 가리키므로, 함수 이름 자체가 함수 포인터로 사용된다.

C언어에서 함수 이름은 함수를 가리키는 포인터로 해석될 수 있으며, 함수 호출에 사용될 때는 함수의 주소를 의미한다.

따라서 compare 함수를 정의할 때 별도로 포인터를 선언하지 않아도, qsort 함수에 인자로 전달될 때는 자동으로 함수 포인터로 취급된다.

 

 

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));

여기서 `int (*compar)(const void *, const void *)` 부분이 비교 함수를 가리키는 함수 포인터를 의미다.

compare 함수의 인자는 원형이 요구하는 인자와 일치하므로, compare 함수를 qsort 함수에 전달할 때 함수 포인터로 자동으로 변환된다.

 

정리하자면,

함수 포인터와 콜백 함수의 활용은 라이브러리 설계와 API 개발에서 강력한 추상화 수단을 제공한다.

라이브러리 제작자는 모든 가능한 사용 시나리오를 사전에 예측하고 구현할 필요 없이, 유연한 인터페이스를 설계함으로써 사용자가 필요에 따라 특정 기능을 구현할 수 있게 한다.

이 접근법은 라이브러리의 확장성과 재사용성을 대폭 향상시킨다.

 

함수 포인터를 사용하는 인터페이스는 사용자에게 콜백 함수를 제공할 수 있는 "프레임워크"를 마련해준다.

사용자는 이 프레임워크 내에서 자신의 로직을 구현한 콜백 함수를 작성하고, 이를 라이브러리에 전달함으로써, 라이브러리의 기본 동작을 사용자 정의 동작으로 확장할 수 있다.

이러한 방식은 특히 이벤트 처리, 비동기 작업 완료 시 콜백, 정렬이나 탐색과 같은 알고리즘의 사용자 정의 비교 로직 등 다양한 분야에서 활용된다.

 

이번 게시글의 내용을 요약하면 아래와 같다.

  1. 콜백 함수의 기본: 콜백 함수는 다른 함수의 인자로 전달되어, 실행 중인 해당 함수에 의해 호출되는 함수다. 이는 비동기 처리, 이벤트 리스닝, 사용자 정의 동작의 실행 등 다양한 상황에서 유용하게 사용된다.
  2. 함수 포인터의 역할: 함수 포인터는 콜백 함수를 다룰 때 핵심적인 역할을 한다. 함수 포인터는 특정 함수의 주소를 저장하는 변수로, 이를 통해 콜백 함수를 다른 함수에 전달하고, 저장된 주소를 사용하여 콜백 함수를 호출할 수 있다.
  3. 인터페이스의 설계: 라이브러리 제작자는 함수 포인터를 사용하여 유연하고 확장 가능한 API를 설계할 수 있다. 이를 통해, 사용자는 라이브러리 제공자가 모든 가능한 사용 사례를 사전에 구현할 필요 없이, 자신의 필요에 맞게 특정 기능을 커스터마이즈할 수 있다.

다음 게시글에서는, C++ 에서의 콜백함수에 대해 다룬다.

댓글