int main( )
{
int k = 35 ;
printf ( "\n%d %d %d", k == 35, k = 50, k > 40 ) ;
}
上述程序的输出为“0 50 0”,没有引号。我的问题是k==35
和k > 40
是如何错误的?从我的角度来看,k
被赋予35,所以“k == 35”应该是真的;那么“{= 1}}在”k = 50“中被赋予50,所以”k> 40“必须为真,不是吗?
答案 0 :(得分:4)
printf
调用调用未定义的行为。
对函数参数的求值顺序是未指定的行为。这意味着可以从一组所有可能的订单中以任何顺序评估参数。该标准不要求实施强制执行任何订单。另外,分隔参数的逗号不是逗号运算符。这意味着在参数的评估之间没有序列点。唯一的要求是必须完全评估所有参数,并且必须在调用函数之前发生所有副作用。现在,C99标准§6.5¶2说
在前一个和下一个序列点之间,一个对象应该具有它 通过表达式的评估,最多修改一次存储值。 此外,先前的值应只读以确定该值 存储。
对函数参数的评估是不合理的。作业表达式k = 50
的评估结果为50
,但其副作用是将50
指定给k
。如果首先评估此表达式并立即产生副作用,则表达式k == 35
将评估为true
,而k > 40
将评估为false
。因此,取决于k = 50
表达的副作用何时发生,其他两个表达式将评估为true
或false
。但是,其他两个表达式违反了上述标准中引用部分的第二点。表达式访问k
,但不是要更改k
的值。这违反了先前的值只能读取以确定该值
存储子句。这就是未定义行为的原因。
只是强调,代码中未定义的行为不是因为参数的评估顺序未指定(未定义,这是不同的),但这是因为参数的评估之间没有序列点,因此,在此背景下违反了标准中的上述引用部分。
未定义的行为意味着代码的行为是不可预测的。任何事情都可能发生在从程序崩溃到你的硬盘驱动器被格式化为恶魔飞出你的鼻子(发射到夸张)。这只是为了强调标准不需要实现来处理这种情况,你应该总是避免这样的代码。
阅读 -
答案 1 :(得分:2)
由于参数列表中的赋值,您的代码会调用未定义的行为:
k = 50
你可能想要:
printf("%d %d %d\n", k == 35, k == 50, k > 40);
由于您在参数列表中嵌入了一个赋值,因此任何结果都是允许的。参数的评估没有必要的顺序;它们可以按任何顺序进行评估 - 任何结果都是正确的。为了您自己的理智,不要编写依赖于参数评估顺序的代码。
如果您正在学习的书不是简单地强调评估的顺序是不确定的,并且任何未定义的行为是坏的,那么您应该放弃该书。
答案 2 :(得分:1)
这是Undefined Behavior,这意味着可能发生任何事情。
为什么 UB ?
答案很简单,在k
的写入和k
的读取之间没有排序约束,甚至没有不确定排序,这不是用来确定的k
的新值。
printf ( "\n%d %d %d", k == 35, k = 50, k > 40 );
// No ordering constraint between function arguments.
除此之外:如果写入发生在参数列表中调用的函数中(而不是在该函数的参数中),则它将不确定地排序,因此不是 UB 。
不确定地测序意味着在不确定哪个之前或之后对其进行测序。它不像UB那样糟糕(任何事情都有),但它仍然不应该依赖,因为订单不可靠。
int SetVar(int *p, int v) {return *p = v;}
printf ( "\n%d %d %d", k == 35, SetVar(&k, 50), k > 40 );
// No ordering constraint between function arguments, but
// the function call is indeterminately ordered