简体   繁体   English

为什么这些连续的宏替换不会导致错误?

[英]Why these consecutive macro replacements do not result in an error?

This program gives output as 5. But after replacing all macros, it would result in --5 . 该程序将输出设为5.但是在替换所有宏之后,它将导致--5 This should cause an compilation error, trying to decrement the 5 . 这应该导致编译错误,尝试减少5 But it compiles and runs fine. 但它编译并运行良好。

#include <stdio.h>
#define A -B
#define B -C
#define C 5

int main()
{
    printf("The value of A is %d\n", A); 
    return 0;
} 

Why is there no error? 为什么没有错误?

Here are the steps for the compilation of the statement printf("The value of A is %d\\n", A); 以下是汇编语句printf("The value of A is %d\\n", A);的步骤printf("The value of A is %d\\n", A); :

  • the lexical parser produces the preprocessing tokens printf , ( , "The value of A is %dn" , , , A , ) and ; 词法分析器产生预处理标记printf("The value of A is %dn", A); .
  • A is a macro that expands to the 2 tokens - and B . A是一个扩展为2个令牌的宏-B
  • B is also a macro and gets expanded to - and C . B也是一个宏,并扩展到-C
  • C is again a macro and gets expanded to 5 . C又是一个宏,并扩展到5
  • the tokens are then converted to C tokens, producing errors for preprocessing tokens that do not convert to proper C tokens (ex: 0a ). 然后将令牌转换为C令牌,从而产生预处理令牌的错误,这些令牌不会转换为适当的C令牌(例如: 0a )。 In this example, the tokens are identical. 在此示例中,令牌是相同的。
  • the compiler parses the resulting sequence according to the C grammar: printf , ( , "The value of A is %d\\n" , , , - , - , 5 , ) , ; 编译器根据到C语法解析所产生的序列: printf("The value of A is %d\\n",- - 5); matches a function call to printf with 2 arguments: a format string and a constant expression - - 5 , which evaluates to 5 at compile time. 匹配对printf的函数调用,带有2个参数:格式字符串和常量表达式- - 5 ,在编译时计算结果为5
  • the code is therefore equivalent to printf("The value of A is %d\\n", 5); 因此代码等同于printf("The value of A is %d\\n", 5); . It will produce the output: 它会产生输出:

     The value of A is 5 

This sequence of macros is expanded as tokens, not strictly a sequence of characters, hence A does not expand as --5 , but rather as - -5 . 这个宏序列扩展为标记,而不是严格的字符序列,因此A不会扩展为--5 ,而是扩展为- -5 Good C compilers would insert an extra space when preprocessing the source to textual output to ensure the resulting text produces the same sequence of tokens when reparsed. 好的C编译器在将源预处理到文本输出时会插入额外的空间,以确保生成的文本在重新分析时产生相同的标记序列。 Note however that the C Standard does not say anything about preprocessing to textual output, it only specifies preprocessing as one of the parsing phases and it is a quality of implementation issue for compilers to not introduce potential side effects when preprocessing to textual output. 但请注意,C标准没有说明文本输出的预处理,它只指定预处理作为解析阶段之一,并且编译器在预处理到文本输出时不会引入潜在的副作用,这是一个实现质量问题。

There is a separate feature for combining tokens into new tokens in the preprocessor called token pasting. 有一个单独的功能可以将令牌组合到预处理器中称为令牌粘贴的新令牌中。 It requires a specific operator ## and is quite tricky to use. 它需要一个特定的操作符##并且使用起来非常棘手。

Note also that macros should be defined with parentheses around each argument and parentheses around the whole expansion to prevent operator precedence issues: 另请注意,应在每个参数周围使用括号定义宏,并在整个扩展周围使用括号以防止运算符优先级问题:

#define A  (-B)
#define B  (-C)
#define C  5

Two consecutive dashes are not combined into a single pre-decrement operator -- because C preprocessor works with individual tokens, effectively inserting whitespace around macro substitutions. 两个连续的破折号不会组合成一个预减量运算符--因为C预处理器可以处理单个标记,有效地在宏替换周围插入空格。 Running this program through gcc -E 通过gcc -E运行该程序

#define A -B
#define B -C
#define C 5

int main() {
    return A;
}

produces the following output: 产生以下输出:

int main() {
    return - -5;
}

Note the space after the first - . 注意第一个之后的空间-

According to the standard, macro replacements are performed at the level of preprocessor tokens, not at the level of individual characters (6.10.3.9): 根据标准,宏替换是在预处理器令牌级别执行的,而不是在单个字符级别(6.10.3.9)执行:

A preprocessing directive of the form 表单的预处理指令

 # define identifier replacement-list new-line 

defines an object-like macro that causes each subsequent instance of the macro name to be replaced by the replacement list of preprocessing tokens that constitute the remainder of the directive. 定义一个类似对象的宏,它使宏名称的每个后续实例被构成指令其余部分的预处理标记的替换列表替换。

Therefore, the two dashes - constitute two different tokens, so they are kept separate from each other in the output of the preprocessor. 因此,两个破折号-构成两个不同的标记,因此它们在预处理器的输出中保持彼此分离。

Whenever we use #include in C program, compiler will replace the variable with its value wherever it is used. 每当我们在C程序中使用#include时,编译器将使用其值在任何地方替换变量。

#define A -B
#define B -C
#define C 5

So when we print A , it will execute in following steps. 因此,当我们打印A时,它将按以下步骤执行。

A=>-B A => - 乙

B=>-C B => - Visual C

A=>-(-C)=>C A => - ( - C)=> C ^

So when we print value of A, it comes out to be 5. 因此,当我们打印A的值时,它出来是5。

Generally these #define statements are used to declare value of constants that are to be used through out the code. 通常,这些#define语句用于声明在代码中使用的常量值。

For more info see this link on #define directive 有关更多信息,请参阅#define指令上的此链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM