noname02
함수포인터 이해하기 본문
함수도 변수라고 생각하면 된다.
그러니까 함수의 이름도 사실은 변수인 것이다.
마치 int a = 10;
으로 하면 어떤 메모리공간에 a라는 이름이 할당되고 거기에 10이라는 값이 들어가서 a를 10처럼 사용할 수 있듯이
void Func(int a) { ... } ;
로 함수를 선언하면 어떤 메모리공간에 Func라는 이름이 할당되고 호출하면 { ... } 를 수행하는 것이다.
물론 Func만 호출한다고 될 리가 없다. 함수는 매개변수를 갖기 때문에 Func(); 와 같이 소괄호를 같이 넣어줘야 한다. 물론 매개변수도 넣어줘야 하고.
자 그럼 변수에는 포인터라는 개념이 있다.
int a = 10;
int *b = &a;
이렇게 하면 b라는 변수공간에 a의 주소값이 들어가 있게 된다. 그런데 알다시피 b는 포인터변수이기 때문에 호출방식에 따라 컴퓨터가 수행하는 일도 다르다.
그냥 b만 호출하면 당연히 b라는 변수공간에 있는 값인 a의 주소값을 출력한다.
*b를 호출하면 b가 포인터변수이기 때문에 *연산자가 붙었으니 b에 있는 주소로 접근해서 그 주소에 있는 값을 int형식으로 읽는다.
괜히 포인터에도 자료형이 있는 것이 아니다. 포인터 변수가 어떻게 행동해야 될 지 그 정보를 주는 것이다.
종합해보면 *연산자는 '이 변수에 있는 주소로 접근을 해서 니 자료형대로 해당 값을 구워먹고 삶아먹으렴' 이라는 명령이 담겨있는 것이다.
*b = 20;을 하면 b에 들어있는 주소값으로 접근해서 해당 값을 int형 20으로 변경하고, *b; 는 int형으로 읽고.. 마치 그냥 포인터에 저장된 주소공간의 이름에 붙여진 변수처럼 행동을 하는 것이다. 여기서는 그 변수가 a가 되는 것이고.
함수포인터도 마찬가지다.
원래 void Func(int a) {...} 에서 Func는 변수마냥 사용된다.
int a=10; 으로 선언하고 난 뒤에 a; 이렇게만 호출하면 되는 것처럼 함수도 한번 선언하고 나면 Func(); 형태로 막 호출하는 것이다.
그럼 함수의 포인터를 선언해보자.
일반 변수의 포인터를 선언할 때 우리는 자료형을 적어줬다. 위에서도 언급했듯 정의된 자료형처럼 포인터를 운용하기 위함이다. 변수의 구조가 자료형 변수이름; 이니까 포인터 역시 자료형 변수이름; 의 형태로 정의되었는데 포인터라는 옵션이 붙었으므로 자료형 *변수이름; 이렇게 쓰인다.
함수포인터도 역시 함수의 구색을 맞춰줘야 한다.
함수는 반환형 함수이름(매개변수...); 의 형태이니 함수포인터 역시 반환형 함수이름(매개변수...) 와 같은 형태로 온다. 그런데 얘도 포인터니까 당연히 이름에 *를 붙여줘야 한다.
반환형 *함수이름(매개변수...);
근데 여기서 주의해야 할 점이 있는데, 위처럼 *를 그냥 쓴다면 이 *은 반환형에 붙어서 int*, 즉 반환형이 int포인터형이라는 뜻이 되어버린다.
따라서 적절하게 괄호를 쳐줘서 반환형 (*함수이름)(매개변수...); 와 같은 형태로 만들어줘야 한다.
근데 위에서 얘기할 때 void Func(int a) {...}에서의 Func가 마치 변수마냥 사용된다고 했는데, 여기서 Func는 배열과 성질이 비슷하다.
배열의 이름은 포인터라는 점을 알고 있을 것이다.
int a[10]; 에서 a[0], a[1]...등은 변수의 이름처럼 쓰이지만 사실은 포인터라는 것을 알고 있을 것이다. 단지 *로 표현을 안했을 뿐이지, a[0]이라는 표현에 이미 * 연산자의 기능까지 합쳐져서 행동하는 것이다.
마찬가지로 함수 역시 Func는 배열의 이름처럼 포인터이다. 그러니까 함수의 이름이 포인터라는 것이다.
단지 함수는 데이터를 저장하는 자료형이 아니라서 우리가 함수의 주소에 직접 접근하지도 못하거니와 포인터 연산과 같은 행동도 못할 뿐이다. 그런데 어째든 함수의 이름 역시 포인터다.
그럼 이것이 주는 의의가 무엇인가 하면 바로 이러한 형태로 우리가 사용할 수 있는 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <stdio.h> int Plus(int a, int b) { return a + b; } int Minus(int a, int b) { return a - b; } int main(void) { int select; int num; int(*pFunc)(int, int); scanf("%d", &select); if (select == 1) pFunc = Plus; else if (select == 2) pFunc = Minus; num = pFunc(1, 2); printf("%d",num); return 0; } | cs |
3, 7번 행에서 Plus함수와 Minus함수를 정의했다.
17번 행에서 함수포인터 pFunc를 정의했다.
19번 행에서 값을 입력받는데, 1인경우
22번행이 실행되어 pFunc함수포인터 주소에 Plus함수 주소가 들어가고 19번 행에서 값이 2인 경우
24번행이 실행되어 pFunc함수포인터 주소에 Minus함수 주소가 들어간다.
그리고 26번행을 통해 num에 함수포인터 pFunc(1, 2)의 결과값을 반환한다.
함수역시 포인터라고 했으며, 이러한 포인터에는 주소값이 들어있는게 당연하다. 따라서 이러한 것이 가능한 것이다. 단지 pFunc라는 함수포인터에 Plus 함수의 주소값, Minus 함수의 주소값을 상황에 따라 대입해줬을 뿐이다.
이것이 바로 함수포인터다.