최상단 광고

2012년 4월 8일 일요일

[C 프로그래밍] 함수포인터


포인터? 생각만 하면 머리가 아파오는 녀석이죠..C 언어로 생계를 유지해온 저로서도 복잡한 포인터를 보면 피하고 싶습니다..이런 포인터 인데 함수포인터라니? 이건 뭐 대마왕급이군요...

그런데 이런 대마왕도 상황에 따라, 그리고 고급프로그래밍을 위해서 간간히 써주어야만 하는 돌발상황이 발생하곤 합니다..어쩔수 없는 운명이죠..TT

피할수 없다면 한번 부딪혀 보는 건 어떨까요? 기초 개념만 잡으면 너무 쉽다(?)고 느끼실 겁니다..자, 그럼 기본부터 한번 시작해 보겠습니다..

우선 다음과 같은 코드를 보죠..아주 단순한 코드입니다..
-----------------------------------------------------------------------------------
main()
{
int a = 10;
int* b = &a;
...
...
}
-----------------------------------------------------------------------------------
변수 a 에 10을 대입하고 변수 b 에는 a 의 주소값을 저장합니다. 그럼 여기서 질문하나 해볼까요? 변수 a 와 b 는 어디에 존재할까요? CPU ? 하드디스크 ? 아님 메모리?

모두 아시겠지만 변수는 메모리, 정확히 말하면 RAM 에 존재합니다. 아래 그림에 간략히 나타내보았습니다.

그럼 다음 코드를 한번 보겠습니다..좀전보다 조금 어렵습니다..(???)
-----------------------------------------------------------------------------------
void func(int x)
{
printf("x = %d\n",x);
}

main()
{
int a = 10;
int* b = &a;

func(a);
}
-----------------------------------------------------------------------------------

이번에는 함수 func 라는 놈이 등장했네요..여기서 다시 질문하나 할께요..변수는 RAM 에 저장되는데 그럼 함수 func 는 어디에 저장될까요? 또 main 도 특별한 함수죠..즉, 시작할때 제일 먼저 불리는 함수..이런 함수들은 도대체 어디에 존재하는걸까요????

함수는 변수와 마찬가지로 RAM 에 저장되어 있습니다...왜냐면 실행되는 모든 프로그램은 RAM 에 저장되어야 하는데 함수도 프로그램의 일부이니까요..그림으로 나타내 볼까요..

위 그림처럼 RAM 의 어딘가에 func 라는 함수부분이 저장되어 있습니다. RAM에는 언제나 주소가 있죠? 예를 들어 변수 a 는 &a 라는 주소에 있고 변수 b 는 &b 라는 곳에 있습니다. 그럼 func 는 어디에 있을까요?
주소를 나타내려면 & 를 붙인다고 했으니 &func? 아님 임의의 다른곳??
C 언어에서 함수가 저장되어 있는 주소값은 바로 함수 이름입니다. 즉, 함수이름이 함수가 저장되어 있는 첫번째 번지를 나타냅니다. (마치 배열과 비슷합니다. 배열 이름이 배열의 시작주소를 나타내는 것처럼요..) 그래서 func 라는 함수가 저장되어 있는 번지는 func 이고, main 함수가 저장되어 있는 번지는 main 입니다..(많이 헛갈리시죠..예전에 제가 그랬습니다...)
이제 함수가 저장되어 있는 주소값을 알았으니 그 값을 저장할수 있는 변수도 있어야겠죠..이렇듯 함수가 저장되어 있는 주소값을 저장할수 있는 변수를 '함수포인터' 라고 합니다. 즉, 함수포인터는 함수의 이름을 가지는 변수입니다..
그럼 함수이름을 저장할수 있는 변수는 어떻게 선언하고 초기화 할까요? 다음의 소스를 한번 보죠..
-----------------------------------------------------------------------------------
void func(int x)
{
printf("x = %d\n",x);
}

main()
{
int a = 10;
int* b = &a;

void (*test)(int);
test = func;

func(a);
}
-----------------------------------------------------------------------------------
우선 'void (*test)(int);' 부분입니다. 해석을 해보면 test 라는 변수는 인자로 한개의 정수(-int-) 를 가지고 반환형이 없는 (-void-) 함수를 가리키는 포인터이다 입니다. 이렇게 선언된 test 라는 변수에 func, 즉 func 가 저장되어 있는 주소값(-함수이름-)을 대입하는 곳이 소스에서 'test=func' 입니다. (func 함수가 인자로 정수한개를 받고 반환형은 없죠..확인해보세요)
그림을 통해 이해해보죠..
이제 마지막으로 함수포인터를 통해 함수가 어떻게 호출되는지 알아보겠습니다..소스코드에서 변수 포인터 b 를 통해 a 를 접근하고자 할때 '*b' 라고 기술했습니다. 그럼 함수포인터는 ? 함수포인터도 변수 이므로 *b 처럼 * 연산자를 이용해 함수를 접근할수 있습니다.다음의 코드를 보면서 결과값이 같다는 것을 확인해보시기 바랍니다...코드에서 (*test)(a) 의 해석은 다음과 같다고 할수 있습니다. 'test 라는 변수가 가리키는 곳에다 a 값을 넘겨준다'
-----------------------------------------------------------------------------------
void func(int x)
{
printf("x = %d\n",x);
}

main()
{
int a = 10;
int* b = &a;

void (*test)(int);
test = func;

func(a);
(*test)(a);
}
-----------------------------------------------------------------------------------
어떠신가요? 함수포인터라고 무시무시한 이름을 가지고 있지만 막상 별것 없죠..이런 함수포인터는 속도가 중요시되거나 그래픽관련 프로그래밍을 구현하고자 할때 가끔 쓰입니다. 또한 커널 프로그래밍을 할때도요..그러나 기본에 충실하시면 어렵지 않게 이해 할수 있을거라 믿습니다..
(이제 함수 포인터는 대마왕의 자리를 내주고 쓸쓸히 은퇴해야겠네요..)
다음의 예제를 한번 풀어보시고 이해해 보시기 바랍니다...(다음주제는 네트웍프로그래밍을 진행해 볼까 합니다..많은 응원부탁 ^^..)
언제나 즐거운 시간만 되세요...
[1] int (*fPtr1)(int); [이해난이도 : 하]
[2] int (*fPtr2)(int, int); [이해난이도 : 하]
[3] void (*signal(int, void(*handler)(int)) ) (int); [이해난이도 : 최상]

댓글 없음: