关于 i++ + ++i 是什么的终极解释

AlanWillaims 2009-11-16 11:31:06
我知道大部分人都了解这个东西是未定义的. 但是反复在面试笔试中看到这种问题实在令人火大. 今天又看见这种题实在忍不住了. 如果是面试我还能跟对方解释, 如果笔试出这种题就只剩下我鄙视它了. 我发贴的目的只是想帮助一下苦命学生. 万一出题的老师或者面试官看见了这贴, 麻烦您顺手在选项中间加一个"不确定", 或者"gcc"吧.

下面根据规范说明这一点. 如果看过就请离开吧. 占用大家时间很不好意思.

C 语言:

C 语言使用 sequence point 来定义表达式的求值顺序. 在两个 sequence point 之间一个对象的 value 只能被修改至多一次(注意这里的语气非常强硬). 另外, 在先的值(这里指的是修改之前的值)如果被读, 则只能被用来决定应该写什么值. 这里所谓 sequence point, 指的是在表达式中特定的点, 在这个点上, 先前的副作用都已完成, 而之后的副作用都还没有发生. 所谓副作用是:
读写 volatile 对象;
修改一个对象;
函数调用;
修改一个文件.

另外, 副作用的顺序是未指定(未指定和未定义有点区别, 但总之都是和实现相关. 后文有时混用两者, 大家凑合着看吧)的.

C规范用一个 annex 总结了所有 sequence points:

1. 参数求值之后, 函数调用之前;
2. 以下操作符的第一个操作数结束之前: &&, ||, ?:, 逗号; (因为这一条, j = i++ && i++; 就是定义的, 而 j = i++ + i++ 则是未定义的, 如果我出题就考这么一个表达式: "i = -1, i++ + 1 || i++")
3. 一个完整的 declarator 之后;
4. 一个完整的表达式之后;
5. 库函数返回之前;
6. conversion specifier (就是 printf 里面 % 打头的那个东西) 对应的行为之后 (这并不意味着 i = 0, printf("%d, %d", i++, i++); 应该是 0, 1 (gcc 是 1, 0). 规范明确, 反复地说函数参数求值顺序是未指定的, 因此用那个 printf 考人也很混蛋, 除非他说明用什么编译器)
7. 提供给 bsearch 和 qsort 的比较函数被调用之前和之后, 以及调用比较函数和移动对象之间.

如此, 像这样的表达式:

(i++)+(i++)+(i++)和(++i)+(++i)+(++i); (http://www.javaeye.com/wiki/topic/512205)
b = a++ + a++ + a++; (http://blog.csdn.net/huanglaobo/archive/2009/04/20/4093123.aspx)

都是未定义的.
a[i++] = i 也是为定义的, 因为它读取 i 的值不只是为了确定应该 store 什么;

i = 0;
int * p = &i;
i = (*p)++;

是未指定的, 因为'对i赋值'和 '*p自增' 这两个副作用的顺序是未指定的.

在C++中, 由于引入了并行, 现在不再用 sequence points 来定义求值顺序, 而改用 happens before 关系了. 我相信这不会导致某个在 C 中未定义的东西在 C++ 中变成定义的了. 相反的应该也不会.

C++ 是这样说的:

如果一个标量的副作用和同一个标量的另一个副作用, 或者和一个使用这个标量值的计算, 没有定义顺序, 则行为是未定义的.

而规范中对每个算符的操作数于算符本身的计算是否有序进行单独的定义. 对于一般情况:

算符的运算对象的求值顺序, 或者表达式的子表达式的求值顺序是没有顺序(unsequenced), 除非规范中另有定义;
运算对象的求值 在 所在的算符的求值 之前 (sequence before);
逗号表达式左边的求值和副作用先于右边的;
对于赋值: 副作用发生在左右表达式求值之后, 整个赋值表达式求值之前. 但是左右表达式之间没有定义顺序.
对于后缀 ++ 和 --: 副作用发生在求值之后. 至于后到什么程度则没说. 因此在整个表达式结束时副作用也可以;
对于前缀 ++ 和 -- 则稍微复杂一点. 规范中只是说了它的语义, 并且说如果 x 不是 bool 类型, 则 ++x 等价于 x+=1 (而且根据规范, x 很快就不能是 bool 类型了). 至于 x+=1 则是一个赋值操作. 赋值操作的顺序是左右表达式-->副作用-->求值. 不过这并不意味着 i=1, j = ++i + ++i 就必须是 5. 实际上因为加法没有定义左右操作数求值顺序, 这里对 i 的两次副作用是无序的, 因此还是未定义.
现在看来唯一有希望有定义的这种表达式就是 i = ++i 了. 由于 ++i 等价于 i += 1, 是个赋值, 它的副作用需要在求值之前. 所以这次两个副作用是有序的. 可惜的是, 左边的 i 虽然是左值, 但是也是要通过求值求出来. 这回左边的求值和右边的副作用是无序的, 因此这个东西还是未定义的.

我承认, 我在这里喷了这么多, 主要原因还是为了发泄不满. 不过只要有一个原本不知道这些的出题人看见了这贴, 我就算造福了参加他考试的一众学生, 也算是积德行善吧. 谢了.
...全文
895 44 打赏 收藏 转发到动态 举报
写回复
用AI写文章
44 条回复
切换为时间正序
请发表友善的回复…
发表回复
RANDOM_RD 2010-06-27
  • 打赏
  • 举报
回复
支持一下 我也曾在这个问题上迷惑过
Arthuel 2010-06-27
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 quzhongsan 的回复:]
据(++i)+(++i)+(++i)在vc上结果是16(已验证过)
在GCC上是18(还未验证)
真正写代码的时候谁会写这种啊
[/Quote]
为什么我的GCC是16;;;
i = 0;printf("%d,%d",i++,i++);GCC是1,0对的
VC6是 0,0;;;;;
CCCCCCCCCCCCCCC 2010-06-17
  • 打赏
  • 举报
回复
lz太闲了
mymtom 2010-06-17
  • 打赏
  • 举报
回复
[Quote=引用 40 楼 evilhacker 的回复:]

我来回答一下标题“i++ + ++i 是什么的终极解释?”
答:被驴子摸了一下脑壳
[/Quote]
只有引用的内容不允许回复!
prolinor 2010-06-16
  • 打赏
  • 举报
回复
学习了啊
evilhacker 2010-06-16
  • 打赏
  • 举报
回复
我来回答一下标题“i++ + ++i 是什么的终极解释?”
答:“蛋疼”
mymtom 2010-06-16
  • 打赏
  • 举报
回复
这就好像中了500万后去买100辆QQ
bobo364 2010-06-13
  • 打赏
  • 举报
回复
可以的,连i++,++i还有最终的解释
cffa_edfe 2010-06-13
  • 打赏
  • 举报
回复
毫无意义
jinjia2010 2010-06-13
  • 打赏
  • 举报
回复
纯语法性问题,
不明白意义在何。
a1121221 2010-05-21
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 xiaozhi_5638 的回复:]
i++ + ++i 是个错误的表达式
先算i++
根据大嘴法则,接着算(i++)++
接着就是((i++)++)+i啦
可是((i++)++)不能当左操作数
所以错了
[/Quote]

中间加上空格就没错了
ArtiFly2000 2010-05-21
  • 打赏
  • 举报
回复
"i = -1, i++ + 1 || i++"
出这样的题?

LZ被别人折腾了,难道还想再去折腾别人吗?
soidothis 2010-05-21
  • 打赏
  • 举报
回复
像这种问题实际运用的时候都是尽量避免的。在不同的编译器里面会得出不同的结果。。。
lancer2002 2010-05-21
  • 打赏
  • 举报
回复
编译器的问题,不同的工具就是不同结果

请叫我卷福 2010-05-21
  • 打赏
  • 举报
回复
i++ + ++i 是个错误的表达式
先算i++
根据大嘴法则,接着算(i++)++
接着就是((i++)++)+i啦
可是((i++)++)不能当左操作数
所以错了
JohnLiu 2010-05-21
  • 打赏
  • 举报
回复
曾经我也发过这样的月经贴!!!
周靖峰 2010-05-21
  • 打赏
  • 举报
回复
果断支持
2010-05-21
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 zhao4zhong1 的回复:]

说似一物即不中
VC调试时按Alt+8,TC或BC用TD调试,打开汇编窗口看每句C对应的汇编不就啥都明白了吗。
(Linux或Unix下应该也可以在用GDB调试时,看每句C对应的汇编。)
[/Quote]
您就别在这恶搞了……
赵4老师 2010-05-21
  • 打赏
  • 举报
回复
说似一物即不中
VC调试时按Alt+8,TC或BC用TD调试,打开汇编窗口看每句C对应的汇编不就啥都明白了吗。
(Linux或Unix下应该也可以在用GDB调试时,看每句C对应的汇编。)

2010-05-21
  • 打赏
  • 举报
回复
这帖又翻出来了?
那我再 up 一下~
加载更多回复(24)

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧