변수만 메모리 공간에 저장되는 것이 아니다.
함수도 바이너리 형태로 메모리 공간에 저장되어 호출 시 실행된다.
이렇게 메모리상에 저장된 함수의 주소 값을 저장하는 포인터 변수가 바로 함수 포인터 변수
이다.
배열의 이름이 배열의 시작주소 값을 의미하듯,
함수의 이름도 함수가 저장된 메모리 공간의 주소 값을 의미한다.
배열의 이름과 마찬가지로 함수의 이름도 그 형태가 상수이다.
함수의 주소 값 저장을 위한 포인터 변수를 별도로 선언할 수 있으며, 이러한 용도로 선언된 포인터 변수를 가리켜 '함수 포인터 변수'라 한다.
함수이름의 포인터 형은 반환형과 매개변수의 선언을 통해 결정짓도록 약속되어있다.
// ex
int SimpleFunc(int num){....}
// 반환형 int, 매개변수 int형 선언된 포인터 형
double ComplexFunc(double num1, double num2){....}
// 반환형 double, 매개변수 2개가 double형으로 선언된 포인터 형
함수 포인터 변수에는 반환형 정보와 매개변수 선언의 정보가 모두 포함되어 있어야한다.
위에서 제시한 예시 함수 2개의 함수 포인터 변수는
int (*fptr) (int);
double (*fdptr) (double, double);
이 된다.
함수 포인터 변수에 함수의 주소 값을 저장하려면
fptr = SimpleFunc;
fdptr = ComplexFunc;
와 같이 대입연산을 진행하면 된다.
이것은 fptr(3)
과 fdptr(3.1, 2.5)
와 같이 함수 호출과 동일한 결과를 보인다.
#include <stdio.h>
void SimpleAdder(int n1, int n2)
{
printf("%d + %d = %d \n", n1, n2, n1 + n2);
}
void ShowString(char* str)
{
printf("%s \n", str);
}
int main()
{
char* str = "Function Pointer";
int num1 = 10, num2 = 20;
void (*fptr1)(int, int) = SimpleAdder;
void (*fptr2)(char*) = ShowString;
/* 함수 포인터 변수에 의한 호출 */
fptr1(num1, num2);
fptr2(str);
return 0;
}
> 출력
10 + 20 = 30
Function Pointer
#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()
{
int age1 = 20;
int age2 = 30;
int first;
printf("입장 순서 1 \n");
first = WhoIsFirst(age1, age2, OlderFirst);
printf("%d세와 %dtp wnd %d세가 먼저 입장! \n\n", age1, age2, first);
printf("입장 순서 2 \n");
first = WhoIsFirst(age1, age2, YoungerFirst);
printf("%d세와 %dtp wnd %d세가 먼저 입장! \n\n", age1, age2, first);
return 0;
}
> 출력
입장 순서 1
20세와 30tp wnd 30세가 먼저 입장!
입장 순서 2
20세와 30tp wnd 20세가 먼저 입장!
이 예제에서는 매개변수의 선언으로 함수 포인터 변수가 올 수 있음을 보이고, 더불어서 전달되는 인자에 따라서 달리 동작하는 함수의 정의도 가능함을 보이고자 한다.
void형 포인터 변수
란? void * ptr;
과 같이 선언되는 포인터 변수를 가리킨다.
void형 포인터 변수는 무엇이든 담을 수 있는 바구니와 같다.
여기에는 함수의 주소 값이던 변수의 주소 값이던 모두 담을 수 있다.
#include <stdio.h>
void SoSimpleFunc(void)
{
printf("I'm so simple");
}
int main ()
{
int num = 20;
void* ptr;
ptr = # // 변수 num의 주소 값 저장.
printf("%p \n", ptr);
ptr = SoSimpleFunc; // 함수의 주소 값 저장.
printf("%p \n\n", ptr);
return 0;
}
> 출력
0000001EB794FBC4
00007FF7F1BA108C
void형 포인터 변수를 가지고는 아무런 포인터 연산을 하지 못한다.
값의 변경이나 참조도 불가능하다.
void형 포인터 변수에는 가리키는 대상에 대한 어떠한 형(Type)정보가 담겨있지 않기 때문이다.
따라서 아래와 같이 코드를 컴파일 하면 에러가 발생한다.
int main ()
{
int num = 20;
void * ptr = #
*ptr = 20; // 컴파일 에러
ptr++; // 컴파일 에러
....
}
그래서 void형 포인터는 일단 주소 값에만 의미를 두고 포인터의 형은 나중에 결정할 때 유용하게 사용된다.
이 후에 Chapter 25에서 다룰 메모리의 동적 할당과 매우 연관 있기 때문에 이를 알아둬야한다.
프로그램 실행 시 main 함수로 전달할 인자를 열거할 수 있으며 (결정할 수 있으며), main 함수 역시 이러한 인자를 전달받을 수 있도록 제한된 형태의 매개변수 선언이 가능하다.
#include <stdio.h>
int main(int argc, char *argv[])
{
int i=0;
printf("전달된 문자열의 수: %d \n", argc);
for(i=0; i<argc; i++)
printf("%d번째 문자열: %s \n", i+1, argv[i]);
return 0;
}
main 함수의 매개변수 선언 중 char * argv[]
에 대해 설명하자면, 이것은 char형 더블 포인터 변수이고, 이는 char형 포인터 변수로 이뤄진 1차원 배열의 이름을 전달 받을 수 있는 매개변수이다.
#include <stdio.h>
void ShowAllString(int argc, char* argv[])
{
int i;
for (i = 0; i < argc; i++)
printf("%s \n", argv[i]);
}
int main()
{
char* str[3] = {
"C Prpgraming",
"C++ Programing",
"JAVA Programing"
};
ShowAllString(3, str);
return 0;
}
> 출력
C Prpgraming
C++ Programing
JAVA Programing
문자열 배열을 매개변수 인자로 전달 받는거라고 이해하면 좋을거 같다...!
문자열 배열의 마지막에 NULL이 삽입되는지 확인해보자.
#include <stdio.h>
int main(int argc, char* argv[])
{
int i = 0;
printf("전달된 문자열의 수: %d \n", argc);
while (argv[i] != NULL)
{
printf("%d번째 문자열: %s \n", i + 1, argv[i]);
i++;
}
return 0;
}
<Review>
사실 터미널 켜서 .exe
확장자 프로그램 실행시키라는데 난 exe 확장자도 안보이고 어떻게 하는지 모르겠다...
이따가 스터디원들에게 물어봐야지 ㅠㅠ
우선 다음 chapter인 도전! 프로그래밍 3 도전 가보자구~