预增量运算符返回左值或右值?

时间:2014-02-18 08:59:31

标签: c lvalue rvalue pre-increment

通过这里的其他问题,我发现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)而不是在执行递增后立即返回的值?

2 个答案:

答案 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产生副作用(设置aa++的副作用)并计算一个值(仅使用{{1}传递一个参数“计算”它的值。)

因此,您的a调用违反了C标准的规则,并且结果行为未定义。

您编写的每个正常表达式都应该只修改一次对象。切勿在同一表达式中使用printf两次。此外,如果您修改对象,则无法单独使用它。如果表达式中有a++个位置,则不能在其他位置单独a++

一些特殊运营商有例外。 a&&运算符需要先评估其左操作数。这会导致左侧的子表达式在右侧的子表达式之前进行排序,并且避免违反C 2011 6.5中的规则。因此||是合法的。逗号运算符还首先评估左侧的子表达式,因此a++ && a++是合法的。但是,函数调用的参数是参数的列表,而不是逗号运算符的使用。