매크로에서 # 와 ## 의 차이
# : stringification
기본적으로 컴파일러에서 처리하는 문자열들은 token으로 보는데, 이 token들을 string constant로 처리하고자 할 때 사용합니다. 즉, 아래 예제에서처럼 val 이라는 token 을 "val" 문자 자체로 사용하게게 됩니다.
사용 시 한 가지 주의할 점은, 이 경우 argument를 macro-expand 하지 않고 처리한다는 것입니다.
즉, # 을 사용하게 되면 macro-expand 하지 않아서 그냥 넘겨받은 값을 string 으로 사용하지만, 아래의 예제에서 xstr 처럼 직접 # 을 사용하지 않고 한단계를 더 거치도록 하면 macro-expand 되어 val 을 일단 11 값으로 대체하여 str 로 전달하게 되고, str 매크로에는 # 이 있으므로 macro-expand 를 더 이상 할 수 없고 그 대로 11 이라는 token 을 곧바로 string 으로 처리하게 됩니다.
#define val 11 #define str(s) #s #define xstr(s) str(s) str (val) ==> "val" xstr(val) ==> xstr(11) ==> str(11) ==> "11"
## : token concatenation
token 두 개를 붙이는 데에 사용합니다. 이것은 string 두 개를 붙이는 것과는 다릅니다.
C 언어에서 string constant 두 개를 붙이려면 단순히 "foo" "bar" 두 개를 연속하게 배치하면 그냥 붙지만, token을 붙이는 데에는 ##을 사용해야 합니다. 사용 예를 아래에서 볼 수 있습니다.
struct command { char *name; void (*function) (void); }; struct command commands[] = { { "quit", quit_command }, { "help", help_command }, ... };
위와 같이 짜야하는 코드를 아래와 같이 심플하게 바꿀 수 있습니다.
#define COMMAND(NAME) { #NAME, NAME ## _command } struct command commands[] = { COMMAND (quit), COMMAND (help), ... };
참고로, 전처리 후의 결과 코드를 보고 싶을 때는 gcc -E 옵션을 주면 됩니다. 그러면, stdout 으로 결과를 뿌려줍니다.
커널 코드의 전처리 결과 코드를 보고 싶을 때는 make ARCH=arm init/main.i 를 실행하여 main.i 파일을 보면 됩니다.