상세 컨텐츠

본문 제목

[typedef] About typedef in Function Pointer

C

by 메타샤워 2023. 7. 25. 12:54

본문

typedef 는 C코드에서 흔히 볼수 있는 말그대로 타입선언이다.

하지만 이 typedef를 제대로 활용하기 위해서는 이해가 필요하다. 
 
typedef는 전처리기가 아니라 명령어이기 때문에 반드시 문장 끝에 세미콜론(;)을 붙여주어야 하며 모든 변수타입을 지정할 수 있다.
이는 가동성의 증대와 변수 타입을 효율적으로 관리하도록 한다.
웬만한 하나의 모듈, 자체에는 사실상 엄청난 typedef문이 들어가 있다.
각 회사마다 프로그램 내 규정에 맞도록 자료형을 정해놓는다.
 
typedef의 의미는 " 변수명을 타입이름으로 변경하는 기능 "
이라고 말할수 있다.
 
보통 typedef A B;
라는 문장은 B를 A로 표시 한다고 해석했으나 그런식으로 해석할 경우에는 해석이 불가능한 복잡한 문장들을 접할 수 있다. 그렇기 때문에
typedef A B;
가 존재 할때에는 B는 변수명이고 A를 B의 타입이라고 생각하고, 변수 B를 이제부터 자료형(type)으로 취급한다고 보면 된다.
예를 들면 배열이나 함수도 typedef로 선언이 가능한다.
그러나 시각상 배열이나 함수는 후위우선에 의해 변수 뒤에서 변수를 꾸며주기 때문에 A B로 해석되지 않는다.
즉 typedef 다음에 자료형과 변수명이 선언되어 있다면, 변수명은 앞으로 그 변수의 탑으로 쓰인다고 해석해야한다. 즉 더이상 변수가 아닌 자료형으로 사용된다는 것이다.
 
쉽게 기억할려면 [typedef 변수명]으로 생각하고, 변수명은 이제부터 선언된 자료형으로 사용된다고 보면 된다.
 
다음코드를 통해 사용예를 보자
코드중에는 함수를 인자로 받고 함수를 return 하는 함수를 예로 보이기 위해 조금 ㅋㅋ드가 지저분해졋고, 배열간 포인터 연결때문에 한번에 읽기도 난해한 감이 있다.
#include <stdio.h>
 
typedef int(*pf_Return_Int)();          // int형을 리턴하고 인자가 없는 함수의 포인터
typedef void(*pf)();                    // 리턴이 없고 인자가 없는 함수의 포인터
typedef int(*ary_2)[3];                 // 2차원 배열
typedef int ary_1[3];                   // 1차원 배열
typedef int *iP;                        // 정수의 포인터
 
int returnInt(){
        return 2;
}
void aaa(){
        printf("aaa\n");
}
void bbb(){
        printf("bbb\n");
}
void ccc(){
        printf("ccc\n");
}
pf funcTable[3] = { aaa, bbb, ccc };

// int를 반환하고 인자가 없는 함수포인터를 인자로 받는다.
pf func(pf_Return_Int arg){    
        return funcTable[arg()];
}
 
void main()
{
        int temp = 5;
        iP val1 = &temp;         // 값이 5인 정수변수의 주소를 포인터에 담는다.
 
        int temp2[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        ary_2 val2 = temp2;      // 2차원배열을 선언하고 2차원 배열의 포인터에 담는다.
 
        ary_1 temp3[3];          // 1차원배열의 변수를 3개 만든다. ( 2차원 배열을 만든거임 )
        iP temp4 = temp2[0];     //temp2의 2차원 배열중 첫번째 행을 가리키는 포인터
 
        printf("%d\n", *(temp4 + 2)); // 첫번째 행 {1,2,3} 중 temp4는 첫번째 방을 
                                      //가리키고 있을것이므로 2칸 이동해 3이 출력됨
       
        // returnInt의 함수주소를 넘겨 funcTable의 2번째 인덱스의 함수를 호출한다.
        func(returnInt)();  
}
3-7 Line의 typedef 구문에서 3,4 Line은 함수포인터 5,6 Line은 2차원배열과 1차원 배열 7 Line은 정수형  포인터를 선언하였다.
그리고 aaa,bbb,ccc 의 보통 함수를 선언하였다.
21 Line은 함수포인터로 함수의 주소를 저장되어 있는 3칸의 배열이다. 나중에 함수를 가져오기위한 함수테이블로 이용될것이다.
24 Line에서 "int형을 반환하고 인자가 없는 함수의 포인터(pf_Return_Int)" 를 인자 로 받아 funcTable의 함수를 리턴하는 함수이다.
 
이제 메인에서는 각 값들의 연결을 볼수 있다. 이를 이해하기 위해서는 좌측의 L-value와 우측의 R-value를 잘 비교해서 보아야 한다.
30~31 Line은 우리가 흔히 아는 정수형 포인터의 연결을 보여준다.
33 Line에서 3차원 배열 하나를 선언하고 34 Line은 2차원 배열로 typedef 선언된 ary_2로 선언된 val2에 기존에 메모리에 할당된 9개 요소를 갖고 있는 2차원 배열을 연결해주는 모습니다.
(연결한다는 의미는 첫번째 주소값3을 넘겨주어 offset(++, +1...)으로 접근이 가능하도록 한다는 말이다. )
36 Line에서는 3칸짜리 temp3배열 내부에서 ary_1(한칸짜리 배열)을 넣어준것이다.
즉 여기서 메모리는 2차원 배열처럼 배열요소 내부에 배열이 있는 구조가 된다.
이 상태는 temp3이라는 이름으로 int형 9칸의 공간이 메모리에 할당된 구조일것이다.
그냥 포인터가 아닌 메모리를 영역이 할당된것이다.
37 Line에서는 배열의 포인터로 사용된 iP변수 temp4에 temp2(2차원배열)의 [0]번째 요소 ( 즉 2차원 배열의 첫번째 행)를 전해준다.
39 Line 출력 구문에서는 포인터로 연결된 temp4에서 temp2 내용으로의 접근을 확인한다.
 
마지막 43Line은 func 라는 함수에서 returnInt함수를 인자로 넣고, 함수테이블(funcTable)에 존재하는 값을 리턴하여 바로 사용하는 예를 보였다.
 
 
결과 :

 

>3
>ccc

 

관련글 더보기