CPP Info Memo Part 3
Table of Contents
这是 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");
3.7 Source file.
(转载请注明出处, 使用许可: 署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议 。)
2023年1月13日 21:49
hello there and thank you for your info – I have certainly picked up something new from right here. I did however expertise several technical issues using this site, as I experienced to reload the web site lots of times previous to I could get it to load properly. I had been wondering if your web hosting is OK? Not that I’m complaining, but sluggish loading instances times will very frequently affect your placement in google and could damage your quality score if advertising and marketing with Adwords. Well I am adding this RSS to my e-mail and can look out for much more of your respective exciting content. Make sure you update this again soon.. 바카라사이트
2023年2月08日 03:02
It?? hard to find experienced people about this topic, however, you seem like you know what you??e discussing! Thanks SaaS Law Firm
2023年2月12日 03:22
magnificent put up, very informative. I ponder why the other specialists of this sector don’t realize this. You should proceed your writing. I am confident, you have a great readers’ base already! Macbook Pro
================================
I would really like you to become a guest poster on my blog. 여우알바
2023年2月22日 02:03
Woh I like your blog posts, bookmarked ! . 여우알바