以下语句在ANSI C中是否有效?它是否有效?

6
在我准备ANSI C考试的过程中,我遇到了以下问题 -
引用:
“以下语句有效吗? 如果无效,请进行必要的更改使其有效。”
原始语句为:test(i++,i++);,它是无效的,因为根据K&R p202,行为未定义。
引用:
参数的评估顺序未指定
但我能把它改成以下语句吗?test(i+=2, i+=3)
问题在于我没有在K&R或任何其他来源中看到这样的记录。但XCode编译并运行它而不发出任何警告。

相关链接:https://dev59.com/nnRC5IYBdhLWcg3wP-Zh - Phillip
相关链接:https://dev59.com/Zm855IYBdhLWcg3wuG-W - alk
被接受的答案是不正确的。在参数中的“,”是一个序列点;只有参数评估的顺序是未指定的。没有未定义的行为。 - Antti Haapala -- Слава Україні
5个回答

12
为了补充现有答案,关键点是该声明。
test(i+=2, i+=3)

会像无限循环一样引发未定义行为

test(i++,i++);

因为在这两种情况下,在函数参数列表中没有安排逗号分隔符的顺序点,因此您最终会在一个顺序点的作用域内两次修改相同变量的值。这将触发未定义行为


12

两种写法都是合法的,即符合C语言的规范,但无论哪种写法,其行为都是未定义的。


6
这要看你对“合法”的定义。许多人会声称,具有未定义行为的程序是不“合法”的,而这些主张至少和你的主张一样有权威性。 - user743382
@hvd 先生,我认为说“有效”的 OP 意味着“对编译器有效”。 - Sourav Ghosh
啊啊啊……我明白了,实际上我通过重写语句并没有改变任何东西,需要在函数调用之前创建额外的变量进行计算。谢谢。 - Anatoly
3
不要混淆“有效”(即语法上的)和“正确”(或根据标准的“严格符合”的)程序。 - Michael Foukarakis
1
@JackWhitham:那显然是错误的。编译器可以随意处理未定义行为,而且事实上,为了进行优化,它们会假定程序不包含任何未定义行为。 - Michael Foukarakis
显示剩余10条评论

7
语句在变更前后语法上都是有效的。但问题仍然存在:如果您修改了参数中的对象,而且评估顺序未指定,则问题将继续存在。
根据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;

根据您想要的顺序。


1
这段代码的未定义行为是在两个序列点之间对同一变量i产生多个副作用。而未指定行为则是这样的:test(printf("arg1\n"), printf("arg2\n")); - chqrlie
@chqrlie,我无法理解。您能否澄清另一个副作用,使行为变得未定义而不是未指定? - phoxis
Sourav Gosh的回答简单明了:在没有中间序列点的情况下多次修改同一变量会引发未定义行为。在OP的代码片段中,i++i++之间没有序列点。在我的例子中,在printf()进入时有一个序列点,因此即使printf修改相同的变量(它确实这样做!),两个调用的执行顺序是未指定但不是未定义的。 - chqrlie
未指定的顺序意味着编译器可以选择其中之一。未定义的行为意味着任何事情都可能发生。 - chqrlie
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - chqrlie
显示剩余5条评论

4
正如其他人已经指出的那样,两种情况下的行为都是未定义的,即使在两种情况下代码都是语法上有效的。我假设问题使用“有效”一词来表示“正确”,就像一个严格符合C程序的情况一样。要使该语句正确,您必须首先了解/推导其意图。
实际上,这可能是不可能的,除非有些外部来源告诉您确切的意图,但为了论证,让我们假设程序员希望使用参数(i+1, i+2)(按照出现的顺序)调用函数test。最好通过简单地表达这个意图来传达它:
test (i + 1, i + 2);
i += 2;

避免由于函数参数的未指定评估顺序引入任何负面影响。

4
这句话很令人困惑:test(i++,i++);它是无效的,因为根据K&R p202的规定,其行为是未定义的。 事实上,在C语言中,这个语句一直都是无效的。从Kernighan和Ritchie在他们的书《C程序设计语言》中最初对C语言的规定到几年前发布的最新的C11标准,包括旧的C99标准和过时的C89标准(也称为ANSI C)。只是原因不同而已。
函数test参数的求值顺序是未指定的,但这不是问题所在:两个表达式修改了同一个变量,并且在函数参数求值之间没有顺序点。因此,无论你如何使用表达式来实现副作用,你都会引发未定义的行为。编译器可能会生成代码,但火箭可能会在起飞时爆炸。

求值顺序是未指定而非未定义,在一个序列点内进行多次修改是未定义的。 - Shafik Yaghmour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接