CPP Info Memo Part 3

| 分类 C-Family  | 标签 c  preprocessor 

这是 Cpp Info Memo 第三部分,前两部分在:

3 Macros

宏 (Macro) 实际上是一段被被命名的代码段。从使用的方式上来分,宏可以分为两种:类对象宏 (object-like macros) 和类函数宏 (function-like macros) 。预编译器并不了解 C 语言中的关键词,因此除了预编译器的操作符 defined 和 C++ 中的命名操作符 (named operators) 之外,所有其他合法的 identifier 都可以被定义成宏。

3.1 类对象宏

object-like macros 是一个简单的标记,它将会在预编译器被真正的代码片段所取代,最常用的方式,就是用来定义一些常量,例如:

#define BUFFER_SIZE       1024

上面的例子中定义了一个 token BUFFER_SIZE ,其值是 1024,其中:

  • #define 是预编译器的指令,用了声明宏定义
  • #BUFFER_SIZE 是宏定义的名字
  • #1024 名字之后的部分是宏定义的展开

使用示例:

// Before preprocess
foo = (char *) malloc (BUFFER_SIZE);
// After preprocess
foo = (char *) malloc (1024);

3.1.1 Note:

  • By convention, macro names are written in UPPERCASE.
  • 宏定义结束在 #define 所在的行
    我们可以用 "\" 来折行,但预编译器处理过后的代码中不会折行,所有代码都会成为一行,例如:
    #define NUMBERS 1,                              \
            2,                                      \
            3
    int x[] = { NUMBERS };
    // After preprocess...
    int x[] = { 1, 2, 3 };
    
  • C Preprocessor 按顺序扫描代码,并只在被定义之后才生效
    foo = X;
    #define X 4
    bar = X;
    
    // produces ...
    
    foo = X;
    bar = 4;
    
  • 如果宏定义中包含另外一个宏,会逐级展开

    当预编译器展开一个宏定义后,如果发现其中还有另外一个宏,会再次处理,如此反复直到全部处理完毕。 例如:

    #define TABLESIZE BUFSIZE
    #define BUFSIZE 1024
    TABLESIZE
    //     ==> BUFSIZE  // Expand to TABLESIZE first
    //     ==> 1024     // Expand to 1024 finally
    

    需要注意的是,如果宏定义中引用了它自己的名字,该定义不会被再次展开,以防止无限递归。

3.2 Function-like Macros

function-like macros also starts with `#define' directive, but continued immediately with a pair of parentheses after the macro name.

#define lang_init()  c_init()
lang_init() //
     ==> c_init()

A function-like macro is only expanded if its name appears with a pair of parentheses after it. If you write just the name, it is left alone.

NOTE:
If you put spaces between the macro name and the parentheses in the macro definition, that does not define a function-like macro, it defines an object-like macro whose expansion happens to begin with a pair of parentheses:

#define lang_init ()    c_init()
lang_init() // after expanding:
     ==> () c_init()()

3.3 Macro Arguments

Function-like macros can take "arguments", just like true functions. To define a macro that uses arguments, you insert "parameters" between the pair of parentheses in the macro definition that make the macro function-like. The parameters must be valid C identifiers, separated by commas and optionally whitespace.

#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
  x = min(a, b);          ==>  x = ((a) < (b) ? (a) : (b));
  y = min(1, 2);          ==>  y = ((1) < (2) ? (1) : (2));
  z = min(a + 28, *p);    ==>  z = ((a + 28) < (*p) ? (a + 28) : (*p));

Things to note:

  • Space and comma

Leading and trailing whitespace in each argument is dropped, and all whitespace between the tokens of an argument is reduced to a single space. Parentheses within each argument must balance; a comma within such parentheses does not end the argument. However, there is no requirement for square brackets or braces to balance, and they do not prevent a comma from separating arguments. Thus,

macro (array[x = y, x + 1])

passes two arguments to `macro': `array[x = y' and `x + 1]'. If you want to supply `array[x = y, x + 1]' as an argument, you can write it as `array[(x = y, x + 1)]', which is equivalent C code.

  • Empty arguments

    You can leave macro arguments empty; this is not an error to the

preprocessor (but many macros will then expand to invalid code). You cannot leave out arguments entirely; if a macro takes two arguments, there must be exactly one comma at the top level of its argument list. Here are some silly examples using `min':

min(, b)        ==> ((   ) < (b) ? (   ) : (b))
min(a, )        ==> ((a  ) < ( ) ? (a  ) : ( ))
min(,)          ==> ((   ) < ( ) ? (   ) : ( ))
min((,),)       ==> (((,)) < ( ) ? ((,)) : ( ))

min()      // error--> macro "min" requires 2 arguments, but only 1 given
min(,,)    // error--> macro "min" passed 3 arguments, but takes just 2

Whitespace is not a preprocessing token, so if a macro `foo' takes one argument, `foo()' and `foo( )' both supply it an empty argument.

  • Strings in macro

    Macro parameters appearing inside string literals are not replaced by

their corresponding actual arguments.

#define foo(x) x, "x"
foo(bar)        ==> bar, "x"

3.4 Stringification

'#' 用来将宏参数子串化。

3.5 Concatenation

'##' 用来连接 Token: When a macro is expanded, the two tokens on either side of each `##' operator are combined into a single token, which then replaces the `##' and the two original tokens in the macro expansion.

Keep in mind that the C preprocessor converts comments to whitespace before macros are even considered. Therefore, you cannot create a comment by concatenating `/' and `*'. You can put as much whitespace between `##' and its operands as you like, including comments, and you can put comments in arguments that will be concatenated. However, it is an error if `##' appears at either end of a macro body.

Consider a C program that interprets named commands. There probably needs to be a table of commands, perhaps an array of structures declared as follows:

struct command
{
  char *name;
  void (*function) (void);
};

struct command commands[] =
{
  { "quit", quit_command },
  { "help", help_command },
  ...
};

It would be cleaner not to have to give each command name twice, once in the string constant and once in the function name. A macro which takes the name of a command as an argument can make this unnecessary. The string constant can be created with stringification, and the function name by concatenating the argument with `_command'. Here is how it is done:

#define COMMAND(NAME)  { #NAME, NAME ## _command }

struct command commands[] =
{
  COMMAND (quit),
  COMMAND (help),
  ...
};

3.6 Variadic Macros

A macro can be declared to accept a variable number of arguments much as a function can. The syntax for defining the macro is similar to that of a function. Here is an example:

#define eprintf(...) fprintf (stderr, __VA_ARGS__)

This kind of macro is called "variadic". When the macro is invoked, all the tokens in its argument list after the last named argument (this macro has none), including any commas, become the "variable argument". This sequence of tokens replaces the identifier `__VA_ARGS__' in the macro body wherever it appears. Thus, we have this expansion:

eprintf ("%s:%d: ", input_file, lineno)
     ==>  fprintf (stderr, "%s:%d: ", input_file, lineno)

测试了几个预编译器,包括 GNU/Clang/VS ,他们对 '##' 都加了和 CPP 一样的特殊处理:

the `##' token paste operator has a special meaning when placed between a comma and a variable argument. If you write

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

and the variable argument is left out when the `eprintf' macro is used, then the comma before the `##' will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding `##' is anything other than a comma.

eprintf ("success!\n")
     ==> fprintf(stderr, "success!\n");

上一篇     下一篇