【问题标题】:Why does this C program compile without an error?为什么这个 C 程序编译没有错误?
【发布时间】:2012-04-20 05:03:48
【问题描述】:

我是 C 的初学者,我在玩 C。我输入了这样的 C 代码:

#include <stdio.h>
int main()
{
    printf("hello world\n"); 
    \
    return 0;
}

即使我故意使用\,C 编译器也不会抛出任何错误。这个符号在 C 语言中是做什么用的?

编辑:

即使这样也有效:

"\n";

【问题讨论】:

  • "\n";这是一个没有任何效果的声明。它被编译器完全忽略并生成警告。尝试使用启用编译警告的 g​​cc -Wall 标志进行编译。
  • 您的主要问题已被多次回答。参考关于"\n"; 的部分,c 程序(或多或少)是一个语句列表。文字值(如 3"\n""hamburgers")是一个完全有效的语句,即使它没有任何事情。

标签: c


【解决方案1】:

反斜杠换行符序列在翻译过程的早期阶段(阶段 2)从代码中删除。它曾经是您在字符串连接之前创建长字符串文字的方式,也是您仍然将宏扩展到多行的方式。

请参阅 C99 标准的 §5.1.1.2 翻译阶段:

翻译的语法规则之间的优先级由以下指定 5)

  1. 物理源文件多字节字符被映射,在定义的实现中 方式,到源字符集(引入换行符 行尾指示符)(如有必要)。三字母序列被替换为 对应的单字符内部表示。
  2. 反斜杠字符 (\) 的每个实例紧跟一个换行符 字符被删除,拼接物理源行以形成逻辑源行。 只有任何物理源行上的最后一个反斜杠才有资格成为一部分 这样的拼接。非空源文件应以换行符结尾, 在任何此类之前不应紧跟反斜杠字符 发生拼接。
  3. 源文件被分解成预处理标记6)和序列 空白字符(包括 cmets)。源文件不应以 部分预处理标记或部分注释。每条评论都替换为 一个空格字符。保留换行符。是否每个非空 除换行符以外的空白字符序列被保留或替换为 一个空格字符由实现定义。
  4. 预处理指令被执行,宏调用被扩展,并且 _Pragma 一元运算符表达式被执行。如果一个字符序列 匹配通用字符的语法名称由令牌产生 连接(6.10.3.3),行为未定义。 #include 预处理 指令使命名的头文件或源文件从阶段 1 开始处理 通过第 4 阶段,递归。然后删除所有预处理指令。
  5. 每个源字符集成员和字符常量中的转义序列和 字符串字面量被转换为执行字符的对应成员 放;如果没有对应的成员,则转换为定义的实现 非空(宽)字符的成员。7)
  6. 连接相邻的字符串文字标记。
  7. 分隔标记的空白字符不再重要。每个 预处理令牌转换为令牌。结果令牌是 句法和语义上的分析和翻译作为一个翻译单元。
  8. 所有外部对象和函数引用均已解析。库组件是 链接以满足对未定义的函数和对象的外部引用 当前翻译。所有此类翻译器输出都收集到程序映像中 其中包含在其执行环境中执行所需的信息。

5) 实现应该表现得好像这些单独的阶段发生了一样,即使在实践中许多通常是折叠在一起的。

6) 如 6.4 所述,将源文件的字符划分为预处理标记的过程是 上下文相关。例如,查看&lt;#include 预处理指令中的处理。

7) 实现不需要将所有不对应的源字符转换为相同的执行 字符。

如果您在杂散的反斜杠后面有一个空格或任何其他字符,则会出现编译错误。我们可以告诉您在它之后没有任何内容,因为您没有编译错误。


你问题的另一部分,关于:

"\n";

完全不同。这是一个简单的表达式,没有副作用,因此对程序没有影响。优化器将完全丢弃它。当你写:

i = 1;

你有一个值被丢弃的表达式;对其修改i 的副作用进行了评估。

有时,您会发现如下代码:

*ptr++;

编译器会警告你表达式的结果被丢弃;表达式可以简化为:

ptr++;

并且会在程序中达到同样的效果。

【讨论】:

  • 在第一阶段,“线路结束指示器”是实现定义的。尾随空格可以被认为是行尾指示符的一部分,因此在第 2 阶段,即使原始输入中有空格,反斜杠之后也会立即换行。
  • @JerryCoffin:理论上,你是对的。在实践中,我的简化陈述适用于我听说过的所有现代环境。您是否有一个明确的反例,其中 C 编译器在执行反斜杠换行符拼接之前去除尾随空格? (引用了 C99 标准的确切规则;我所做的任何评论都排在标准和正在使用的实现中的错误之后。)
  • 不,不是现在的,虽然我似乎记得一些古代的人在部分时间做过。
  • @JerryCoffin:有传言说在 IBM 大型机上使用了“打孔卡”类型的记录,然后在 80 列(72 列?)图像上隐含尾随空白,那些可能会被淘汰。在这个距离上很难确定这些空白是否“真的存在”。但这是我所知道的唯一可能存在问题的背景。
  • 一般来说,大型机都足够面向 Hollerith 卡,这意味着尾随空格相当普遍(在我工作的控制数据机器上确实如此)。这不是我想的,尽管我认为它可能是相关的。我(模糊地)记得是在 MS-DOS 或 CP/M 上,而不是大型机,但我不记得更多了。
【解决方案2】:

\,当紧跟换行符时,会被预处理消耗,并导致下一个“物理”行连接到当前逻辑行。这对于编写较长的预处理指令非常重要,这些指令必须全部在一个逻辑行上:

#define SHORT very log macro \
   consisting of lots and \
   lots of preprocessor \
   tokens

如果您删除反斜杠换行符序列,则不再正确。来自 Unix 文化的其他一些语言具有类似的反斜杠行继续语法:源自 Bourne shell 的 POSIX shell 语言,还有 makefile。

$ this is \
one shell command

关于"\n";,这是用于形成表达式语句的主要表达式。在 C 中,表达式可以用作语句,并且一直被利用。例如,您的 printf 调用是一个表达式语句。 printf("hello world\n") 是一个后缀表达式,它调用一个函数,获得一个返回值。因为您将此表达式用作语句,所以返回值被丢弃。 printf的返回值 指示打印了多少个字符,或者它是否成功,因此通过丢弃它,您的程序会忘记 printf 调用是否真的有效。

由于表达式语句的值被丢弃,如果这样的语句也没有副作用,它是一个无用的语句(就像你的"\n")。但是这种无用的表达语句并没有错误。如果您在编译器命令行中添加警告选项,您可能会收到警告,例如“statement with no effect”之类的。

【讨论】:

    【解决方案3】:

    反斜杠 \ 被 C 预处理器解释。它保护它的下一个字符(你的情况下的换行符)。

    【讨论】:

    • 其实是'protect'的反义词;它确保反斜杠和换行符都被删除。
    【解决方案4】:

    反斜杠只是转义下一个字符。在这种情况下,可能是行结束 (CR) 字符。完全合理。

    【讨论】:

    • C 中的转义仅在字符串中启用。
    • 确实,预处理器只能转义行尾字符。为误导而道歉。上面dAm2k的回答比较准确。
    • @MatteoItalia:翻译的第 2 阶段是:“每个反斜杠字符 (\) 的实例都被删除,后面紧跟一个换行符,将物理源代码行拼接成逻辑源代码行。” (§5.1.1.2/1.1)。
    • @JerryCoffin:在标准中(仅指关于字符常量/字符串文字的“转义序列”)既不是所谓的转义,也不是普通的说法(我从未听说过转义字符是已删除)。在这种情况下,\ 是行继续字符,而不是转义字符。
    • 我的观点更多的是关于正在发生的事情,而不是人们可能会或可能不会选择称呼它。
    【解决方案5】:

    反斜杠加上后面是escape sequence; "\n" 一起是换行符(打印换行符)。另一个重要的是“\t”,用于制表符。

    【讨论】:

    • 转义序列仅出现在字符串和字符文字中。问题中的反斜杠不在字符串或字符文字内。
    猜你喜欢
    • 2015-10-30
    • 2022-10-14
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    相关资源
    最近更新 更多