CS 지식 정리

함수 포인터란?

termuni 2025. 3. 14. 19:58

라즈베리파이를 c언어로 다루기 위해, 이것저것 공부를 하고 있다.

 

그러던 와중, 함수포인터가 매우 자주 보여 이를 정리하고자 한다!

 

원리 위주로 파해칠 예정이며, 활용은 다른 게시글 참고바란다!

 

그리고 제일 중요한 것은, 포인터를 잘 모르겠으면 무조건 이해하고 와야한다.

꼭!!!! 무조건이다!!


https://dojang.io/mod/page/view.php?id=591

 

C 언어 코딩 도장: 68.0 함수 포인터 사용하기

C 언어에서 함수는 이름이 정해져 있죠. 그래서 함수를 호출하려면 함수 이름으로 직접 호출했습니다. void hello() { printf("Hello, world!\n"); } int main() { hello(); // 함수 이름으로 함수를 직접 호출 return

dojang.io

이 글을 참조하여 작성했으니, 만약 부족한 설명이 있다면 위 게시글로 이동해서 보길 권장합니다.

void hello()
{
    printf("Hello, world!\n");
}

int main()
{
    printf("%p\n", hello); // 00D1137F(32비트): 함수 이름도 포인터이므로 메모리 주소가 출력됨

    return 0;
}

 

이런식으로, 포인터를 프린트 하면 주석과 같은 메모리 값이 나올 것으로 예측된다.


반환값도, 매개변수도 없는 함수 포인터 (기본형)

#include <stdio.h>

void hello()     // 반환값과 매개변수가 없음
{
    printf("Hello, world!\n");
}

void bonjour()    // 반환값과 매개변수가 없음
{
    printf("bonjour le monde!\n");
}

int main()
{
    void (*fp)();   // 반환값과 매개변수가 없는 함수 포인터 fp 선언

    fp = hello;     // hello 함수의 메모리 주소를 함수 포인터 fp에 저장
    fp();           // Hello, world!: 함수 포인터로 hello 함수 호출

    fp = bonjour;   // bonjour 함수의 메모리 주소를 함수 포인터 fp에 저장
    fp();           // bonjour le monde!: 함수 포인터로 bonjour 함수 호출

    return 0;
}

 

이런 식으로, 함수를 정의하고 함수 포인터를 정의하여, 매칭시켜 실행시킬 수 있다.

어찌보면, 변수를 매칭시키는 느낌과 비슷하다!

만약 void (*fp)()라는 것에 어색함을 느낀다면... 포인터를 공부하고 오는 것을 권장한다.

 

이는 제일 단순한 형태로, 함수 포인터를 정의함으로서
이 함수(fp)가 어느 함수의 메모리 주소를 포인팅, 즉 지시하고 있을지 모르는 상태이다.

 

그러다가 fp = hello; 로, 함수 포인터가 hello 라는 함수를 지시하게 변경해주는 것.


반환값이 있는 함수 포인터

만약 지시하고 싶은 함수의 모양이 다음과 같은 형태라면 어떻게 될까?

int Get_Age()
{
	age = 21;
	return age;
}

이런 형태로 되어있다면, 함수 포인터는?

int (*fp)();

이렇게 정의해주면, 그대로 받아 올 수 있다.

fp = Get_Age;

int age = fp();

즉, 형식을 맞게 맞춰주면 되는 것!

입력값이 있는 함수 포인터

void Print_Age(int age)
{
	printf("Age is %d\n", age);
}

이런 식으로 입력 값이 있는 함수의 경우 다음과 같은 함수 포인터를 정의해주면 된다.

void (*fn)(int);

 

이후 매칭 시켜주고, 다음과 같이 실행이 가능하다.

fp = Print_Age;

fp(21);

 

입력값과 반환값이 있는 함수 포인터

이젠 예상이 되는 것 같다.

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

int (*fp)(int, int);

 

이렇게 한 뒤,

fp = add;                      // add 함수의 메모리 주소를 함수 포인터 fp에 저장
printf("%d\n", fp(10, 20));    // 30: 함수 포인터로 add 함수를 호출하여 합을 구함

이런 식으로 구현 가능!


함수 포인터를 배열로 사용

void (*fp[4])();

이렇게, 일반적인 int a[4] 처럼 이름 뒤에 정의하면 된다.

 

만약 뒤에 여러 함수를 붙이고싶다면, 일반적인 배열 정의처럼 형식에 맞는 함수로 맞추어 중괄호로 묶어서 정의해주면 된다.

즉, 다음과 같다.

void A(){...}
void B(){...}
void C(){...}
void D(){...}

void (*fp[4])() = {A, B, C, B}

함수의 형식이 복잡해지더라도, 단순하게 생각해야한다!!


함수 포인터를 함수의 매개변수로 사용

이게 무슨 소리인가.. 싶을텐데! 

우선 이걸 보자.

void Print_Age(int age)
{
	printf("Age is %d\n", age);
}

 

int 라는 매개변수를 넘겨서 출력하는 것이다.

 

그러면, 만약 함수 포인터를 넘기게 된다면?

 

int Get_Age(int age) {    return age;   }

void Print_Age(int (*fp)(int), int age) {
	printf("Age is %d\n", fp(age));
}

...
//대충 여기서부터 메인
int age = 21;
int (*fp)(int) = Get_Age;	// 함수 포인터 선언과 함께 초기화
Print_Age(fp, age);

 

이런 식으로 정리가 가능할 것이다!


함수 포인터를 함수의 반환값으로 사용

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

이러한 함수를 반환하는 함수를 제작하는 것과 비슷하다!

우선 간단한 버전을 보겠다.

void Print_Age(void) 
{    printf("Age is 21\n");   }

void (*fp(void))(void) 
{    return Print_Age;   }

...

// 함수 포인터를 사용하여 Print_Age 호출
void (*func_ptr)(void) = fp(); 
func_ptr();

 

이런 형식으로, *fp 자리에 매개변수 괄호를 더 넣는 식으로 정의된다.


다음은 좀 더 심화 버전이다.

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

int (*getAdd())(int, int) {    return add;   }

...


int (*fp)(int, int) = getAdd();

printf("%d\n", fp(10, 20));    // 30: getAdd를 호출한 뒤 반환값으로 add 함수 호출

 

이런식으로, 함수를 받는 함수를 정의할 수 있다.

 

더 더 꼬아버리면??

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

int (*getAdd(int x, int y))(int, int){
    printf("%d %d\n", x, y);
    return add;
}

...

int (*fp)(int, int) = getAdd(8, 9);
printf("%d\n", fp(10, 20));

 

복잡해보이지만, 사실 참고해온 게시글의 코드가 더더 복잡했다. 뇌를 꼬아버리는 수준.

일부러 한 번 풀어 적어두었다.


Typedef로 함수 포인터 정의하고 활용하기

이제 슬슬 뇌가 꼬인다!

 

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

typedef int (*FP)(int, int);    // FP를 함수 포인터 별칭으로 정의

FP getAdd()
{
    return add;
}

 

이렇게 적으면 어떻게 될까?

이에 대한 설명은 다음과 같다.

typedef int (*FP)(int, int); 에서

typedef : 다음으로 정의한다

int (int, int) :  이러한 함수 포인터의 형식으로

FP : FP라는 이름으로!

 

이해가 잘 안 된다면, struct를 typedef하는 것과 비슷하게 생각해보면 된다.

typedef struct AAA{
	....
};

AAA test1;

typedef : 다음으로 정의한다

struct {...} : 이와 같은 구조체의 형식으로

AAA : AAA라는 이름으로!

 

그래서, 만약 위 함수를 풀어서 쓴다면

typedef int (*FP)(int, int);    // FP를 함수 포인터 별칭으로 정의

FP getAdd()
{
    return add;
}

/*---------------------------------------------------------------*/

int (*getAdd())(int, int) {    return add;   }

 

이렇게, 위와 아래가 같은 것!


사실 참고한 사이트에서도 권장하는 것은, 사용하기 전에 검색해보는 것이다.

점점 복잡해지는 형태이다보니, 헷갈릴 수 있기 때문..

 

그러니, 한 번 코드를 짜고, gpt 또는 옆에있는 다른 C언어 개발자에게 물어보는 것을 권장한다.