매크로에서 # 와 ## 의 차이

# : 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 파일을 보면 됩니다.

You may also like...