【问题标题】:Does '#'-character have to be at the start of a line in the C preprocessor? [duplicate]'#' 字符是否必须位于 C 预处理器的行首? [复制]
【发布时间】:2015-03-08 09:28:51
【问题描述】:

我已经编写 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;
 }

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

 #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 不再在行的“开始”,因为还有其他非空白字符。

最后一个例子的奇怪之处在于Vim text-editor 没有突出显示评论后的#endif

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

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

【问题讨论】:

  • 我一直缩进预处理器指令。但我从来没有在# 和指令名称之间放置空格,我总是确保附加了# 和指令名称,而是在# 前面放置空格。它工作正常。
  • @user3629249 我不同意:这是一个典型的语言律师问题,无法通过使用编译器检查编译来回答。这种问题必须引用标准来回答。可惜,问题已经有五个标签了,否则我会添加语言律师标签...
  • @user3629249 这不是投反对票的好理由,我赞成它,因为即使编译器接受它并不意味着所有编译器都会接受。您必须在此处引用标准。
  • 我将您的c-preprocessor 标记换成了language-lawyer,因为您正在询问有关标准的具体问题。 cpreprocessor 这对应该足够了,添加的标签可能会减少反对和/或投票结束的倾向。如果您反对更改,请随时回滚。
  • @MartinJames:测试您的特定编译器是否允许缩进指令很容易。问题是语言标准保证了什么。

标签: c syntax language-lawyer c-preprocessor conditional-compilation


【解决方案1】:

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

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

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

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

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

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

  2. 预处理指令被执行,宏调用被扩展,并且 _Pragma 一元运算符表达式被执行。如果一个字符序列 匹配通用字符的语法名称由令牌产生 连接(6.10.3.3),行为未定义。 #include 预处理 指令使命名的头文件或源文件从阶段 1 开始处理 通过第 4 阶段,递归。然后删除所有预处理指令。

有时,您会发现源自 1980 年代的编码标准仍然规定“# at start of line”。

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

【讨论】:

  • 如果我正确地阅读了第 3 阶段(并且只是为了明确),这意味着 /* Now there are other characters before the '#' */ #endif 是有效的,对吗?
  • @Cornstalks:这就是我的阅读方式,但如果有人在预处理指令行的开头向我提供代码以使用 cmets 进行审查,我会在批准之前将其送回修复它,并且也可能给他们讲讲卫生编码实践!
  • 哦,当然!像许多事情一样,仅仅因为它有效/合法并不意味着它是一个好主意。
  • 虽然预处理器的大多数用途不需要缩进(包括守卫、包含、常量定义),但我发现缩进#if ... #else ... #endif 块以提高可读性是一种很好的做法。我还发现缩进宏定义是一种很好的做法,这些宏定义仅在一个函数的范围内使用以匹配普通代码的缩进(并在函数末尾添加相应的#undef)。并不是说我会经常使用它,但是当这些东西是必要的时,适当的缩进确实有助于提高可读性。
【解决方案2】:

不,这里引用了 C 标准的引文(来自第 6.10 节):

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

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

这意味着:

# define foo
  # define bar

foo 的定义很好,因为# 是文件中的第一个标记。 bar 的定义很好,因为 # “跟在包含至少一个换行符的空格之后。”

【讨论】:

    猜你喜欢
    • 2010-11-25
    • 2010-12-03
    • 2023-03-18
    • 2021-03-30
    • 1970-01-01
    • 2011-07-02
    • 1970-01-01
    相关资源
    最近更新 更多