来自 C11 5.1.1.2翻译阶段:
第2段:
[...]非空的源文件应以换行符结尾 字符,不应该立即加上反斜杠 在进行任何此类拼接之前的字符。
这意味着每个源文件都必须以换行符结束。
示例:
#include <stdio.h>
int main()
{
printf("Hello world\n");
return 0;
}
以上使用clang prog.c -Wall -Wextra -std=gnu11 -pedantic
命令在Clang上编译的示例。编译器生成以下警告:
prog.c:7:16: warning: no newline at end of file [-Wnewline-eof]
}
^
没关系,因为源文件的末尾没有换行符。
使用gcc prog.c -Wall -Wextra -std=gnu11 -pedantic
命令,我在GCC上编译了上述程序。 GCC不会产生任何警告或错误。
那么,为什么GCC不会产生任何警告或错误?
答案 0 :(得分:3)
“应”的含义由第4节第2点定义:
如果违反约束或运行时约束之外的''shall''或''shall not''要求,则行为未定义。
您引用的段落不在“约束”部分中。因此,如果源文件未以尾随换行符结尾,则程序具有未定义的行为。
未定义的行为无需诊断。编译器可以自由地做任何事情。 GCC开发人员可能已经决定使程序的行为好像最后有一个换行符,而不是打扰用户警告。
答案 1 :(得分:2)
标准没有规定存储在磁盘上的物理字节与构成C程序的逻辑字符之间的任何特定关系。例如,如果源字符集仅使用代码0x00-0x7E,则符合实现可能会说任何行的第一个字符将设置高位,而前面是任何其他类型的分隔符。然后需要这样的实现,就好像每一行都跟一个换行符一样,即使源文件中没有出现换行符(我也不认为需要实现以允许完全空白的源行)以与包含单个空白字符的行可区分的方式表示。)
如果某个实现指定一个文本文件由换行符分隔的多行组成,并且一个N行文件将包含N-1个换行符,则需要表现为好像有一个换行符文件中的最后一个字节。但是,如果实现指定所有有效文本文件以换行符结尾,则在给出无效的文本文件时,它没有义务。
请注意,除其他事项外,在某些实现中,#include
之后的第一行将连接到包含文件末尾的不完整行,这似乎是合理的。这种行为虽然古怪,但在某些情况下可能会有用,并且某些代码可能依赖它。鉴于这种拼接,如果出乎意料,可能会产生基本上无限制的后果,将行为保留为未定义比尝试对可能发生的事情进行分类更简单。
答案 2 :(得分:1)
存在一个问题,换行不是很好的标准定义,因为不同的系统有不同的新行约定。但是你是对的...如果标准说编译器必须在那种情况下发出警告而gcc没有,那么它应该作为非整合问题提交。
但我同意@ supercat的答案,因为可以假设没有最终\n
的文件可以被安全地解释为正确分隔的文本文件,最后没有行结束...因为\n
可以解释为行分隔符,而不是一行结束。如果此解释有效,则空文件将被解析为一个空行文件,编译器无需解析它,在这种情况下不应发出警告。这同样适用于没有最终\n
的任何文件,并且使用\n
完成的文件应该被解释为n + 1
行文件,带有一个额外的空行(这不会使内部C代码的含义有什么不同,我很害怕)
如果你去gcc项目抱怨,这可能就是你得到的回应,所以要谨慎,但不要犹豫,不要这样做。
顺便说一句,您是否尝试使用最终\\
字符(没有\n
字符)来提供编译器,允许编译器插入最终换行符模拟正确定义的文件,但预处理器必须以特殊形式处理,以防\\
字符后跟一个新行。在这种情况下,编译器应该发出一些东西,因为你无法继续经过文件的最后一行。如果最后一行终止于\\
(这是不合格的),Clang没有说什么,让我们看看gcc是什么......(抱歉,我现在无法访问gcc)