通过这里的其他问题,我发现C中的预增量运算符返回rvalue,而不是lvalue。但是,在尝试下面的代码时
int a=35;
printf("%d %d %d %d %d",a++,a,++a,a++,++a);
我期待输出
38 39 38 36 36
但是我得到了输出
38 39 39 36 39
如果pre-increment返回rvalue,那么为什么打印'a'的最终值(表示返回lvalue)而不是在执行递增后立即返回的值?
答案 0 :(得分:2)
您正在调用未定义的行为:分隔函数调用参数的逗号不是“逗号运算符”。与此不同的是,它们不是序列点。
此外,您的示例与运算符是否返回rvalue或左值无关,因为您的代码不会尝试修改返回的值。
答案 1 :(得分:1)
当你编写一个函数调用,例如printf("%d %d %d %d %d",a++,a,++a,a++,++a);
时,它会变成一组要执行的操作:
a
的值。将a
设置为a
增加1的值。a
。a
的值。将a
设置为a
的值增加1。a
的值。将a
设置为a
增加1的值。a
的值多一个的操作。将a
设置为a
的值增加1。C实现必须执行所有这些操作。但是,C标准没有完全指定它们的执行顺序。根据C 2011(N1570)6.5.2.3 10,在评估参数和调用之前有一个序列点。这意味着必须在执行调用之前执行参数本身产生的操作。
但是,C标准没有说明参数的评估顺序。它甚至没有说由单个表达式(如在a++
中)产生的两个操作必须一起执行。允许C实现准备a
的值,然后评估其他一些参数,然后将a
设置为a
增加1的值。
这意味着您的函数调用可能使用a
的值,并在任意数量的不同顺序中多次设置a
的值。这是一个问题,因为它违反了C标准中的规则。 6.5 2说:
如果对标量对象的副作用相对于同一标量对象的不同副作用或使用相同标量对象的值进行的值计算未被排序,则行为未定义。...
在本段中,“未序列”表示两个操作没有C标准设置的明确顺序:如果允许C实现以任一顺序执行两个操作,则它们是未排序的。您的printf
来电包含多项未经检测的操作,这些操作会对a
产生副作用(设置a
是a++
的副作用)并计算一个值(仅使用{{1}传递一个参数“计算”它的值。)
因此,您的a
调用违反了C标准的规则,并且结果行为未定义。
您编写的每个正常表达式都应该只修改一次对象。切勿在同一表达式中使用printf
两次。此外,如果您修改对象,则无法单独使用它。如果表达式中有a++
个位置,则不能在其他位置单独a++
。
一些特殊运营商有例外。 a
和&&
运算符需要先评估其左操作数。这会导致左侧的子表达式在右侧的子表达式之前进行排序,并且避免违反C 2011 6.5中的规则。因此||
是合法的。逗号运算符还首先评估左侧的子表达式,因此a++ && a++
是合法的。但是,函数调用的参数是参数的列表,而不是逗号运算符的使用。