typedef BOOL int;
typedef int BOOL;

둘 중 어느 것이 맞는지 단박에 알아차릴 수 있겠는가?
나는 typedef만 쓰려고 하면 지금도 둘 중에 뭐가 맞는지 헷갈리고는 한다.
답은 아래 것이 맞다.
그럼 앞에 있는 타입으로부터 뒤에 따라오는 새로운 타입을 만들겠다는 말인가?

아래 정의들을 보자.

typedef int BOOL, *PBOOL;

typedef struct tagFILEINFO
{
  int i;
} FILEINFO, *PFILEINFO;

이제 또 어디부터가 앞이고 뒤인지 헷갈린다.

typedef BOOL (*fn_t)(int, int*);

함수의 경우에는 조금 더 머리가 아프다.
빌어먹을, 대체 어디가 앞이고 어디부터 뒤란 말인가?

typedef을 정의할 때는 이를 헷갈리지 않기 위해서 딱 한 가지만 기억하면 된다.

변수를 적어야 할 위치에 새로운 타입을 적어라.


위에 나왔던 typedef 들을 하나씩 살펴보겠다.
빨간색은 타입이요, 파란색은 변수이다.

int형 변수를 선언할 때는 다음처럼 한다.

int i;

아래처럼 한 줄에 포인터 변수와 같이 선언할 수도 있다.

int j, *p;

이제 typedef를 다시 보면

// 변수를 적어야 할 위치에 새로운 타입을 적는다.
typedef int BOOL;
typedef int BOOL, *PBOOL;


구조체를 정의함과 동시에 변수를 만들 수 있다는 것도 알고 있을 것이다.

struct FILEINFO
{
  int i;
} fileInfo;
// 구조체를 선언함과 동시에 전역 공간에 fileInfo 라는 인스턴스를 생성하였다.

물론 아래처럼 여러 변수를 만들 수도 있다.

struct FILEINFO
{
  int i;
} fileInfo, *pFileInfo, ***pppFileInfo;

이제 typedef를 다시 보면

// 변수를 적어야 할 위치에 새로운 타입을 적는다.
typedef struct tagFILEINFO
{
  int i;
} FILEINFO, *PFILEINFO, ***PPPFILEINFO;


아래 함수를 나타내는 타입은 무엇일까?

BOOL foo(int i, int* p);

foo는 함수 이름이고 타입은 다음과 같다.

BOOL (*)(int, int*)

타입이 있으므로 변수도 만들 수 있다.
그런데 함수의 경우에는 변수가 뒤쪽에 붙는 것이 아니라 가운데에 들어가는 것을 이해하는 것이 중요하다.
BOOL(*)(int, int*) 이라는 타입의 변수 pfn을 선언하려면 다음과 같이 한다.

BOOL (*pfn)(int, int*);

함수 포인터를 사용하는 예제도 한 번 살펴보고 넘어가자.

void foo(int x)
{
  printf("%d", x);
}

int main()
{
  // void (*)(int) 타입의 변수 pfn을 정의하면서 동시에 foo를 대입한다.
  void (*pfn)(int) = foo; 
  pfn(10); // 함수 포인터로 함수 호출도 가능하다.
}

이제 typedef를 다시 보면,

// 변수를 적어야 할 위치에 새로운 타입을 적어라.
typedef BOOL (*fn_t)(int, int*); // fn_t라는 새로운 타입을 정의하였다.

함수에 호출 규약까지 넣는 경우에는 아래처럼 꼭 괄호 안에 호출 규약을 넣어 주어야 한다.

typedef BOOL (__stdcall *fn_t)(int, int*);


멤버 함수의 경우에는 타입을 다음처럼 쓴다.

void (MyClass::*)(int, int*);

위에서 설명한 규칙을 잘 기억했다면 이제 typedef을 쉽게 만들어 낼 수 있다.

// 변수를 적어야 할 위치에 새로운 타입을 적어라.
typedef void (MyClass::* memberfn_t)(int, int*);


함께 읽으면 좋은 글: