본문 바로가기

Core BSP 분석/리눅스 커널 핵심 분석

GCC - C언어 매크로(Macro) -(1)

C Macro를 잘 알아두면 편리합니다. Register 설정을 Macro로 관리하면 엄청 편하거든요. 주소를 다 외울 수는 없으니까요. 그리고 일단은 Macro도 argument를 받을 수 있어요. 이런 Macro를 선언해서 쓰는 방법도 여러 가지가 있습니다. Macro는 자주 쓰이는 코드 묶음을 한 개로 쓸 수 있다는 장점이 있어요. 예를 들어서 코드를 짜다 보니까, 
critical_section_in();
ret = io_read ();
critical_section_out();
 
이라는 부분이 반복되면 계속 위 코드를 입력하기 귀찮겠죠? 이 코드들을 아름답게 하나의 Macro로 만들 수 있습니다. 그러면 #define으로 CRITICAL_IO_IN() 을 선언하는데, 반복되는 부분을 역슬래쉬 "\" 으로 계속 연결하면 CRITICAL_IO_IN() 하나만 사용해도 나머지가 3줄의 코드를 입력한 것과 마찬가지 효과입니다. 
#define CRITICAL_IO_IN() \
critical_section_in(); \
ret = io_read (); \
critical_section_out();
 
라고 선언하면, 코드에서 CRITICAL_IO_IN(); 이라고만 넣어줘도 3줄을 한꺼번에 컴파일러가 붙여 줍니다.
 
두 번째로, 이런 Macro도 인자를 받을 수 있어요. 함수들의 인자가 계속 달라져도 Macro로 만들 수 있습니다. 
예를 들어서,
critical_section_in(current);
ret = io_read (io_num);
critical_section_out(previous);
 
이런 패턴의 코드가 반복되면 귀찮겠죠. 이럴 때는,
#define CRITICAL_IO_IN (current, io_num, previous) \
critical_section_in(current); \
ret = io_read (io_num); \
critical_section_out(previous); \
 
이런 식으로 선언해 주면 마치 함수 쓰는 것처럼 Macro 내부의 인자들에게 하나씩 전달합니다. 예를 들어,
CRITICAL_IO_IN (1, 2, 3);
 
이라고 호출해 주면,
critical_section_in(1); 
ret = io_read (2); 
critical_section_out(3); 
 
결과적으로는 이런 식으로 만들어 줍니다. 여기에 편한 Macro의 기능 하나를 더 소개하면 놀라움을 금치 못하실 겁니다. 그것은 ##기능인데요. ##을 이용하면, Define으로 선언된 이름들도 Argument로 넣어줄 수 있습니다. 
예를 들어서, 
#define CRITICAL_IO_IN (CURRENT, IO, PREVIOUS) \
critical_section_in(DEVICE_##CURRENT##_BUFFER); \
ret = io_read (IO_##IO##_NUM); \
critical_section_out(DEVICE_##PREVIOUS##_BUFFER);
 
라고 선언해 둔 후, 
CRITICAL_IO_IN (BOOTUP, USB, STARTUP);
 
이라고 호출해 주면  어떤 식으로 호출이 되냐면, 
critical_section_in(DEVICE_BOOTUP_BUFFER);
ret = io_read (IO_USB_NUM);
critical_section_out(DEVICE_STARTUP_BUFFER);
 
이렇게 바꿔줍니다. 위 코드에서 볼드체가 아규먼트가 치환된 코드입니다.