为什么 i=3; s=(++i)+(++i)+(++i); s==? 的结果在 JavaScript 和 C++ 中计算的结果是不一致的?

昨天有个同学问起这个问题,自己觉得相当自信,肯定是4+5+6=15无疑了,可是同学说不对,我就用Javascript算了一下,确实是15;另一个同学闲来无事也用C++算了一下,可是结果是16。而且最初提问的那个同学说确实是16,只是不知道这是个什么逻辑。同样的表达式在不同语言中的运算结果居然不一样,这是为什么呢?C++运算这个式子时是什么样一个逻辑?
关注者
62
被浏览
16,048
补充解释一下为什么不同的 C / C++ 编译器可以有不同表现:
  1. C / C++ 标准中都规定[1][2]有 sequence point :
    At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.
  2. 所谓 side effects 即对于执行环境的状态有所影响,比如改变变量的值。
  3. Prefix increment 运算符 ++E 实际上等同于 (E+=1) [3],换言之,它会改变 E 的值。
  4. Compound assignment E1 op= E2 几乎等同于 E1 = E1 op (E2) ,唯一区别是 E1 只会计算一次[4]。
  5. C / C++ 标准对于执行构成某表达式之子表达式的次序并无指定unspecified[5],而非 undefined,两者并不相同[6]。下述 9 是 undefined 的状况)。
  6. 换言之,型如 (++x) + (++y) [...] 这样的表达式中,括号圈起的表达式的执行次序可以由编译器自行决定。
  7. 在两个 sequence point 之间,一个对象(object)所储存的值最多只应通过执行一个表达式来改变一次,并且只应在需要决定储存何值的情况下访问这一结果[6]:
    Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be read only to determine the value to be stored.
  8. (++i) + (++i); 这样的表达式中,i 的值改变两次,违反了上述引文的第一句;而由 6 可知,两次 ++i 的执行次序并无指定,但无论哪一个 ++i 先执行,与另一个 ++i 相加时将读取其结果,但此结果已经不再会决定 i 将会存储何值,违反了上述引文的第二句。
  9. 违反 7 中引文的规定属于 undefined[7],这一情况并无标准规定应对措施,可以由编译器自由处置[9],可能的结果包括(但不限于)忽略结果、中止程序运行、复活僵尸恐龙、导致女友怀孕、发射洲际飞弹、引发天网觉醒、或者删除硬盘上一切视频文件。

Sequence point 是这个问题(以及很多其他 C / C++ 问题)难以理解的主要原因。但很多教材也有意或无意地绕开 / 简化描述 sequence point 的原理,只是粗暴地教导读者不应该这样做。

Sequence point 的存在,是 C 语言以编译器书写者之便利为主要设计宗旨的一种体现。诸如 Java / JavaScript 等其他绝大多数编程语言并无 sequence point 的概念,比如 Java 明确规定了表达式的计算次序[10],ECMA Script 情况类似[11],所以没有太多编译(解释)器行为不一致的问题。

[1] ISO/IEC 9899:1999 (C99), §5.1.2.3 – 3
[2] ISO/IEC 14882:2011 (C++11), §1.9 – 7
[3] C99, §6.5.3.1 – 2
[4] C99, §6.5.16.2 – 3
[5] C99, Annex J.1
[6] stackoverflow.com/quest
[7] C99, §6.5 - 2
[8] C99, Annex J.2
[9] C99, §3.4.3 - 2
[10] docs.oracle.com/javase/
[11] ECMA 262, Annex D ecma-international.org/