내장형 하드웨어/C언어

C언어 - 함수 포인터, 다중 포인터, 구조체

동화다아아 2011. 7. 5. 17:59

- 함수 포인터

⇒ 변수나 파일을 포인터 변수를 이용하여 접근하듯이, 함수도 포인터를 이용하여 호출할 수 있다. 함수 포인터(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

⇒ 출력 결과(%08X)         

   

⇒ *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이 출력된다.

⇒ 출력 결과(%08X)        

      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

가 된다.

⇒ 출력 결과(%08X)         

 

→ %d로 출력