본문 바로가기
프로그래밍 언어/c(일반)

포인터(4) - 함수 포인터 & 사용 예시 정리

by [Akashic Records] 개발의선지자 2024. 7. 12.

해당 글은 개인 공부 정리를 위해 작성되었습니다.

함수 포인터

프로그램에서 정의된 함수는 프로그램이 실행될 때 모두 메인 메모리에 올라갑니다.

배열명이 배열의 시작 주소값를 가리키는 포인터 상수를 의미하듯,

함수명은 함수가 정의되어 있는 메모리의 시작 위치를 가리키는 포인터 상수(constant pointer) 입니다.

함수명은 포인터이므로 printf에서 출력해보면 메모리 주소(%p)가 출력된다

 

※ 선언 방법 : 반환값자료형 (*함수포인터이름) (매개변수 자료형)

 

#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 funcNum;
    int num1 = 0, num2 = 0;
    //반환값자료형 (*함수포인터이름) (매개변수 자료형)
    int (*fp)(int , int);
    scanf("%d", &funcNum);
    switch(funcNum)
    {
        case 0:
        fp = add;
        break;

        case 1:
        fp = sub;
        break;

        case 2:
        fp = mul;
        break;

        case 3:
        fp = div;
        break;

        default:
        break;
    }
    scanf("%d %d", &num1, &num2);
    printf("result %d\n", fp(num1, num2));
    
    return 0;
}

 

 

함수 포인터 배열

※선언 방법  : 반환값자료형 (*함수포인터이름[크기])(매개변수자료형1, 매개변수자료형2);

 

#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 Calculator(int num1, int num2, int (*func)(int, int)) { return func(num1, num2); }

int main() 
{
    int funcNum;
    int num1 = 0, num2 = 0;
    int (*fp[4])(int , int);
    scanf("%d", &funcNum);
    switch(funcNum)
    {
        case 0:
        fp[0] = add;
        break;

        case 1:
        fp[1] = sub;
        break;

        case 2:
        fp[2] = mul;
        break;

        case 3:
        fp[3] = div;
        break;

        default:
        break;
    }
    scanf("%d %d", &num1, &num2);
    printf("result %d\n", Calculator(num1, num2, fp[funcNum]));
    
    return 0;
}

typedef 키워드

typedef :  새로운 타입을 만드는 키워드이다.

typedef 키워드를 이용하면 복잡한 함수 포인터형에 새로운 이름을 붙일 수 있습니다.

 

※선언 방법 :  typedef 자료형 (*함수 포인터명)(매개변수)

※사용 방법  : 선언된 함수 포인터명 별칭명

 

=ex)

typedef int (*FuncPtr)(int, int); // 선언
FuncPtr testptr;  // 사용

 

아래는 "typedef 함수 포인터 새로운 타입명"을 사용한 예제이다.

typedef Handle (*getHandleFunc)(void* instance);
 
getHandleFunc getHandle;
 
Handle getEventHandle(void* instance);
 
void main(void)
{
  void *instance;
  ..
  ..
 
  getHandle = getEventHandle;   //assign
  getHandle(instance);          //실제로는 getEventHandle 실행
}
 
Handle getEventHandle(void* instance)
{
  //실제구현... 
}

 

사용 영역

 

1. 콜백함수 -> 내부적으로 함수를 만들어서 직접 호출하지 않더라도, 함수의 매개변수로 호출 가능

 

아래 콜백 함수의 예시이다.

//A가 배포한 라이브러리 함수 
//실제 소스코드는 B는 볼수 없음
//다만 A가 콜백을 구현 할 수 있도록 만듬 
int sum(int a, int b, void (*funcptr)(int*,int*)) {

	if (funcptr != NULL) {
		funcptr(&a, &b);
	}

	return a + b;
}

//B가 만든 사용자 정의 함수 콜백용으로 만든 함수
void userdefine(int* a, int* b) {
	if (*a < 0) *a = *a * -1;
	if (*b < 0) *b = *b * -1;
}


int main() {

	int result = 0;

	result = sum(2, -2, NULL);
	printf("콜백 함수 없이 호출 한 결과 : %d \n", result);

	result = sum(2, -2, userdefine);
	printf("콜백 함수를 정의하여 호출 한 결과 : %d\n", result);

	return 0;
}

 

2. 구조체에 함수를 넣을 수 있다. ->  객체 프로그래밍 방식 구현 가능

 

3. Device driver

typedef struct {
    const char      *name;
    void (*read) (byte *buffer, int count);
    void(*write) (byte *buffer, int count);
} device_type;

device_type device =
{
    "It's me",
    device_read,
    device_write
}

device_type *device_target;
device_target = &device

(*device_target->read)(buffer, count)

 

4.특정 주소번지로 branch

void (*example) (void);
example = (void (*)())0x7777;
(*example)();

(*(void(*)())0x7777)();

 

5. 하나의 프로그램이 여러 개의 파일로 분리되어 있을 때 다른 파일에 있는 정적 함수를 호출