【问题标题】:Do parentheses force order of evaluation and make an undefined expression defined?括号是否强制评估顺序并定义未定义的表达式?
【发布时间】:2014-04-02 16:26:23
【问题描述】:

当我遇到这个问题时,我正在阅读我的教科书

  1. 以下表达式后面的 a 的值是多少?
    假设 a = 5 的初始值。提到步骤
    • a+=(a++)+(++a)

起初我认为这是未定义的行为,因为 a 已被多次修改。
然后我读了这个问题,它说提及步骤,所以我可能认为这个问题是正确的。

所以我的问题是:

  • 应用括号是否定义了未定义的行为?
  • 在计算括号表达式后是否创建了序列点?
  • 如果定义了,括号有什么关系,因为 ++ 和 () 具有相同的优先级

注意:解释清楚且清晰的答案将获得我的投票

【问题讨论】:

  • 最好的办法是编写既可读又可维护的合理代码。因此,首先要避免这种思路。
  • 您的教科书是否旨在教授“C/C++”?只是好奇。
  • @EdHeal 好吧,我同意你的看法。但这是我教科书中的一个问题,而且很多网站,如 this 都有这样的例子。
  • 那个链接弄错了,Akshay。它的原因是,由于在应用于单个操作数时,前增量比后增量具有更高的优先级,因此当应用于不同的操作数时,它也会首先被评估。不过,这种推理是错误的。没有任何依据。

标签: c++ c expression undefined-behavior operator-precedence


【解决方案1】:

不,应用括号不会使其成为一种定义的行为。它仍然未定义。 C99 标准 §6.5 ¶2 说

在前一个序列点和下一个序列点之间,一个对象应该有它的 表达式的评估最多修改一次存储的值。 此外,应仅读取先验值以确定该值 存储起来。

将子表达式放在括号中可能会强制子表达式的求值顺序,但不会创建序列点。因此,它不能保证子表达式的副作用(如果它们产生任何副作用)何时发生。再次引用 C99 标准 §5.1.2.3¶2

表达式的计算可能会产生副作用。在某些 执行序列中的指定点称为序列点,所有 以前评价的副作用应该是完整的,没有副作用 后续评估的效果应该已经发生。

为完整起见,以下是附录 C 中 C99 标准规定的序列点。

  1. 在计算参数后调用函数。

  2. 以下运算符的第一个操作数的结尾:逻辑与&&;逻辑或 ||;有条件的 ?;逗号 ,

  3. 完整声明符的结尾。

  4. 完整表达式的结尾;表达式语句中的表达式;选择语句的控制表达式(if切换); whiledo 的控制表达式 陈述; for 语句的每个表达式;这 return 语句中的表达式。

  5. 就在库函数返回之前。

  6. 在与每个格式化输入/输出函数转换说明符关联的操作之后。

  7. 在每次调用比较函数之前和之后,以及对比较函数的任何调用和任何调用之间 作为参数传递给该调用的对象的移动。

【讨论】:

  • 那么您将如何解释this 页面上的想法。
  • 说这是由不知道自己在说什么的人写的。
  • 如果你用谷歌搜索“在哪里学习 C”,那是搜索结果中的第四个链接。
  • @AkshayLAradhya:我建议使用批判性判断。 1. 并非所写的所有内容都是真实的(x-ref 帮助,但不能证明任何事情)和 2. 任何公司的目标首先是赚钱(而且准确是昂贵的,可能收益很小)
  • 鉴于计算机科学是以数学为基础的,令人惊奇的是,C 语言认可了这种愚蠢。几乎在其他任何地方,括号都会强制评估顺序并产生明确定义的结果。带括号的变量如何以某种方式未定义令人困惑。
【解决方案2】:

添加括号不会创建序列点,并且在更现代的标准中,它不会创建关于副作用的排序前关系,这是您所拥有的表达式的问题,除非注意到其余部分将与到 C++11。括号是5.1 部分中的主要表达式主要表达式,它具有以下语法(强调我的未来):

primary-expression:
  literal
  this
  ( expression )
  [...]

在第 6 段中说:

带括号的表达式是主要表达式,其类型和值与括起来的表达式相同。括号的存在不会影响表达式是否为左值。带括号的表达式可以在与可以使用括起来的表达式完全相同的上下文中使用,并且具有相同的含义,除非另有说明

postfix ++ 是有问题的,因为我们无法确定更新 a 的副作用何时会在 C++11 之前发生,而在 C 中这适用于 postfix ++prefix ++ 操作。关于 C++11 中 prefix ++ 的未定义行为如何变化,请参阅 Assignment operator sequencing in C11 expressions

+= 操作存在问题,因为:

[...]E1 op = E2 等价于 E1 = E1 op E2 除了 E1 是 只评估一次[...]

因此在 C++11 中,以下内容从未定义变为已定义:

a = ++a + 1 ;

但这仍未定义:

a = a++ + 1 ;

以上两者在 C++11 之前以及在 C99 和 C11 中都是未定义的。

来自草案 C++11 标准部分1.9程序执行15 段说:

除非另有说明,对单个运算符的操作数和单个表达式的子表达式的求值是无序的。 [注意:在程序执行期间多次评估的表达式中,其子表达式的未排序和不确定排序的评估不需要在不同的评估中一致地执行。 —尾注] 运算符的操作数的值计算在运算符结果的值计算之前排序。如果标量对象的副作用相对于同一标量对象的另一个副作用或使用同一标量对象的值的值计算是未排序的,则行为未定义。

【讨论】:

  • 您的最后一句话似乎表明,如果代码使用前缀递增运算符(对于a += ++a + ++a),那么 C++11 将认为该语句已定义。这是真的吗?在这种情况下,a 的最终值是多少?
  • @RobKennedy 我添加了一个链接,指向我最近在 C++11 中回答的一个问题 prefix ++ 返回一个左值,这意味着当您读出该值时,它会强制左值重新值转换,这会强制与副作用的关系。
  • 那么你的意思是a+=(a++)+(++a)这个表达式是有效的?
  • @AkshayLAradhya 查看我的更新,postfix +++= 创建未定义行为均无效。
猜你喜欢
  • 2010-11-24
  • 1970-01-01
  • 1970-01-01
  • 2018-08-01
  • 2013-06-28
  • 1970-01-01
  • 2016-05-04
  • 2017-11-24
相关资源
最近更新 更多