为什么(X + = X + = 1)用C和Javascript评估不同? [英] Why does (x += x += 1) evaluate differently in C and Javascript?

查看:128
本文介绍了为什么(X + = X + = 1)用C和Javascript评估不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果变量的值x 最初是0,前pression X + = X + = 1 将计算为2℃,比1的JavaScript。

对于C语义似乎很明显对我说: X + = X + = 1 是间preTED为 X + =(X + = 1)是,反过来,相当于

  X + = 1
X + = X //其中x是1在这一点上

什么是背后JavaScript的跨pretation逻辑?什么规范实施这样的行为? (应该指出,顺便说一句,了Java的JavaScript同意在这里)。

更新:
原来,前pression X + = X + = 1 是未定义行为根据C标准(感谢的 ouah 约翰·伯德,的 DarkDust 德鲁杜曼)这似乎破坏了一些读者的问题的关键所在。这位前pression可以制成符合标准,并通过如下插入标识功能到其中: X + = ID(X + = 1)。相同的修改可以由给Javascript code和问题仍然存在题。 presuming大多数读者能够理解其背后的非符合标准的制定的时候,我会保持它,因为它是更简洁。

更新2:原来,根据C99引入了身份的功能可能不是解决歧义。在这种情况下,亲爱的读者,请把原来的问题作为属于C ++,而不是C99,其中+ =可以是最可能是现在可以安全地被视为与一个唯一地定义的操作的序列可重载的操作者。也就是说, X + = X + = 1 是相当于现在操作符+ =(X,运算符+ =(X,1))。很抱歉的漫长道路标准的符合性。


解决方案

JavaScript和Java有pretty这个前pression严格得多左到右的评价规则。 C没有(甚至在您所提供的具有身份干预功能的版本)。

ECMAScript的规范我有(第3版,这我承认很旧 - 当前版本可以在这里找到:的 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf )说,复合赋值运营商都像这样计算:


  

11.13.2复合赋值(OP =)


  
  

生产AssignmentEx pression:LeftHandSideEx $ P $ @ pssion =
  AssignmentEx pression,其中重@ presents指定的运营商之一
  以上,如下评价:


  
  

      
  1. 评估LeftHandSideEx pression。

  2.   
  3. 呼叫的GetValue(结果(1))。

  4.   
  5. 评估AssignmentEx pression。

  6.   
  7. 呼叫的GetValue(结果(3))。

  8.   
  9. 申请运营商向@结果(2)和Result(4)。

  10.   
  11. 呼叫PutValue(结果(1),结果(5))。

  12.   
  13. 返回结果(5)

  14.   

您注意,Java具有相同的行为,如JavaScript - 我认为它的规范更具有可读性,所以我会在这里发布一些片段(的http://java.sun.com/docs/books/jls/third_edition/html/ex$p$pssions.html#15.7):


  

15.7评估顺序


  
  

Java编程语言保证了该操作数
  运营商出现在一个特定的评估为了评估,
  即,从左至右


  
  

建议code不是关键靠这个规范。
  code是当每个前pression包含通常更清晰的最多一个侧面
  效果,作为其最外面的操作,当code不依赖于
  到底哪个引发异常的左到右的结果
  前pressions的评价。


  
  

15.7.1评估二元运算符的左侧操作数第一左侧操作数出现的任何部分之前充分评估
  右边的操作数进行评估。例如,如果左侧操作数
  包含一个分配给一个变​​量与右边的操作数
  包含对相同变量的引用,则该值所产生
  参考将反映发生的分配的事实
  第一位。


  
  

...


  
  

如果运营商是一个复合,赋值运算符(§15.26.2),然后
  左侧的操作数的评价包括记住
  变量左侧的操作数是指和提取和保存
  该变量在隐含组合操作的实用价值。


在另一方面,在你提供一个中间身份功能未未定义行为例如:

  X + = ID(X + = 1);

虽然它不是不确定的行为(因为函数调用提供了一个序列点),它仍然是不确定的行为是否最左边的 X 在函数调用之前或之后进行评估。所以,虽然这不是怎么都行不确定的行为,C编译器仍允许同时评估 X 变量调用 ID()<前/ code>功能,在这种情况下,终值存储到变量将是 1

例如,如果 X == 0 开始,评估可能看起来像:

  TMP = X; // TMP == 0
X = TMP + ID(X = TMP + 1)
在这一点上//点¯x== 1

或者它可能像这样评价它:

  TMP = ID(X = X + 1); // TMP == 1,X == 1
X = X + TMP;
//点​​¯x== 2在这一点

请注意:未规定行为比不确定的行为稍有不同,但它仍然是不可取的行为。

If the value of the variable x is initially 0, the expression x += x += 1 will evaluate to 2 in C, and to 1 in Javascript.

The semantics for C seems obvious to me: x += x += 1 is interpreted as x += (x += 1) which is, in turn, equivalent to

x += 1
x += x  // where x is 1 at this point

What is the logic behind Javascript's interpretation? What specification enforces such behaviour? (It should be noted, by the way, that Java agrees with Javascript here).

Update: It turns out the expression x += x += 1 has undefined behaviour according to the C standard (thanks ouah, John Bode, DarkDust, Drew Dormann), which seems to spoil the whole point of the question for some readers. The expression can be made standards-compliant by inserting an identity function into it as follows: x += id(x += 1). The same modification can be made to the Javascript code and the question still remains as stated. Presuming that the majority of the readers can understand the point behind "non-standards-compliant" formulation I'll keep it as it is more concise.

Update 2: It turns out that according to C99 the introduction of the identity function is probably not solving the ambiguity. In this case, dear reader, please regard the original question as pertaining to C++ rather than C99, where "+=" can be most probably now safely be regarded as an overloadable operator with a uniquely defined sequence of operations. That is, x += x += 1 is now equivalent to operator+=(x, operator+=(x, 1)). Sorry for the long road to standards-compliance.

解决方案

JavaScript and Java have pretty much strict left-to-right evaluation rules for this expression. C does not (even in the version you provided that has the identity function intervening).

The ECMAScript spec I have (3rd Edition, which I'll admit is quite old – the current version can be found here: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) says that compound assignment operators are evaluated like so:

11.13.2 Compound Assignment ( op= )

The production AssignmentExpression : LeftHandSideExpression @ = AssignmentExpression, where@ represents one of the operators indicated above, is evaluated as follows:

  1. Evaluate LeftHandSideExpression.
  2. Call GetValue(Result(1)).
  3. Evaluate AssignmentExpression.
  4. Call GetValue(Result(3)).
  5. Apply operator @ to Result(2) and Result(4).
  6. Call PutValue(Result(1), Result(5)).
  7. Return Result(5)

You note that Java has the same behavior as JavaScript – I think its spec is more readable, so I'll post some snippets here (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7):

15.7 Evaluation Order

The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.

It is recommended that code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.

15.7.1 Evaluate Left-Hand Operand First The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated. For example, if the left-hand operand contains an assignment to a variable and the right-hand operand contains a reference to that same variable, then the value produced by the reference will reflect the fact that the assignment occurred first.

...

If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied combining operation.

On the other hand, in the not-undefined-behavior example where you provide an intermediate identity function:

x += id(x += 1);

while it's not undefined behavior (since the function call provides a sequence point), it's still unspecified behavior whether the leftmost x is evaluated before the function call or after. So, while it's not 'anything goes' undefined behavior, the C compiler is still permitted to evaluate both x variables before calling the id() function, in which case the final value stored to the variable will be 1:

For example, if x == 0 to start, the evaluation could look like:

tmp = x;    // tmp == 0
x = tmp  +  id( x = tmp + 1)
// x == 1 at this point

or it could evaluate it like so:

tmp = id( x = x + 1);   // tmp == 1, x == 1
x = x + tmp;
// x == 2 at this point

Note that unspecified behavior is subtly different than undefined behavior, but it's still not desirable behavior.

这篇关于为什么(X + = X + = 1)用C和Javascript评估不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆