“#”字符是否必须位于 C 预处理器中一行的开头?

Does '#'-character have to be at the start of a line in the C preprocessor?

我编写 C 语言已经有一段时间了。在这段时间里,我了解到将 preprocessor-directives 之前的“#”字符放在第一列是一种常见的约定。

示例:

 #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;
 }

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

 #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;
 }

我不认为我见过任何人这样格式化它:

 #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;
 }

我的问题是 C 语言标准是否要求 # 字符应该在第一列。

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

如果以上所有情况都是合法的,那么我想知道这是否合法。

 #include <stdio.h>

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

这里的 #endif 不再位于行的 "start" 上,因为中间还有其他 non-whitespace 个字符。

最后一个例子似乎很奇怪 Vim text-editor 没有突出显示评论后面的 #endif

我给出的所有这些示例都使用 gcc 并打开了 -Wall -pedantic 标志进行编译,没有任何警告(包括最后一个在 #endif 之前带有注释的示例)。

请注意,我只是对语法感到好奇。当我编程时,我总是像其他人一样将 #-character 放在第一列。我永远不会在严肃的项目中写 ++i; #endif 这样的东西。

在某些准标准 C 预处理器中(即 1989 年之前),预处理器仅识别行首的 #

由于 C89/C90 标准要求预处理器将 # 识别为行中的第一个非空白字符(C99 和 C11 标准也是如此),它现在是完全合法的缩进指令,在这个千年里,即使是可移植的代码也可以这样做。

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

A preprocessing directive consists of a sequence of preprocessing tokens that satisfies the following constraints: The first token in the sequence is a # preprocessing token that (at the start of translation phase 4) is either the first character in the source file (optionally after white space containing no new-line characters) or that follows white space containing at least one new-line character.

翻译阶段在第 5.1.1.2 节翻译阶段中定义。

  1. The source file is decomposed into preprocessing tokens 7) and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial comment. Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is implementation-defined.

  2. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal character name is produced by token concatenation (6.10.3.3), the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted.

偶尔,您会发现源自 1980 年代的编码标准仍然规定“# 在行首”。

我通常不缩进预处理器指令,但这样做是合法的。

不,这是 C 标准的引述(来自第 6.10 节):

A preprocessing directive consists of a sequence of preprocessing tokens that satisfies the following constraints: The first token in the sequence is a # preprocessing token that (at the start of translation phase 4) is either the first character in the source file (optionally after white space containing no new-line characters) or that follows white space containing at least one new-line character.

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

这意味着:

# define foo
  # define bar

foo 的定义很好,因为 # 是文件中的第一个标记。 bar 的定义很好,因为 # "follows white space containing at least one new-line character."