본문 바로가기
c언어

c언어 함수 포인터에 대하여 알아보기

by 개발자 L 2023. 2. 8.
반응형

c언어 함수 포인터에 대하여 알아보기

네 안녕하세요, 이번 포스팅에서는 함수 포인터에 대하여 알아보도록 하겠습니다.

제가 지금까지 썼던 포스팅에 있던 포인터들은 모두 변수들을 가리키는 포인터였습니다.

하지만 우리가 지금까지 배웠던 것들 중에 보면 함수도 직접 만들어서 쓰고 그랬었죠?
그러면 그것들에도 대응이 되어야 하는 것들이 있어야 합니다.

그래서 그러한 함수 포인터에 대하여, 그리고 어떻게 사용을 하는지 알아보도록 하겠습니다.

그럼 지금부터 시작하도록 하겠습니다.

 

2. 함수 포인터

함수 포인터란 말 그대로 함수를 가리키는 포인터 입니다.

함수 역시 실행이 시작이 되는 주소를 가지고 있어서 그 주소를 포인터에 넣어 지정을 할 수 있습니다.

그래서 그러한 주소를 이용하여 함수를 간접 참조할 수 있게 해주는 포인터가 바로 함수 포인터입니다.

그리고 이러한 함수 포인터는 이런 식으로 선언합니다.

int (*pf)(int, int);

보시면 일반적인 포인터들과는 차이가 있음을 볼 수 있습니다.

그 이유는 함수는 매개 변수들과 반환형을 가지고 있기 때문에

일반적인 포인터들처럼 변수명만 쓰고 앞에 에스터리스크만 써준다고 하여 선언이 되지 않습니다.

반환이 되는 자료형, 그리고 그 안에 있는 매개 변수들이 정확하게 해당 함수와 일치해야만 그 기능을 합니다.

그리고 함수 포인터가 함수를 가리키게 하려면 이런 식으로 쓰면 됩니다.

int add(int, int); // 함수의 원형
int (*pf)(int, int); // 함수 포인터 선언(매개 변수, 반환형이 같아야 함.)
.
.
.
pf = add; // 함수의 이름을 함수 포인터 변수 pf에 대입

쓰는 원리는 일반적인 포인터들과 비슷합니다.

이렇게 포인터로 함수를 지정을 하고서 함수를 호출하려면 이런 식으로 하면 됩니다.

sum = (*pf)(10, 20);

이렇게 쓰게 되면 이미 포인터 변수 pf가 함수의 시작 주소를 가지고 있어 간접 참조가 가능하기 때문에

add() 함수에 매개 변수가 입력이 되고, 그 값을 이용해서 함수가 실행이 되게 되며, 반환값을 받게 됩니다.

그리고 위의 호출 커멘드를 보면 괄호를 두 번씩이나 써야 해서 조금 번거롭죠?

이 방법이 정석적인 방법이지만,

컴파일러는 pf가 함수 포인터라는 것을 알고 있기 때문에 함수 포인터 선언만 해놓으면 호출을 할 때에는

pf만 써서 나타내도 함수 포인터를 쓴 것과 같은 기능을 합니다.

이런 식으로 말이죠.

sum = pf(10, 20); // sum = (*pf)(10, 20);과 같은 기능을 함.

그럼 이를 가지고서 실제 코딩을 하여 어떻게 쓰이는지 보여드리도록 하겠습니다.

#include <stdio.h>

int add(int, int);
int sub(int, int);

int main()
{
    int result;
    int (*pf)(int, int);

    pf = add;
    result = (*pf)(10, 20);

    printf("10 + 20은 %d 입니다.\n", result);

    pf = sub;
    result = pf(10, 20);

    printf("10 - 20은 %d 입니다.\n", result);

    return 0;
}

int add(int x, int y)
{
    return x + y;
}

int sub(int x, int y)
{
    return x - y;
}

이렇게 작성을 했고,

함수 포인터 변수 pf를 이용한 함수 호출을 정석적인 방법으로 호출했을 때와,

그냥 포인터 변수명만 사용해 호출했을 때 같은 결과가 나오는지 보여드리기 위해서 둘 다 써봤습니다.

그럼 결과를 보도록 하겠습니다.

10 + 20은 30 입니다.
10 - 20은 -10 입니다.

결과도 잘 나온 것을 확인할 수 있습니다.

반응형

 

2. 함수 포인터의 배열

그러면 이를 이용해서 함수 포인터의 배열도 만들 수 있을까요?
결론을 먼저 말씀드리자면, 만들 수 있습니다.

하지만 표기법이 좀 복잡해집니다.

선언 방법은 이렇습니다.

int (*pf[5])(int, int); // pf 뒤에 배열 연산자인 대괄호를 넣을 것.

복잡하지만 결국에는 배열 연산자가 추가가 된 것이라 크게 어려울 건 없습니다.

이 포인터의 경우는 메뉴 선택 같은 로직을 다룰 때 정말 많이 쓰입니다.

그 이유는 배열이 있는 함수 자체를 포인터에 담아서 간접 참조가 가능해지니 말이죠.

그럼 이를 이용해서 간단한 코드를 한 번 만들어보도록 하겠습니다.

#include <stdio.h>

void calc();

int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
int mod(int x, int y);

void calc()
{
    printf("====================\n");

    printf("0. 덧셈\n");
    printf("1. 뺄셈\n");
    printf("2. 곱셈\n");
    printf("3. 나눗셈(몫)\n");
    printf("4. 나눗셈(나머지)\n");
    printf("5. 프로그램 종료\n");

    printf("====================\n");
}

int main()
{
    int x, y, choice, result;
    int(*pf[5])(int, int) = {add, sub, mul, div, mod}; // 함수 포인터 배열 선언

    while(1) // 조건이 참일 동안(무한루프)
    {
        calc();

        printf("메뉴를 선택하세요 : ");
        scanf("%d", &choice);

        if(choice < 0 || choice >= 5)
        {
            printf("프로그램을 종료합니다.\n");
            
            break;
        }

        printf("두 개의 정수를 입력하세요 : ");
        scanf("%d %d", &x, &y);

        result = pf[choice](x, y); // 함수 포인터를 이용한 함수 호출

        printf("연산 결과는 %d 입니다.\n", result);
    }

    return 0;
}

int add(int x, int y)
{
    return x + y;
}

int sub(int x, int y)
{
    return x - y;
}

int mul(int x, int y)
{
    return x * y;
}

int div(int x, int y)
{
    return x / y;
}

int mod(int x, int y)
{
    return x % y;
}

이렇게 한 번 써봤습니다.

그럼 결과가 어떻게 나오는지 한 번 보도록 하겠습니다.

먼저 덧셈을 입력했을 때입니다.

====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 나눗셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : 0
두 개의 정수를 입력하세요 : 1 2
연산 결과는 3 입니다.

뺄셈을 입력했을 때입니다.

====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 나눗셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : 1
두 개의 정수를 입력하세요 : 3 4
연산 결과는 -1 입니다.

곱셈을 입력했을 때입니다.

====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 나눗셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : 2
두 개의 정수를 입력하세요 : 4 5
연산 결과는 20 입니다.

나눗셈(몫)을 입력했을 때입니다.

====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 나눗셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : 3
두 개의 정수를 입력하세요 : 6 2
연산 결과는 3 입니다.

나눗셈(나머지)을 입력했을 때입니다.

====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 나눗셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : 4
두 개의 정수를 입력하세요 : 9 6
연산 결과는 3 입니다.

이 결과들이 계속 무한루프로 돌고,

프로그램이 종료되는 조건인 0 보다 작은 수, 그리고 5 이상의 숫자를 입력하면 종료가 됩니다.

이런 식으로 말이죠.

// 음수를 입력했을 경우
====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 덧셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : -1
프로그램을 종료합니다.

// 5 이상의 숫자를 입력했을 경우
====================
0. 덧셈
1. 뺄셈
2. 곱셈
3. 나눗셈(몫)
4. 나눗셈(나머지)
5. 프로그램 종료
====================
메뉴를 선택하세요 : 5
프로그램을 종료합니다.

프로그램이 정상적으로 작동하는 것을 볼 수 있습니다.

 

3. 함수 인수로써의 함수 포인터

함수 포인터는 함수 인수로써의 작용을 할 수도 있습니다.

그 이유는 함수 포인터 역시 변수의 범위 내에 있기 때문입니다.

이 기능을 이용하게 되면 호출이 된 함수가 특정한 함수를 호출하도록

호출을 하는 쪽에서 결정이 되도록 도와줄 수 있습니다.

이를 이용해서 수식을 계산하는 프로그램을 한 번 작성을 해보도록 하겠습니다.

#include <stdio.h>
#include <math.h>

double f1(double k);
double f2(double k);
double formula(double (*pf)(double), int n);

int main()
{
    printf("수식의 계산 결과는 %f 입니다.\n", formula(f1, 10)); // f1을 인수로 하여 함수 호출
    printf("수식의 계산 결과는 %f 입니다.\n", formula(f2, 10)); // f2를 인수로 하여 함수 호출

    return 0;
}

double formula(double (*pf)(double), int n)
{
    double sum = 0.0;

    for(int i = 1; i < n; i++)
    {
        sum += pf(i) * pf(i) + pf(i) + 1; // 함수 포인터를 이용하여 이차식 계산
    }

    return sum;
}

double f1(double k)
{
    return 1.0 / k;
}

double f2(double k)
{
    return cos(k);
}

어떤 이차식을 계산하여 호출하는 함수를 만들어봤습니다.

그럼 결과를 보도록 하겠습니다.

수식의 계산 결과는 13.368736 입니다.
수식의 계산 결과는 12.716152 입니다.

결과도 문제없이 잘 나온 것을 볼 수 있습니다.

 

여기까지 함수 포인터에 대하여 알아보았는데요,

다음 포스팅에서는 다차원 배열과 포인터에 대하여 알아보도록 하겠습니다.

긴 글 읽어주신 독자분들께 진심으로 감사드립니다~

반응형

댓글