当前位置: 首页 > 知识库问答 >
问题:

C中赋值语句的评估顺序

简培
2023-03-14
map<int, int> mp;
printf("%d ", mp.size());
mp[10]=mp.size();
printf("%d\n", mp[10]);

这段代码给出的答案不是很直观:

0 1

我明白为什么会发生这种情况-赋值的左侧返回对mp[10]底层值的引用,同时创建上述值,然后才使用新计算的size()对右侧进行评估映射。

这种行为在C标准中是否有表述?还是求值顺序没有定义

使用g 5.2.1获得结果。

共有3个答案

慕逸仙
2023-03-14

我敢肯定,标准并没有为表达式指定< code > x = y;在C标准中计算< code>x或< code>y的哪个顺序(这就是为什么不能执行< code>*p = *p 的原因,例如,因为< code>p 不是按照定义的顺序执行的)。

换句话说,为了保证 x = y 的顺序;在定义的顺序中,您需要将其分解为两个序列点。

 T tmp = y;
 x = tmp;

(当然,在这种特殊情况下,人们可能会认为编译器更喜欢在< code>size()之前执行< code>operator[],因为这样可以将值直接存储到< code>operator[]的结果中,而不是保存在临时位置,以便在< code>operator[]求值之后再存储它——但是我很确定编译器不需要按这个顺序执行)

梁丘飞鸾
2023-03-14

来自C 11标准(强调矿山):

5.17赋值和复合赋值操作符

1赋值运算符(=)和复合赋值运算符都是从右向左分组的。它们都需要一个可修改的左值作为左操作数,并返回一个引用左操作数的左值。如果左操作数是位域,则所有情况下的结果都是位域。在所有情况下,赋值都是在左右操作数的值计算之后,赋值表达式的值计算之前进行的。

语言未指定是首先计算左操作数还是首先计算右操作数。编译器可以自由选择首先计算任一操作数。由于代码的最终结果取决于操作数的求值顺序,因此我会说这是未指定的行为,而不是未定义的行为。

未指明的行为

行为,对于格式良好的程序构造和正确的数据,这取决于实现

邢华清
2023-03-14

是的,这是标准所涵盖的,并且是未指定的行为。这种特殊的情况在最近的C标准提案中有所涉及:N4228:改进惯用C的表达式求值顺序,该提案试图改进求值规则的顺序,使其对某些情况有良好的规定。

它对这个问题描述如下:

表达式求值顺序是C社区中反复出现的讨论话题。简而言之,给定一个表达式,如f(a,b,c),标准没有规定子表达式f,a,b,c的求值顺序。如果这些子表达式中的任何两个碰巧修改了同一个对象而没有插入序列点,那么程序的行为就是未定义的。例如,表达式f(i,I)其中I是一个整数变量导致未定义的行为,v[i] = i也是如此。即使行为没有被定义,对表达式求值的结果仍然可以是任何人的猜测。考虑下面的程序片段:

#include <map>

int main() {
  std::map<int, int>  m;
  m[0] = m.size(); // #1
}

在对标记为#1的语句求值之后,映射对象m应该是什么样子?{{0,0}或{{}0,1}}?

我们知道,除非另有规定,否则子表达式的求值是不排序的,这来自于草案C 11标准部分<code>1.9</code>程序执行,其中规定:

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的计算是无序的。[...]

和所有部分5.17赋值和复合赋值操作符[expr.ass]说的是:

[…]在所有情况下,赋值都是在右操作数和左操作数的值计算之后、赋值表达式的值计算之前排序的。[…]

因此,本节没有确定评估的顺序,但我们知道这不是未定义的行为,因为运算符[]size()都是函数调用,第1.9节告诉我们(强调我的):

[...]调用函数时(无论该函数是否是内联的),与任何参数表达式或指定被调用函数的后缀表达式相关联的每个值计算和副作用都在被调用函数体中的每个表达式或语句执行之前进行排序。[注意:与不同参数表达式相关的值计算和副作用是没有顺序的。—end note ]调用函数(包括其他函数调用)中的每个求值,如果在被调用函数体执行之前或之后没有特别排序,则相对于被调用函数的执行是不确定排序的...]

请注意,我在“Chtml" target="_blank">编程语言”第4版36.3.6节中的代码是否具有定义良好的行为?”这个问题中提到了< code>N4228提案中的第二个有趣的例子。。

进化工作组在上一次WG21会议上似乎接受了《代码》N4228的修订版,但该文件(P0145R0)尚未提供。所以这可能在C17中不再是未指定的。

p0145的修订版3对此进行了指定,并更新[expr.ass]p1:

赋值运算符 (=) 和复合赋值运算符都从右向左分组。所有这些都需要一个可修改的左转值作为其左操作数;它们的结果是引用左操作数的左值。如果左操作数是位字段,则所有情况下的结果都是位字段。在所有情况下,赋值在右操作数和左操作数的值计算之后,在赋值表达式的值计算之前进行排序。右操作数在左操作数之前排序。...

 类似资料:
  • 问题内容: 如果您有一个if语句,其中对多个变量或函数进行了评估,那么按什么顺序对其进行评估? 在这种特定情况下,是将foo相对于5评估,然后将6相对于吧(从左至右)进行评估,还是将其从右至左进行评估?我假设a和以相同顺序求值。 问题答案: 如果第一个是,则将首先评估left子句,然后评估第一个。 这就是为什么您可以执行以下操作的原因: 没有破坏。 相反,对于子句,只有第一个为:时,将评估righ

  • 问题内容: 在Python中,我们可以这样做: 有人可以指出我有关此功能的文档吗? 它是语言的实现细节或功能吗? 利用此功能是否很好? 问题答案: 在和 短路 ,请参见布尔操作文档: 表达式首先计算; 如果为假,则返回其值;否则,将求值并返回结果值。 表达式首先计算; 如果为true,则返回其值;否则,将求值并返回结果值。 注意如何,对,是 只有 当评估计算为一个真正的价值。相反,for ,仅当评

  • 赋值语句在 Lua 被强化了,它可以同时给多个变量赋值。 例如: a,b,c,d=1,2,3,4 甚至是: a,b=b,a --多么方便的交换变量功能啊 在默认情况下,变量总是认为是全局的。假如你要定义局部变量,则在第一次赋值的时候,需要用 local 说明。比如: local a,b,c = 1,2,3 -- a,b,c 都是局部变量

  • 本文向大家介绍Python用exec评估语句,包括了Python用exec评估语句的使用技巧和注意事项,需要的朋友参考一下 示例            

  • 问题内容: 当可以设置为None时,使用以下格式是否不好? 问题是,如果my_var为None ,将引发TypeError。 还是我应该使用: 要么 换一个问题,以上哪一项是Python最佳实践(如果有)? 欢迎其他选择! 问题答案: 依赖条件语句的顺序是安全的(此处是Python参考),特别是由于您指出的问题-能够缩短可能导致一系列条件语句出现问题的评估是非常有用的。 大多数语言会弹出这种代码:

  • 问题内容: 分配给变量like 和使用let like有什么区别?还是像和这样的案例?每种方法的优点和缺点是什么? 问题答案: 完全是做什么的,它用于算术表达式。目前几乎没有差别之间和。 您的示例无效。要将单词添加到变量(这是一个字符串操作),将不起作用,因为它不是算术表达式: 实际上,如果仅设置了变量,则它是一个算术表达式,否则将被视为零。