呵呵,很棒,学习语言就是要有跟细节问题叫板的决心,不做半调子程序员。
我以前的C语言老师就跟我们说过C语言的细节问题是永远都无法全部弄清的,可见细节问题确实不好对付,要坚持。
好了,不说废话了,下面谈一些关于这个自增问题我个人的一点看法,仅供参考。
首先:k=(j)+(++j)+(j)+(++j)+(j);k值为32,我的解释是 6+6+6+7+7 = 32.
这是为什么呢?不着急,我们从头开始慢慢谈。
第一个概念:序列点和副作用
首先感谢3楼,我是看到3楼的回帖才听说这个概念的,这两个概念谈的都是C语言的表达式计算。
所谓副作用,这里粗糙的说就是表达式中变量的值被修改。
为什么叫副作用呢,因为表达式的主要作用是返回求解的值,你可能听说过每个表达式都会返回一个值之类的话,说的就是这个意思。
相对于表达式返回值这一主要作用,由于表达式的求解而对变量值产生的修改被称之为副作用。
所谓序列点,这里也粗糙的说,就是表达式求解过程中的一些时刻点,比如说2+5*3这个式子,先乘后加,那么这里先和后的两个动作之间,我们就说有一个序列点
关于序列点和副作用之间的关系,有这样一句话需要仔细理解:
在序列点上,该点之前所有运算的副作用都应该结束,并且后继运算的副作用还没发生。
这里我们就简单的这样理解,如果表达式中有序列点,也就是有明确的先算和后算之分,那么先的那部分算完后变量就会被修改,然后再接着算后面的。
那么表达式中哪些地方会产生序列点呢,我觉得有明确求值顺序的地方就有序列点。
第二个概念:表达式求值顺序
先介绍一下C语言中的表达式:
1.由操作符和操作数构成表达式
2.表达式可以嵌套
哪些因素会影响到求值顺序呢。有三点:操作符自身规定的求值顺序,优先级,结合性
被规定有明确求值顺序的操作符有三个 (&& ||)、(?:)、(,)这三类操作符都规定先算左操作数。除此之外,其他操作符都没有定义求值顺序,这也是产生未定义的原因
由于表达式可以嵌套,a+b+c 是两个简单表达式嵌套而成的复杂表达式,子表达式的求值顺序由操作符的优先级决定,相同优先级的由结合性确定
如a+b*c *优先级比+高 则先求解b*c 再求解a+(),再如a+b+c 由于优先级相同,+具有左结合性,则先算a+b 再算()+c
有一点值得提出:尽管不是所有操作符都定义了自身的求值顺序,但所有操作符几乎都有明确的优先级和结合性,
因此多个操作符的复杂表达式的各个子表达式的求值顺序几乎总是确定的。
嗯,说了这么多,现在可以来看看上面这个表达式了
(j)+(++j)+(j)+(++j)+(j)
首先:+号的左结合性,j+(++j)这里产生一个序列点,++比+有更高的优先级产生一个序列点,那么在算+之前j的值应该被修改为6,++j自身也是返回6的 所以6+6=12
这样在第一个+号产生的序列点上,表达式变为 12+j+(++j)+j
接着:12+j 注意此时j已经是被明确修改为6了 所以 12+6 = 18 表达式变为18+(++j)+j
继续:18+(++j) 应该是 18+7 = 25 没错吧
最后:25 + j 同样的 由于上一个序列点的存在,这里的j已经明确为7了 所以 25+7 = 32
解释完了。
补充说明(关于(j++)+(j++)的重新解释):
再看一下++j 和 j++,一开始说 这个是先算后加 先加后算,又有先后了,这里也有序列点在里面?
注意:这个先后跟序列点无关。++j返回的是变量j之身的引用,j++返回的是一个临时变量的常引用,可以说++j还是j,但j++已经跟j关系不大了
这里先算后算的只是说他们的表达式返回的值的差别而不是说求值顺序,需要澄清。
(j++)+(j++),5+5=10,怎么解释。根据上面分析,先算两个j++ 再算+
注意:两个j++没有定义顺序,也就是没有序列点,j++后j的值在什么时候修改,
根据序列点的定义只要在进行+运算前修改就行了,可以在第一个j++后就修改,也可以等两个j++都算完后。看来编译器是放在+号之前
j++ 返回5 j还是5 再j++ 返回5 要算+了 产生副作用 j变成7
(j++)+(++j)也要重新解释:j++ 返回5 ++j 返回j自身,在算+号前 j产生副作用变为7 所以 +求解 为 5+7 = 12
(++j)+(++j)一样 算完++j后,在算+之前j = 7,而++j返回j自身,所以j+j=14
这是关于上面回帖的重新解释 那个有点粗糙,不好意思。
但是这里还有一个问题:我无法解释j+(j++)为什么会返回10,按我的理解,我觉得应该是j++返回5,j为6 应该是11