Macro의 사용 방법
이 부분은 C의 테크닉과 관련된 부분에 해당합니다.
C에서 헤더 파일을 짜는 경우에는 아래와 같이 작성해주시길 바랍니다.
#ifndef MYMACRO_H_
#define MYMACRO_H_
#include <stdio.h>
#endif
이러한 것을 헤더 가드header guards라고 이야기 합니다. 이러한 헤더 가드는 헤더 파일이 중복되어 들어가는 것을 막아주는 역할을 합니다. 그리고 이러한 매크로는 #
이라는 전처리 지시자를 사용하도록 합니다. 전처리 중에 #define
에 관해서 좀 더 달아보도록 하겠습니다.
#define
매크로
#include <stdio.h>
#define HELLO "hello world!"
int main() {
fputs(HELLO, stdout);
return 0;
}
이러한 코드가 있는 경우 HELLO 라는 값은 “hello world!”로 치환되라는 것을 #define
이 지시합니다. 이는 gcc -E
를 통해서 전처리 후에 코드를 확인을 할 수 있습니다. 이를 확인하면 아래와 같이 나오는 것을 알 수 있습니다.
<참고>
프로세스는 파일 디스크립터file descriptor라는 것을 기본적으로 가지고 파일을 열때마다 추가적으로 그 할당을 받게 됩니다. 가장 잘 알려진 파일 디스크립터로 0: 표준 입력(
stdin
), 1: 표준 출력(stdout
), 2: 표준 오류(stderr
)가 있습니다.근데 왜 입출력이 파일 디스크립터로 표현이 되는가? 그것은 유닉스 체계에서는 장치는 파일로 취급되기 때문입니다. 다시 말해 키보드, 마우스, 심지어 모니터까지 파일로 취급되어 다루어지게 됩니다.
이러한 표준 입출력을 다른 곳으로 변경을 하고 싶은 경우 유닉스의 대부분 셸은
<
,>
,&
와 같은 특별한 기호를 지원을 합니다. 이를 리다이렉션redirection이라고 합니다.<
는 우측에 있는 것이 표준 출력이고, 좌측이 그 표준 출력을 표준 입력으로 받는 것에 해당합니다.>
은 반대에 해당합니다.근데 이 방식은 표준 오류를 출력하지 못하는 경우가 있습니다. 그러한 경우에는
&
를 붙여서>&
와 같이 사용하면 표준 오류도 나오게 됩니다.
797 # 5 "sample.c"
798 int main(void){
800 fputs("hello world!",
801 # 7 "sample.c" 3 4
802 stdout
803 # 7 "sample.c"
804 );
806 }
HELLO
라고 위에서 적은 내용이 아래에서는 “hello world”로 변경이 되게 됩니다.
#if
매크로
#if ~ #endif
는 그 부분에 해당하는 부분이 #if
다음에 나오는 내용을 만족하는 경우 실행을 하라는 것을 의미합니다. 이를 테면, 아래와 같은 코드가 존재한다 하겠습니다.
#include <stdio.h>
int main(void) {
#if A
printf("A is defined [1]\n");
#else
printf("A is not defined [2]\n");
#endif
}
이 경우에 gcc -E sample.c
를 해주게 되면 다음과 같이 나오는 것을 알 수 있습니다.
796 # 4 "sample.c"
797 int main(void){
798
799
800
801 printf("A is not defined [2]\n");
802
803 }
결과적으로, 출력이 A is not defined [2]
가 나오는 것을 알 수 있습니다. 이제는 그 위의 A is defined [1]
을 출력해보도록 하겠습니다. 이를 출력하고자하는 경우에는 gcc -E -D A sample.c > result.txt
를 해주도록 합니다. 여기서 -D
는 Define을 의미하고, 뒤에 오는 A
는 문자열 A
를 지칭합니다. 결과적으로, 아래와 같이 나오는 것을 알 수 있었으며, A is defined [1]
이 뜨는 것을 알 수 있었습니다.
795 # 4 "sample.c"
796 int main(void){
797
798 printf("A is defined [1]\n");
799
800
801
802 }
물론 늘 문자열만 넣을 필요가 없이 숫자를 넣어도 됩니다 #if 0
라고 하는 경우에는 해당 구역은 절대로 컴파일 대상에 포함이 되지 않게 됩니다. 하지만 만약 #if 1
이라고 하면 해당 구역은 컴파일 대상으로 포함되게 됩니다.
이 이외에도 여러 개가 define
의 설정을 확인하고 싶은 경우 #if defined(A) || defined(B)
와 같이 사용하고, 좀 더 확장되게 사용하고 싶은 경우에 사용하는 미리 정의된 것들도 존재합니다.
좀 더 자세히 알고 싶으시면 https://gcc.gnu.org/onlinedocs/cpp/Macros.html 링크를 확인해주시길 바랍니다. 대표적인 매크로 몇 개만 확인을 해보면 현재 표준 C 컴파일러인지를 확인하는 __STDC__
가 있고, 현재 파일의 라인 수가 얼마만큼 있는 지는 __LINE__
을 통해서 확인 가능합니다.
그리고 여기서 알 수 있듯이 미리 지정된 형식은 __(sample)__
의 형태를 가지기 때문에 헤더 가드를 작성을 할 때에는 __(sample)__
식으로 작성하지 말아주시길 바랍니다.
이러한 방식을 잘 활용하면 좀 더 보편적인 코드를 만들 수 있습니다.