【问题标题】:Is following statement valid in ANSI C? Is it valid at all?以下语句在 ANSI C 中有效吗?它是否有效?
【发布时间】:2015-05-08 10:28:06
【问题描述】:

在准备 ANSI C 考试期间,我遇到了以下问题 -

以下陈述有效吗?
如果不是,请进行必要的更改以使其有效。

原始语句是:test(i++,i++); 它无效,因为根据 K&R p202 未定义行为

参数的评估顺序未指定

但是我可以将其更改为以下语句吗? test(i+=2, i+=3)?

我没有在 K&R 或任何其他来源看到这样的记录的问题。但是 XCode 编译它并运行没有任何警告。

【问题讨论】:

标签: c language-lawyer c89 sequence-points


【解决方案1】:

两者都是有效的语句,即合法的 C,并且在这两种情况下,行为都是未定义的。

【讨论】:

  • 这取决于您所说的“有效”。很多人会声称具有未定义行为的程序不是“有效的”,而这些声明至少与您的一样合法。
  • 如果你这么说,那又有什么用呢?
  • @hvd 先生,我认为 valid OP 的意思是 对编译器有效
  • 不要混淆 valid(如在句法上)和 正确(或根据标准“严格符合”)程序。
  • @JackWhitham:这显然是错误的。编译器不仅可以用未定义的行为做任何他们想做的事情,事实上他们会假设程序不包含 UB 以执行优化。
【解决方案2】:

将现有答案加起来,关键是陈述

test(i+=2, i+=3)

调用未定义的行为与

一样多
test(i++,i++);

因为在这两种情况下,函数参数列表中都没有为逗号分隔符安排序列点,因此,你最终修改相同变量的值两次在单个序列点的范围内。这会调用undefined behaviour

【讨论】:

    【解决方案3】:

    语句在语法上是有效的,无论是在更改之前还是之后。但问题仍然存在。如果您正在修改参数中的对象,并且未指定评估顺序。

    C99 第 6.5.2.2 节第 10 段

    函数指示符的求值顺序,实际 参数,实际参数中的子表达式是 未指定,但在实际调用之前有一个序列点。

    根据第 3.4.4 节第 1 段

    未指明的行为

    使用未指定的值或其他行为 国际标准提供了两种或多种可能性并强加了 在任何情况下都不会选择进一步的要求。

    另一方面,第 3.4.3 节第 1 段告诉

    未定义的行为

    行为,使用不可移植或错误的程序结构或 错误数据,本国际标准对此没有规定 要求

    在顺序或求值的情况下,它可以按任何顺序完成,取决于编译器如何生成代码,它可以按任何顺序存储在内存中,也可以通过寄存器传递参数。生成代码后,二进制文件在任何地方的行为都相同。因此,对于单个二进制文件,每次的结果都是相同的,但根据编译器的决定,事情可能会发生变化。

    最好的办法是避免任何看起来不正确或花哨的事情。如果有疑问,它可能是未定义的、未指定的、实现定义的行为。因此,您可以使同一件事变得明确和确定,如下所示。

    test (i, i+1);
    i += 2;
    

    test (i+1, i);
    i+= 2;
    

    取决于你想要的顺序。

    【讨论】:

    • 有趣!该顺序实际上不是未定义的,而只是未指定的。这是不同的。但是参数的评估之间没有序列点,所以行为仍然是未定义的。
    • 导致其行为未定义的原因是 2 个序列点之间对同一变量 i 的多重副作用。未指定的行为是这样的:test(printf("arg1\n"), printf("arg2\n"));
    • @chqrlie 我无法得到它。您能否澄清导致行为未定义而不是未指定的其他副作用?
    • Sourav Gosh 的回答简单明了:在没有中间序列点的情况下多次修改 same 变量会调用未定义的行为,i++ 和 @ 之间没有序列点987654327@ 在 OP 的代码片段中。在我的示例中,在输入 printf() 时有一个,因此即使 printf 修改了相同的变量(确实如此!),两个调用的执行顺序是未指定的,但不是未定义的。
    • test(i++, i++) 调用未定义的行为,而 test(a(), b()) 只是具有未指定的行为。
    【解决方案4】:

    正如其他人已经指出的那样,这两种情况下的行为都是未定义的,即使代码在两种情况下语法上都是有效的。我假设问题使用“有效”来表示“正确”,就像在严格符合 C 程序中一样。要使陈述正确,您必须首先了解/得出其意图。

    实际上,如果没有一些外部来源告诉您该意图是什么,这实际上是不可能的,但是为了论证,我们假设程序员希望使用参数 (i+1, i+2) 调用函数 test(按顺序外貌)。最好通过以下方式传达此意图:

    test (i + 1, i + 2);
    i += 2;
    

    避免由未指定的函数参数求值顺序引入的任何不良影响。

    【讨论】:

      【解决方案5】:

      这句话很令人困惑:test(i++,i++); 它是无效的,因为根据 K&R p202 的行为是未定义的

      事实上,这个声明在C 中一直是无效的。从 Kernighan 和 Ritchie 在他们的书C 编程语言 中对 C 的原始规范到几年前发布的最新 C11 标准,包括较旧的 C99 标准和过时的 C89 标准,也称为 ANSI C。只是不是出于上述原因。

      函数test 的参数被计算的顺序是未指定的,但这不是这里的问题:两个表达式修改同一个变量并且函数参数计算之间没有序列点。因此,无论您如何在用于参数的表达式中实现副作用,您都会调用未定义的行为。编译器可能会生成代码,但火箭可能会在起飞时爆炸。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 2011-02-18
      • 1970-01-01
      • 2011-10-31
      • 1970-01-01
      • 1970-01-01
      • 2012-01-26
      相关资源
      最近更新 更多