'#' - 字符必须位于C预处理器中的一行的开头吗?

wef*_*fa3 9 c syntax conditional-compilation language-lawyer c-preprocessor

我已经编程了C很长一段时间了.在这段时间里,我了解到在第一列放置预处理程序指令之前的"#"字符是一种常见的约定.

例:

 #include <stdio.h>

 int main(void) {
 #ifdef MACRO1
 #ifdef MACRO2
      puts("defined(MACRO1) && defined(MACRO2)");
 #else
      puts("defined(MACRO1)");
 #endif
 #else
      puts("!defined(MACRO1)");
 #endif
      return 0;
 }
Run Code Online (Sandbox Code Playgroud)

当人们缩进他们的预处理器指令时,他们通常会这样做:

 #include <stdio.h>

 int main(void) {
 #ifdef MACRO1
 # ifdef MACRO2
     puts("defined(MACRO1) && defined(MACRO2)");
 # else
     puts("defined(MACRO1)");
 # endif
 #else
     puts("!defined(MACRO1)");
 #endif
     return 0;
 }
Run Code Online (Sandbox Code Playgroud)

我不认为我曾见过有人像这样格式化:

 #include <stdio.h>

 int main(void) {
 #ifdef MACRO1
  #ifdef MACRO2
     puts("defined(MACRO1) && defined(MACRO2)");
  #else
     puts("defined(MACRO1)");
  #endif
 #else
     puts("!defined(MACRO1)");
 #endif
     return 0;
 }
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果C语言标准要求#-character应该在第一列.

那么上面的第三个选择是否合法?

如果以上所有案件都合法,那么我想知道这是否合法.

 #include <stdio.h>

 int main(void) {
 #ifdef MACRO
     puts("defined(MACRO)");
 /* Now there are other characters before the `#` */ #endif
     return 0;
 }
Run Code Online (Sandbox Code Playgroud)

这里#endif不再是该行的"开始",因为路上还有其他非空白字符.

关于最后一个例子看起来很奇怪的是Vim文本编辑器没有突出显示#endif评论之后的内容.

截图

所有这些例子我已没有使用任何警告编译gcc-Wall -pedantic开启标志(包括最后一个与评论之前#endif).

请注意,我只是对语法感到好奇.#在编程时,我总是像其他人一样把字符放在第一列.我永远不会++i; #endif在严肃的项目中写出类似的东西.

Jon*_*ler 15

在一些预标准C预处理器(意思是1989年之前)中,预处理器仅#在行的开头识别.

由于C89/C90标准要求预处理器#将线路识别为第一个非空白字符(并且C99和C11标准也是如此),现在缩进指令是完全合法的,它甚至可以实现这个千年的所有这一切都是可移植的代码.

在ISO/IEC 9899:2011(C11标准)中,第6.10节预处理指令说:

预处理指令由满足以下约束预处理标记的序列组成:所述序列中的第一令牌是#预处理令牌(在翻译阶段4的开始)是源文件(可选地后空白空间中的第一个字符不包含换行符或在包含至少一个换行符的空格后面.

翻译阶段在5.1.1.2翻译阶段中定义.

  1. 源文件被分解为预处理标记7)和空白字符序列(包括注释).源文件不应以部分预处理标记或部分注释结束.每个注释都被一个空格字符替换.保留换行符.是否保留或替换为新行以外的每个非空白字符序列是由实现定义的.

  2. 执行预处理指令,扩展宏调用,并执行_Pragma一元运算符表达式.如果通过标记连接(6.10.3.3)生成与通用字符名称的语法匹配的字符序列,则行为未定义.甲#include预处理指令导致从第1阶段至第4阶段处理指定的头或源文件,递归.然后删除所有预处理指令.

偶尔,您会发现源自20世纪80年代的编码标准仍然规定" #在线路开始".

我通常不会缩进预处理程序指令,但这样做是合法的.

  • @Cornstalks:这就是我阅读它的方式,但是如果有人在预处理指令行的开头向我提供代码以进行评论,我会在批准之前将他们送回修复它,并可能给他们讲授有关卫生编码的讲座做法也是! (2认同)
  • 哦当然!像许多事情一样,仅仅因为它有效/合法并不能使它成为一个好主意。 (2认同)
  • 虽然预处理器的大多数用途都不需要缩进(包括守卫、包含、常量定义),但我发现为了可读性而缩进 `#if ... #else ... #endif` 块是一种很好的做法。我还发现缩进旨在在一个函数范围内使用的宏定义仅用于匹配普通代码的缩进(并在函数末尾添加相应的“#undef”)是一种很好的做法。并不是说我会经常使用它,但是当这些东西是必需的时,适当的缩进确实有助于提高可读性。 (2认同)

Cor*_*lks 5

不,这是C标准的引用(从第6.10节开始):

预处理指令由一系列满足以下约束的预处理令牌组成:序列中的第一个令牌是一个#预处理令牌(在转换阶段4的开始处)是源文件中的第一个字符(可选地在空格之后)不包含换行符或在包含至少一个换行符的空格后面.

所以它是#在文件的开头或者在包含至少一个换行符的一些空格之后.

这意味着:

# define foo
  # define bar
Run Code Online (Sandbox Code Playgroud)

foo的定义很好,因为它#是文件中的第一个标记.bar的定义很好,因为#"跟随包含至少一个换行符的空格."