我们可以在很多行中编写宏而不用反斜杠吗?

时间:2017-01-18 02:46:01

标签: c gcc c-preprocessor

我在CPP手册中看到了一些示例,我们可以在没有反斜杠的情况下在多行中编写宏体。

 #define strange(file) fprintf (file, "%s %d",
 ...
 strange(stderr) p, 35)

输出:

fprintf (stderr, "%s %d", p, 35)

它们是特殊情况,如参数宏中的指令还是只允许#define?

对于include指令如果我没有错,它必须总是在一行上声明。

修改

来自https://gcc.gnu.org/onlinedocs/cpp/Directives-Within-Macro-Arguments.html

  

3.9宏观参数中的指令

     

偶尔在其中使用预处理程序指令很方便   宏的参数。 C和C ++标准声明了这一点   这些情况下的行为是不确定的。 GNU CPP处理是任意的   宏参数中的指令与它完全相同   已处理的指令是类似函数的宏调用   不存在。

     

如果在宏调用中,重新定义了该宏,那么新的   定义在参数预扩展时及时生效,但是   原始定义仍用于参数替换。这里有一个   病理学例子:

 #define f(x) x x
 f (1
 #undef f
 #define f 2
 f)
     

扩展为

 1 2 1 2
     

使用上述语义。

这个例子有很多行。

3 个答案:

答案 0 :(得分:3)

没有反斜杠换行的多行宏定义

由于在翻译阶段3中注释被替换为空格:

  
      
  1. 源文件被分解为预处理标记 7)和序列   空格字符(包括注释)。源文件不得以a结尾   部分预处理令牌或部分注释。每条评论都被替换为   一个空格字符。保留换行符。是否每个都是空的   新行以外的空白字符序列被保留或替换为   一个空格字符是实现定义的。
  2.   

并且预处理器作为阶段4运行:

  
      
  1. 执行预处理指令,扩展宏调用,以及   执行_Pragma一元运算符表达式。如果是一个字符序列那个   匹配通用字符名称的语法由token生成   连接(6.10.3.3),行为未定义。 #include预处理。#include <stdio.h> #define possible_but_absurd(a, b) /* comments */ printf("are translated"); /* in phase 3 */ printf(" before phase %d", a); /* (the preprocessor) */ printf(" is run (%s)\n", b); /* but why abuse the system? */ int main(void) { printf("%s %s", "Macros can be continued without backslashes", "because comments\n"); possible_but_absurd(4, "ISO/IEC 9899:2011,\nSection 5.1.1.2" " Translation phases"); return 0; }   指令导致从阶段1处理命名的头文件或源文件。   通过阶段4,递归。然后删除所有预处理指令。
  2.   

编写像这样的多行宏是可能的,但是很荒谬:

Macros can be continued without backslashes because comments
are translated before phase 4 is run (ISO/IEC 9899:2011,
Section 5.1.1.2 Translation phases)

,在运行时,声明:

??/

宏定义中的反斜杠 - 换行符

翻译阶段1和2也有些相关:

  
      
  1. 物理源文件多字节字符在实现定义中映射   方式,源字符集(引入换行符)   如有必要,可以使用终端指标。 Trigraph序列被替换为   相应的单字符内部表示。
  2.   

三字符替换在名义上是相关的,因为\是反斜杠的三字符。

  
      
  1. 反斜杠字符(#undef)的每个实例后面紧跟一个换行符   删除字符,拼接物理源行以形成逻辑源行。   只有任何物理源代码行的最后反斜杠才有资格成为其中一部分   这种拼接。非空的源文件应以换行符结尾,   在任何此类之前不得立即使用反斜杠字符   拼接发生。
  2.   

这告诉您,在第4阶段(预处理器)运行时,宏定义位于单个(逻辑)行上 - 尾部反斜杠 - 换行符组合已被删除。

该标准指出,这些阶段似乎是&#39; - 编译器的行为必须好像经历了不同的阶段,但许多实现并没有正式将它们完全分开。

避免使用GCC扩展名

扩展示例(引自GCC手册)的调用遍布多行,但定义严格在一行。 (这不是GCC扩展,而是完全标准的行为。)

请注意,如果您远程理智,您将忽略在宏的调用中放置预处理指令的可能性(示例中为#define<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> )。它是GCC扩展,完全不可移植。该标准表明行为未定义。

  

附件J.2未定义的行为

     
      
  • 在宏参数列表中有一系列预处理令牌,否则它们将充当预处理指令(6.10.3)。
  •   

答案 1 :(得分:1)

不,这是不可能的。在每行之前处理行拼接作为代码文本行或预处理指令,并且在预处理器中,步骤编号4(有效预处理算法)不会到达任何行拼接,因为它们之前已被删除。

#define strange(file) fprintf (file, "%s %d",

您的定义被解释为一个函数宏,它使预处理器将strange(stderr)替换为fprintf (stderr, "%s %d",。预处理器不了解C语法和语义,它是另一种语言,非turing-complete。

答案 2 :(得分:0)

没有。你不可以。您必须使用反斜杠作为多行预处理器宏的行继续转义字符。