C언어 - 함수 포인터, 다중 포인터, 구조체
- 함수 포인터
⇒ 변수나 파일을 포인터 변수를 이용하여 접근하듯이, 함수도 포인터를 이용하여 호출할 수 있다. 함수 포인터(function pointer)는 함수의 실행코드의 시작주소를 가지고 있으며, 함수 포인터를 이용하여 함수를 호출할 수 있다. 배열 이름이 메모리 주소값을 가지는 상수이듯이, 함수 이름도 함수의 시작코드의 주소값을 가지는 주소값 상수로 볼 수 있다.
⇒ 반환자료형 (* 함수 포인터 변수 이름) (인수1, 인수2, ……, 인수 n);
// 함수 포인터 예제
#include<stdio.h>
int(*test(void)) (const char *, ...);
int(*(*test2(void))(void)) (const char *, ...);
int main()
{
int (*fp) (const char *, ...)=0; // printf를 가리키는 함수 포인터
// 포인터 변수 fp을 0으로 초기화 했다.
fp = printf;
fp("Hi!!!\n");
return 0;
}
int(*test(void)) (const char *, ...)
{
return printf;
}
int(*(*test2(void))(void)) (const char *, ...)
{
return test;
}
⇒ main() 안에서 int (*fp)(const char *, ...) = 0; 가 선언되었다. 이것은 printf를 가리키는 함수 포인터이다.
⇒ int(*test(void)) (const char *, ...) 함수를 살펴보면
→ printf의 원형 int (*) (const char *, ...)에서 (* 다음에 test(void)를 삽입한 모습임을 알 수 있다.
⇒ test와 test2 함수는 위 소스에서 출력과는 관계가 없다.
// 인자를 가진 함수 포인터 문제 풀이
#include <stdio.h>
//int printf(const char *, ...); printf를 반환한다는뜻
//test1(float f);
int(*test1(float f))(const char *, ...);
//test2(char c);
int (*(*test2(char))(float))(const char *, ...)
int main()
{
int (*fp)(const char *, ...)=0;
//int printf(const char *, ...);
//fp1 선언
int (*(*fp1)(float f))(const char *, ...)=0; // 포인트가 1개, 함수 선언이 아니다.
//fp2 선언
int (*(*(*fp2)(char c))(float))(const char *, ...)=0;
// 포인트가 1개(3개가 아니다.), 함수 선언이 아니다.
fp2 = test2;
fp1 = fp2('a');
fp = fp1(0.1);
fp("Hi....\n");
return 0;
}
int (*test1(float f))(const char *, ...)
{
printf("float : %0.1f\n", f);
return printf;
}
int (*(*test2(char c))(float))(const char *, ...)
{
printf("char : %c\n", c);
return test1;
}
⇒ test1 함수 선언
→ int printf(const char *, ...);
→ int (*)(const char *, ...);
→ int (* 까지 test1 앞에 복사
→ int (*test1(float f) 다음에 나머지 )(const char *, ...);를 붙여 넣는다.
⇒ fp1 선언
→ int (*test1(float f))(const char *, ...);를 fp1으로 선언하기 위해 붙여넣고.
→ int (*fp)(const char *, ...)=0;와 int printf(const char *, ...); 를 비교해 보면 printf가 (*fp)로 바뀌었음을 알 수 있다.
→ 따라서 int (*test1(float f))(const char *, ...)를 같게 하면
→ int (*(*fp1)(float f))(const char *, ...)=0; 으로 바꾸어 진다.
⇒ test2 함수 선언
→ test1의 타입이 필요하므로 복사 - int (*test1(float f))(const char *, ...)
→ test1을 (*)로 변경 - int (*(*)(float f))(const char *, ...)
→ int (*(* 까지 test2 앞에 복사
→ int (*(*(test2(char c) 다음에 나머지 )(float f))(const char *, ...)를 붙여넣는다.
→ int (*(*(test2(char c))(float))(const char *, ...);가 된다.
⇒ fp2 선언
→ int (*(*(test2(char c))(float))(const char *, ...);를 fp2로 선언하기 위해 붙여넣고.
→ fp1과 같이 int (*(*fp1))(const char *, ...)=0;와 int printf(const char *, ...); 를 비교하면 (*(*(fp2)))가 된다.
→ 따라서 int (*(*(*fp2)(char c))(float))(const char *, ...)=0; 으로 바꾸어진다.
- 다중 포인터 개념
int A = 100;
int *P = &A;
int **PP = &P;
int ***PPP = &PP;
⇒ 정리해보면 아래 표와 같다.
|
* |
** |
*** |
A → 100(값) |
*A → error |
**A → error |
***A → error |
P → 1000(주소) |
*P → 100(값) |
**P → error |
***P → error |
PP → 2000(주소) |
*PP → 1000(주소) |
**PP → 100(값) |
***PP → error |
PPP → 3000(주소) |
*PPP → 2000(주소) |
**PPP → 1000(주소) |
***PPP → 100(값) |
// 다중 포인터 예제
#include<stdio.h>
int main()
{
int A = 100;
int *P = &A;
int **PP = &P;
int ***PPP = &PP;
printf("------------------\n");
printf("&A : %08X\n", &A);
printf("&P : %08X\n", &P);
printf("&PP : %08X\n", &PP);
printf("&PPP : %08X\n", &PPP);
printf("------------------\n");
printf("A : %d\n", A);
printf("P : %08X\n", P);
printf("PP : %08X\n", PP);
printf("PPP : %08X\n", PPP);
printf("------------------\n");
//printf("*A : %d\n", *A);
//error - 곱셈기호 '*' 로 해석된다.
printf("*P : %d\n", *P);
printf("*PP : %08X\n", *PP);
printf("*PPP : %08X\n", *PPP);
printf("------------------\n");
//printf("**A : %d\n", **A); - error
//printf("**P : %08X\n", **P); - error
printf("**PP : %d\n", **PP);
printf("**PPP : %08X\n", **PPP);
printf("------------------\n");
//printf("***A : %d\n", ***A); - error
//printf("***P : %08X\n", ***P); - error
//printf("***PP : %08X\n", ***PP); - error
printf("***PPP : %d\n", ***PPP);
return 0;
}
- 구조체
#include<stdio.h>
typedef struct
{
int A;
int B;
int C;
}EMB;
int main()
{
int array[6];
EMB *stp;
unsigned char *UCP;
stp = (EMB *)array;
UCP = (unsigned char *)array;
*UCP = 0x64;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x63;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x62;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x61;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
++UCP;
*UCP = 0x00;
printf("===========================\n");
printf("stp -> A : %d\n", stp -> A);
printf("stp -> B : %d\n", stp -> B);
printf("stp -> C : %d\n", stp -> C);
printf("==========================\n");
printf("array[0] : %d\n", array[0]);
printf("array[1] : %d\n", array[1]);
printf("array[2] : %d\n", array[2]);
printf("===========================\n");
stp = (EMB *)&array[1];
printf("stp -> A : %d\n", stp -> A);
printf("stp -> B : %d\n", stp -> B);
printf("stp -> C : %d\n", stp -> C);
printf("===========================\n");
UCP = (unsigned char *)array;
++UCP;
stp = (EMB *)UCP;
printf("stp -> A : %d\n", stp -> A);
printf("stp -> B : %d\n", stp -> B);
printf("stp -> C : %d\n", stp -> C);
return 0;
}
⇒ 메모리 모양 예상
64 |
00 |
00 |
00 |
63 |
00 |
00 |
00 |
62 |
00 |
00 |
00 |
61 |
00 |
00 |
00 |
… |
⇒ 출력 결과를 통해 stp -> A부터 C까지와 array[0]부터 array[2]까지는 같은 값을 가지는 것을 알 수 있다.
64 |
00 |
00 |
00 |
63 |
00 |
00 |
00 |
62 |
00 |
00 |
00 |
61 |
00 |
00 |
00 |
… |
⇒ *UCP = 0x64; 를 통해 int array[6]에 1바이트씩 값을 넣고 있는데 처음에는 64 00 00 00 의 순서로 값이 들어간 것을 확인할 수 있다. 단, CPU가 little endian방식을 취하므로 들어가는 것과 출력이 다르다. (%d로 출력하면 100, 99, 98이 출력된다.)
⇒ 세 번째 출력의 stp -> A부터 C까지를 확인해 보면
stp = (EMB *)&array[1];
를 통해 A가 array[1]에서 시작되어 C가 array[3]까지 표시된다.
64 |
00 |
00 |
00 |
63 |
00 |
00 |
00 |
62 |
00 |
00 |
00 |
61 |
00 |
00 |
00 |
… |
→ %d로 출력하면 98, 97, 96이 출력된다.
⇒ UCP = (unsigned char *)array;
++UCP;
stp = (EMB *)UCP;
→ 위의 소스로 1바이트만큼 이동한 UCP가 stp에 대입되고 따라서 stp가 가리키는 값은
64 |
00 |
00 |
00 |
63 |
00 |
00 |
00 |
62 |
00 |
00 |
00 |
61 |
00 |
00 |
00 |
… |
가 된다.
'내장형 하드웨어 > C언어' 카테고리의 다른 글
C언어 - 동적 자료형, 연결리스트 (0) | 2011.07.08 |
---|---|
C언어 - 구조체 크기 (0) | 2011.07.07 |
C언어 - 배열의 값과 주소 표시법, 함수 포인터 배열 (0) | 2011.07.04 |
C언어 - text mode, binary mode, fprintf, freed, 배열과 포인터 (0) | 2011.06.30 |
C언어 - 저수준 입출력, 리다이렉션 (0) | 2011.06.30 |