변수만 메모리 공간에 저장되는 것이 아님
프로그램 실행의 흐름을 구성하는 함수들도 바이너리 형태로 메모리 공간에 저장되어 호출 시 실행
메모리상에 저장된 함수의 주소 값을 저장하는 포인터 변수가 바로 '함수 포인터 변수'
함수 포인터의 이해
프로그래머가 정의하는 모든 함수는 프로그램 실행 시 '메인 메모리'에 저장되어서 실행이 된다.
함수의 이름은, 메모리상에 저장된 함수의 주소 값을 의미한다.
배열의 이름이 배열의 시작주소 값을 의미하듯, 함수의이름도 함수가 저장된 메모리 공간의 주소 값을 의미
이러한 함수의 주소 값 저장을 위한 포인터 변수를 별도로 선언할 수 있으며, 이러한 용도로 선언된 포인터 변수를 가리켜 '함수 포인터'라 한다.
그렇다면 함수 포인터를 어떻게 선언??
함수 포인터의 포인터 형을 결정해야 한다.
int simplefunc(int num){....}
함수의 반환형과 매개변수 선언
반환형 : int
매개변수 선언 : int num
함수 이름 simplefunc은 simplefuc 함수의 주소 값을 의미하는 상수형태의 함수 포인터가 된다.
그렇다면 simplefunc의 형태는?
"함수이름의 포인터 형은 반환형과 매개변수의 선언을 통해서 결정짓도록 약속"
만약 함수 형태가 아래와 같다면
int simplefunc(int num){....}
"반환형이 int이고 매개변수로 int형 변수가 하나 선언된 포인터 형(type)"
double simplefunc(double num1, double num2){....}
"반환형이 double이고 매개변수로 두 개의 double형 변수가 선언된 포인터 형(type)
적절한 함수 포인터 변수의 선언
함수의 주소 값(함수 포인터 값)을 저장할 수 있는 포인터 변수는 어디에 선언?
이 포인터 변수에는 반환형 정보와 매개변수 선언의 정보가 모두 표현되어 있어야 한다.
다음과 같이 의미 부여하여 함수 포인터 변수를 선언
함수를 대상으로 예
int simplefunc(int num1, int num2) {....}
반환형이 int형이고 매개변수로 int형 변수가 두 개 선언되었으니, 이 함수의 주소 값 저장을 위한 함수 포인터 변수는 다음과 같이 선언
int (*fptr) (int, int);
그리고 이 함수 포인터 변수에 simplefunc의 주소 값을 저장하려면 다음과 같이 대입연산을 해야 함
fptr = simplefunc;
이렇듯 대입연산이 끝나고 나면 fptr과 simplefuc에는 동일한 값이 저장되어, 상수냐 변수냐 이 둘의 유일한 차이점이 된다.
따라서 fptr을 이용해서도 다음과 같이 simplefunc 함수를 호출할 수 있다.
fptr(3, 4); // simplefunc(3, 4)와 동일한 결과를 보임
#include <stdio.h>
void simpleadd(int, int);
void showstring(char*);
void shows(char);
int main(void)
{
char* str = "Function Pointer";
int num1 = 10;
int num2 = 20;
void (*fptr1)(int, int) = simpleadd;
void (*fptr2)(char*) = showstring;
/* 함수 포인터 변수에 의한 호출 */
fptr1(num1, num2);
fptr2(str);
return 0;
}
void simpleadd(int n1, int n2)
{
printf("%d + %d = %d \n", n1, n2, n1 + n2);
}
void showstring(char* str)
{
printf("%s \n", str);
}
예제 하나 더 제시
매개변수의 선언으로 함수 포인터 변수가 올 수 있음을 보이고, 더불어서 전달되는 인자에 따라서 달리 동작하는 함수의 정의도 가능함을 보이고자 함.
#include <stdio.h>
int whoisfirst(int age1, int age2, int (*cmp)(int n1, int n2))
{
return cmp(age1, age2);
}
int olderfirst(int age1, int age2)
{
if (age1 > age2)
return age1;
else if (age1 < age2)
return age2;
else
return 0;
}
int youngerfirst(int age1, int age2)
{
if (age1 < age2)
return age1;
else if (age1 > age2)
return age2;
else
return 0;
}
int main(void)
{
int age1 = 20;
int age2 = 30;
int first;
printf("입장순서 1 \n");
first = whoisfirst(age1, age2, olderfirst);
printf("%d세와 %d세 중 %d세가 먼저 입장! \n\n", age1, age2, first);
printf("입장순서 2 \n");
first = whoisfirst(age1, age2, youngerfirst);
printf("%d세와 %d세 중 %d세가 먼저 입장! \n\n", age1, age2, first);
return 0;
}
int whoisfirst(int age1, int age2, int (*cmp)(int n1, int n2))
매개변수 선언으로도 함수 포인터 변수가 올 수 있다.
따라서, 이 함수를 호출할 때에는 세 번째 인자로, 반환형 int이고 두 개의 int형 변수가 매개변수로 선언된 함수의 이름(함수의 주소 값)이 전달 되어야 한다.
return cmp(age1, age2);
첫 번째, 두 번째 인자를 재 전달하면서 세 번째 인자로 전달된 함수를 호출하고 있다. 그리고 이때 반환되는 값을 재 반환 한다.
결과적으로 세 번째 인자로 어떤 함수의 주소 값이 전달되느냐에 따라서 whoisfirst의 동작방식이 결정된다.
위 예제에서 보이듯이 함수 포인터를 통해서 함수의 동작방식에 유연성을 제공할 수 있음.
형(Type)이 존재하지 않는 void 포인터
다음과 같이 선언되는 포인터 변수를 가리켜 'void형 포인터 변수'라 한다.
void* ptr;
void형 포인터 변수는 무엇이든 담을 수 있는 바구니에 비유.
void형 포인터 변수에는 어떠한 변수의 주소 값이든 담을 수 있다.
#include <stdio.h>
void simplefunc(void)
{
printf("simple func");
}
int main(void)
{
int num = 20;
void* ptr;
ptr = # // 변수 num의 주소 값 저장
printf("%p \n", ptr);
ptr = simplefunc; // 함수 simplefunc의 주소 값 저장
printf("%p \n", ptr);
}
무엇이든 담을 수 있으니 좋게 느껴짐?
하지만 단점도 있음
void형 포인터 변수를 가지고는 아무런 포인터 연산도 하지 못한다.
값의 변경이나 참조도 불가능 하다.
void형 포인터 변수에는 가리키는 대상에 대한 어떠한 형(type) 정보도 담겨져 있지 않으므로 이는 당연.
따라서 아래의 코드 실행시 컴파일 에러
#include <stdio.h>
int main(void)
{
int num = 20;
void* ptr = #
*ptr = 20;
ptr++;
}
void형 포인터는 다음의 상호아 또는 다음과 유사한 형태의 전략이 필요한 경우에 유용하게 사용된다.
"일단 주소 값에만 의미를 두고, 포인터 형은 나중에 결정한다."
→ 나중에 메모리의 동적 할당에서 요긴하게 쓰인다고 함
참고 : [윤성우 열혈 C 프로그래밍] - 대부분의 내용 및 코드는 이 책에서 개인 공부 정리 목적으로 참고하였습니다.
'Language&Framework&Etc > C' 카테고리의 다른 글
문자와 문자열 관련 함수(21-1) 스트림과 데이터의 이동 (0) | 2020.12.10 |
---|---|
함수 포인터와 void 포인터(19-2) main 함수로의 인자 전달 (0) | 2020.12.10 |
다차원 배열과 포인터의 관계(18-2) 2차원 배열이름의 특성과 주의사항 (0) | 2020.12.10 |
다차원 배열과 포인터의 관계(18-1) 2차원 배열 이름의 포인터형 (0) | 2020.12.10 |
포인터의 포인터(17-2) 다중 포인터 변수와 포인터의 필요성 (0) | 2020.12.10 |