【问题标题】:Why are multiple increments/decrements valid in C++ but not in C?为什么多个增量/减量在 C++ 中有效但在 C 中无效?
【发布时间】:2013-02-10 14:52:27
【问题描述】:

测试。(c/cpp)

#include <stdio.h>

int main(int argc, char** argv)
{
  int a = 0, b = 0;
  printf("a = %d, b = %d\n", a, b);
  b = (++a)--;
  printf("a = %d, b = %d\n", a, b);

  return 0;
}

如果我将以上内容保存为 .cpp 文件,它会在执行时编译并输出:

a = 0, b = 0
a = 0, b = 1

但是,如果我将其保存为 .c 文件,则会收到以下错误:

test.c:7:12: error: lvalue required as decrement operator.

(++a) 操作不应该在(newValue)-- 操作之前解决吗?有人对此有任何见解吗?

【问题讨论】:

  • b = (++a)--; 未定义的行为吗?
  • @LihO:为什么? a 上的增量在其评估之前排序
  • 为什么不只是b = a + 1
  • @AndyProwl:我总是避免在同一行中使用更多的前增量/后增量操作......我不想最终得到这样的结果:b = ++a + ++a;,到目前为止已经是 UB据我所知。
  • 我认为真正要理解的教训是,C 代码行数越少,程序就越快。最好写 2 或 3 行。它更明显,更容易理解。我建议仅在非常特殊的情况下使用 predecrement/increment 并仅使用 post decrement/increment(即您无法找到任何其他方式来编写它)。

标签: c++ c language-lawyer lvalue incompatibility


【解决方案1】:

在 C 中,前缀和后缀递增/递减运算符的结果不是左值。

在 C++ 中,后缀自增/自减运算符的结果也不是左值,但前缀自增/自减运算符的结果是左值。

现在在 C++ 中执行 (++a)-- 之类的操作是未定义的行为,因为您在两个序列点之间修改了两次对象值。

编辑: 跟进@bames53 评论。它在 C++98/C++03 中是未定义的行为,但 C++11 中关于序列点概念的更改现在使该表达式已定义。

【讨论】:

  • C++11 取消了序列点,而只要求对读取和修改进行排序,它们位于 (++a)-- 中。见here
  • 不同意 (++a)-- 定义明确的说法。 bames53 链接到的页面将适用于--(++a),但后增量的顺序不同
  • @MM (++a)-- 中的++a 的副作用按照 C++11, 5.17p1 相对于后缀 -- 进行排序:"在所有情况下,分配排序在左右操作数的值计算之后,赋值表达式的值计算之前。" (with 5.3p2 "如果 x 不是 bool 类型,则表达式 ++x 是相当于 x+=1")。
  • @ouah ++a 中涉及的赋值不是按递减顺序排列的(我不确定你认为引用的哪一部分是相关的——递减不是 @ 的操作数之一987654330@)。也许一个新问题是合适的,而不是 cmets 中的论点
  • @M.M 我指的是引用的第二部分“赋值在赋值表达式的值计算之前排序[...]”。我们知道,对于后缀-- (5.2.6p1) “--表达式的值计算在操作数对象的修改之前排序。”,对我来说,副作用++a 应该在-- 之前发生。我在 SO 中不再很活跃,所以如果你不相信你可以问一个新问题,当然,如果我错了,我很乐意修改我的答案。
【解决方案2】:

在 C 和 C++ 中,有一些左值表达式可以用在 = 运算符的左侧,而右值表达式则不能。 C++ 允许更多的东西成为左值,因为它支持引用语义。

++ a = 3; /* makes sense in C++ but not in C. */

递增和递减运算符类似于赋值,因为它们修改了参数。

在 C++03 中,(++a)-- 会导致未定义的行为,因为两个没有相互排序的操作正在修改同一个变量。 (即使一个是“pre”,一个是“post”,它们是无序的,因为没有,&amp;&amp;? 等。)

在 C++11 中,表达式现在符合您的预期。但是C11并没有改变任何这样的规则,这是一个语法错误。

【讨论】:

  • “因为它支持引用语义”这仅仅是因为C没有将运算符定义为左值。
  • @bames53 这是达到目的的手段……
【解决方案3】:

对于任何可能想要了解标准中所述差异的精确细节的人,C99,§6.5.3/2 说:

前缀 ++ 运算符的操作数的值递增。结果是新的 递增后操作数的

相比之下,C++11,§5.3.2/1 说:

结果是更新后的操作数;它是一个左值,如果它是一个位域 操作数是位域。

[强调,在这两种情况下]

还请注意,虽然aint(++a)-- 给出了未定义的行为(至少在C++03 中),但如果a 是一些用户定义的类型,那么您使用的是您自己的++-- 的重载,行为将被定义——在这种情况下,您将获得相当于:

a.operator++().operator--(0);

由于每个运算符都会导致一个函数调用(不能重叠),您实际上 确实 有序列点来强制定义的行为(请注意,我不推荐使用它,只是注意到在这种情况下实际上定义了行为)。

【讨论】:

    【解决方案4】:

    §5.2.7 递增和递减:

    后缀++ 表达式的值是其操作数的值。 [ ... ]  操作数应为可修改的左值

    您在 C 编译中遇到的错误有助于表明这只是 C++ 中的一个功能。

    【讨论】:

      猜你喜欢
      • 2023-03-12
      • 2014-01-23
      • 1970-01-01
      • 2022-01-13
      • 1970-01-01
      • 2015-02-01
      • 2018-06-20
      • 1970-01-01
      相关资源
      最近更新 更多