본문 바로가기

C언어 자습/혼자공부하는 C언어 (저자 서현우)

함수 포인터

함수 포인터란?

 

함수의 정의가 있는 부분의 메모리 주소를 의미 합니다.

재사용성과 가독성을 위해 유용하게 사용하는 함수, main 함수와는 다른 곳에 위치하고 있지만 그들도 결국 메모리상에 존재하는 것이었습니다.이 함수를 실행하기 위해서는 결국 메모리 위치를 알아야 하는 것입니다. 

#include<stdio.h>


int sum(int, int);	// 함수 선언

int main()
{
	int (*fp)(int, int); //함수 포인터 선언
	int res;	//반환값을 저장할 변수

	fp = sum;	//함수명을 함수 포인터에 저장
	res = fp(10, 20);	//함수 포인터로 함수 호출
	printf("result : %d\n", res); //반환값 출력
	
	return 0;
}

int sum(int a, int b) // 함수 정의
{
	return (a + b);
}

 

함수를 선언할 때는 매개변수의 이름까지 작성할 필요가 없습니다.

왜 그런 것일까요?

함수 선언은 코드 영역에서 이루어집니다.

코드 영역은 텍스트 영역으로 말 그대로 우리가 작성한 텍스트가 메모리 상에 존재하는 영역입니다.

main 함수에서 함수를 호출하는 코드를 만나게 되면 함수의 원형(proto type)을 참조해서 매개변수의 자료형과 개수를 확인하게 됩니다.

이를 통해 호출할 함수의 존재를 확인하고 필요한 인자들이 적절한 자료형과 개수를 가지고 있는지 확인합니다. 신원확인 과정을 거치는 것이죠. 이 과정에 매개변수의 이름은 필요가 없습니다. 반환값의 종류과 매개변수가 어떤 자료형이고, 몇개인지만 알면 되죠.

 

함수명은 주소다?

함수명은 사실 포인터였다.

위 코드에서 res 값에 함수 반환값을 대입하는 부분을 아래와 같이 바꿔 보겠습니다.

res = fp(10,20) 

이 문장을 아래와 같이

res = (*sum)(10,20)

으로 바꿔도 결과값이 동일합니다.

res = sum(10,20)

말 할것도 없이 위 문장도 동일한 결과를 보여주고 있습니다.

sum 이라는 함수 이름은 사실 포인터 였던 것입니다.

 

그래서 변수를 참조 할 때는 fp = &sum 과 같은 방식으로 주소참조 앰퍼샌드를 사용 했습니다만.

함수 참조 시에는 fp = sum도 정상 작동하는 것을 볼 수 있었습니다.

 

이를 정확히 확인하기 위해 Debug 도중 메모리 값을 확인 했는데 예상과는 다른 결과가 나왔습니다.

 

sum의 값입니다. 일단 값이 주소로 나와 있다는 점에서 포인터 라는건 확실히 알게 되었습니다.

하지만 해당 메모리로 가 보면 쓰레기 값(garbage data)가 있네요. 도저히 함수의 시작지점이라고 볼만한 명령어나 코드가 보이지 않습니다.

 

이것에 대해서는 따로 연구 해보도록 하고 일단 함수명이 포인터라는건 확실하게 알게 되었네요

 

본문에서 포인터변수의 값에 함수명을 저장하게 되었는데

int (*fp)(int, int);

이 문장은 fp라는 포인터 변수의 선언입니다. 이 포인터는 반환값이 int 이고, int 매개변수 두개를 받는 함수형태를 참조하는 포인터 라는 뜻입니다.

fp = sum;

이 문장으로 함수 포인터를 초기화 했죠. sum  이라는 함수의 주소를 참조하도록 한거죠.

 

여기서 주의 할 점은 함수형 포인터 변수를 선언할 때 포인터와 변수를 괄호로 완전히 묶어야 한다는 것입니다.

int *fp(int, int);

실수로라도 위와 같이 선언 해 버리면 매개변수로 정수형 2개를 받으면서 정수형을 참조하는 주소값을 반환하는 함수가 선언되어 버립니다. 의도와 완전히 다른 결과가 생기니까 주의 해야 겠습니다.

 

 

함수 포인터의 활용

#include<stdio.h>

void func(int* (int, int));
int sum(int, int);
int mul(int, int);
int max(int, int);

int main(void)
{
	int sel;

	printf("01 두 정수의 합 \n");
	printf("02 두 정수의 곱 \n");
	printf("03 두 정수 중에서 큰 값 계산 \n");
	printf("원하는 연산을 선택하세요 : ");
	scanf("%d", &sel);

	switch (sel)
	{
	case 1: func(sum); break;
	case 2: func(mul); break;
	case 3: func(max); break;
	}

	return 0;
}

void func(int(*fp)(int, int))
{
	int a, b;
	int res;

	printf("두 정수의 값을 입력하세요 : ");
	scanf("%d%d", &a, &b);
	res = fp(a, b);
	printf("결과 값은 %d\n", res);
}

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

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

int max(int a, int b)
{
	if (a > b) return a;
	else return b;
}

선언부

void func(int* (int, int));

반환값이 없는 함수를 선언합니다.. Parameter로 함수 포인터를 받는데, 이 포인터는 어떤형태를 참조하냐 하면

int 반환값을 가지고 있고, Parameter로 int형태 2개를 받는 함수를 참조 합니다..(그런 식으로 쓰일 것입니다.)

라고 선언한 것입니다.

복잡하지만 파트 하나하나 뜯어서 살펴보면 이해하는데 어려움이 없습니다.