69 함수 포인터 활용하기

함수 포인터를 만드는 방법을 알아보았으니 이번에는 함수 포인터를 다양하게 활용해보겠습니다. 함수 포인터도 일반적인 포인터이므로 포인터로 할 수 있는 작업을 그대로 수행할 수 있습니다.

함수 포인터를 배열로 만들기, 구조체 멤버로 사용하기, 함수의 매개변수와 반환값으로 사용하기 등이 가능하겠죠?

함수 포인터를 응용하면 함수를 고정된 형태가 아닌 데이터 형태로 취급할 수 있습니다. 즉, 함수를 보관하거나 주고받기가 쉬워집니다.

참고로 함수 포인터 활용하기는 C 언어 문법에서 가장 까다로운 부분입니다. 따라서 내용이 좀 어려울 수 있습니다.

69.1 함수 포인터 배열 사용하기

먼저 사용자에게 함수 번호와 계산할 값을 입력받은 뒤 함수 포인터로 계산을 해보겠습니다.

계산 함수는 int형 반환값, int형 매개변수 두 개를 가지고 있는 함수이며 0부터 3까지 번호를 매겼습니다.

  • int add(int a, int b):  0
  • int sub(int a, int b):  1
  • int mul(int a, int b):  2
  • int div(int a, int b):  3

다음 내용을 소스 코드 편집 창에 입력하세요.

function_pointer.c

#define _CRT_SECURE_NO_WARNINGS    // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>

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

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int div(int a, int b)
{
    return a / b;
}

int main()
{
    int funcNumber;    // 함수 번호
    int num1, num2;
    int (*fp)(int, int) = NULL;    // int형 반환값, int형 매개변수 두 개가 있는 함수 포인터 선언

    printf("함수 번호와 계산할 값을 입력하세요: ");
    scanf("%d %d %d", &funcNumber, &num1, &num2);    // 함수 번호와 계산할 값을 입력받음

    switch (funcNumber)   // 함수 번호에 따라 함수 포인터 설정
    {
    case 0:               // 0이면
        fp = add;         // 덧셈 함수
        break;
    case 1:               // 1이면
        fp = sub;         // 뺄셈 함수
        break;
    case 2:               // 2이면
        fp = mul;         // 곱셈 함수
        break;
    case 3:               // 3이면
        fp = div;         // 나눗셈 함수
        break;
    }
    
    printf("%d\n", fp(num1, num2));    // 함수 포인터를 사용하여 계산 결과 출력

    return 0;
}

소스를 컴파일하여 실행한 뒤 0 10 20을 입력하고 엔터 키를 누르세요.

실행 결과

함수 번호와 계산할 값을 입력하세요: 0 10 20 (입력)
30

함수 번호가 funcNumber에 저장됩니다. switch 분기문에서 funcNumber에 따라 함수 포인터 fp에 함수의 주소를 넣어줍니다. 여기서는 0을 입력했으므로 fpadd 함수가 들어갑니다. 그리고 fp를 호출하여 10과 20을 더한 값인 30이 나왔습니다.

함수를 번호로 나타내려고 하니 코드가 너무 길어집니다. 이때는 함수 포인터를 배열로 만들면 간단해집니다.

함수 포인터 배열은 함수 포인터를 선언할 때 함수 포인터 이름 뒤에 [ ] 대괄호 안에 배열의 크기를 지정하면 됩니다.

  • 반환값자료형 (*함수포인터이름[크기])(매개변수자료형1, 매개변수자료형2);

그럼 int형 반환값, int형 매개변수가 두 개인 함수를 담을 수 있는 배열은 어떻게 만들까요?

int add(int a, int b)    // int형 반환값, int형 매개변수 두 개
{
    return a + b;
}

요소의 개수가 4개이며 add 함수를 담을 수 있는 함수 포인터 배열은 이렇게 만듭니다.

int (*fp[4])(int, int);    // int형 반환값, int형 매개변수 두 개가 있는 함수 포인터 배열 선언

이제 함수 포인터 배열을 사용해보겠습니다. 다음 내용을 소스 코드 편집 창에 입력하세요.

function_pointer_array.c

#define _CRT_SECURE_NO_WARNINGS    // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>

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

int sub(int a, int b)
{
    return a - b;
}

int mul(int a, int b)
{
    return a * b;
}

int div(int a, int b)
{
    return a / b;
}

int main()
{
    int funcNumber;    // 함수 번호
    int num1, num2;
    int (*fp[4])(int, int);    // int형 반환값, int형 매개변수 두 개가 있는 함수 포인터 배열 선언

    fp[0] = add;    // 첫 번째 요소에 덧셈 함수의 메모리 주소 저장
    fp[1] = sub;    // 두 번째 요소에 뺄셈 함수의 메모리 주소 저장
    fp[2] = mul;    // 세 번째 요소에 곱셈 함수의 메모리 주소 저장
    fp[3] = div;    // 네 번째 요소에 나눗셈 함수의 메모리 주소 저장

    printf("함수 번호와 계산할 값을 입력하세요: ");
    scanf("%d %d %d", &funcNumber, &num1, &num2);    // 함수 번호와 계산할 값을 입력받음

    printf("%d\n", fp[funcNumber](num1, num2));    // 함수 포인터 배열을 인덱스로 접근하여 함수 호출

    return 0;
}

소스를 컴파일하여 실행한 뒤 0 10 20을 입력하고 엔터 키를 누르세요.

실행 결과

함수 번호와 계산할 값을 입력하세요: 0 10 20 (입력)
30

먼저 다음과 같이 함수 포인터 이름 fp 뒤에 대괄호를 붙이고 크기를 4로 지정하여 요소가 4개인 함수 포인터 배열을 선언했습니다.

// int형 반환값, int형 매개변수 두 개가 있는 함수 포인터 배열 선언
//↓ 반환값 자료형
int (*fp[4])(int, int);    // ← 매개변수 자료형
//    ↑   ↖ 크기가 4인 배열
// 함수 포인터 이름

이제 fp는 배열이므로 인덱스로 접근할 수 있습니다. 여기서는 add, sub, mul, div 함수의 메모리 주소를 각 요소에 저장했습니다.

fp[0] = add;    // 첫 번째 요소에 덧셈 함수의 메모리 주소 저장
fp[1] = sub;    // 두 번째 요소에 뺄셈 함수의 메모리 주소 저장
fp[2] = mul;    // 세 번째 요소에 곱셈 함수의 메모리 주소 저장
fp[3] = div;    // 네 번째 요소에 나눗셈 함수의 메모리 주소 저장

함수 포인터 배열은 다음과 같이 인덱스를 사용하여 요소에 접근한 뒤 함수를 호출할 수 있습니다. 따라서 fp[funcNumber](10, 20)와 같이 fp 뒤에 대괄호를 붙이고 인덱스를 지정한 뒤 함수를 호출하면 됩니다. 여기서는 0 10 20을 입력하여 funcNumber에 0이 들어있으므로 fp의 첫 번째 요소에 들어있는add 함수가 호출됩니다.

printf("%d\n", fp[funcNumber](num1, num2));    // 함수 포인터 배열을 인덱스로 접근하여 함수 호출

만약 배열에 들어있는 모든 함수를 호출하고 싶다면 반복문을 사용하면 됩니다.

// 배열의 요소 개수만큼 반복(요소 개수: 배열의 전체 크기 / 요소 한 개의 크기)
for (int i = 0; i < sizeof(fp) / sizeof(fp[0]); i++)
{
    printf("%d\n", fp[i](num1, num2));    // 함수 포인터 배열을 인덱스로 접근하여 함수 호출
}

이처럼 함수 포인터 배열을 활용하면 함수 자체도 데이터화하여 함수 호출을 자동화할 수 있습니다.

참고 | 함수 포인터 배열을 선언하는 동시에 초기화하기

함수 포인터 배열도 { } (중괄호)를 사용하여 초기화 할 수 있습니다.

int (*fp[4])(int, int) = { add, sub, mul, div };    // 중괄호로 함수의 메모리 주소를 저장