今天复习遇到一个有关赋值和自增的阴间问题……来记录一下,要不感觉过几天又要忘……
一、赋值操作的基本要求
大家都知道赋值操作的基本要求为赋值号的左边只能是一个变量,不能是式子,只有以下特例:
①形如i+=3、i*=5这类;
②形如++i=5这类勉强也可以(其实真正工作中这么写的人基本都有点问题,离他远点);这句++i=5的意思是,先让i进行一次自增,然后给i赋值为5……对,第一步白干了,这和直接赋值为5没什么区别。有一说一就算是面试题出这个写法也会被怀疑出题人是nt……
这里说一下,情况②能通过编译的原因是因为左自增优先执行,先于i当前的其他操作赋值执行,可以认为在赋值操作之前,左侧已经结束了自增操作,再次成为了一个变量,勉强可以跑得通;
但若是i++=5就跑不通了,因为右自增需要晚于i当前的其他操作执行,这就相当于左边是一个式子,不符合赋值操作的要求。
总结一下就是说,“++i=与i无关的常量;”的执行效果等同于直接“i=常量;”。
同理“++i=i”的执行效果等同于“++i; i=i;”,就相当于直接“++i;”
然后我遇到一个阴间操作“++i=i++;”……
二、i=i++;
++i=i++的操作无非是先执行一步++i,再执行一步i=i++,也就是说主要的阴间部分在后面这句i=i++上。
其实i=i++执行完之后 i 的值不变……不信的童鞋可以自己试试……
int i=1;
i=i++;
printf("i=%d",i);
很反直观是不是。
然后我找到一篇知乎大佬写的文章,虽然大佬写的是Java,不过内部原理和C语言一样:Java中i=i++的操作
看Java头大的童鞋也可以直接看我接下来要写的,应该能更加基础一些:
(一)、栈的基本概念
首先要清楚一点:在分析局部变量及其操作时,系统会自动使用一种叫“栈”的结构,这种结构的特点是像一个筐子一样,先放进去的东西压在最底下只能最后拿出来,后放进去的东西则在最顶上拿的时候最先出来——也就是先进后出,后进先出;系统内部运行代码,就是在这些变量进进出出的过程中适时地插入操作语句,从而实现代码要求的对这些变量进行的操作。
(二)、操作数栈
现在我写一句“int i=1;”,运行的时候系统会自动生成一个叫做“操作数栈”的栈,这个栈专门存各种变量的数值且只存数值而不管其内存地址什么的别的特征;也就是说此栈中的数值仅仅是复制了一遍变量的数值,是与变量本身相互独立的不同事物。i=i++不会使 i 的数值发生变化这件事情,产生原因就是这个栈与代码的运行没有完美地协调好。
运行int i=1;时操作数栈的操作为:
①开辟一块4字节的内存,起名为 i ;
②系统检查赋值号右侧,是一个数值,那就直接把这个数值存入栈中:
操作数栈为:
此时 i 未赋值
③然后进行赋值操作,把数值从栈中取出,赋给变量 i ;
此操作结束后 i=1,栈为空:
操作数栈为:
此时i=1
(三)、从操作数栈来看执行i=i++时的内部流程
假定我们已经结束了int i=1;的执行,现在i=1,在执行i=i++时:
①首先可知这句代码整体是个赋值语句,既然是赋值语句那就像上面那句int i=1一样,操作数栈会先检查赋值号右侧,发现右侧是i++,也就是先把 i 当前的值存入栈,再执行 i 的自增操作:
操作数栈为:
此时i=1
②此时赋值号右侧的 i 已经存入了栈,又由于自增符号的优先级高于赋值符号,接下来应该执行右侧这个 i 的自增操作。而如上面链接里知乎大佬所查,自增操作在系统内部的执行是直接在变量值上进行的,不会影响操作数栈及其内部的数据:
于是这一步自增操作结束后就变成了:
i=2
此时操作数栈为:
③最后进行赋值操作,系统从栈中取出数据,赋值给变量 i ……对的,白忙活了,又赋值回去了:
操作数栈为:
此时i=1
至此,i=i++的操作结束,i 的数值虽然被自增操作直接修改成2,但最后一步又把栈里面存着的1赋值回了 i ,白忙活了……所以出现了这个情况:
int i=1;
i=i++;
printf("i=%d",i);
可见i=i++执行完之后 i 的值不变这件反直观事件的原因就是因为自增操作和操作数栈是相互独立的,二者“没有协调好”,导致本来符合直观的数据反而被二次覆盖了。
(四)、i=++i
同理,如果是i=++i的话:
①由于是赋值语句,系统检查赋值号右侧,发现了左自增操作,于是优先执行自增操作:
i=2
此时操作数栈为:
②然后就是正儿八经的赋值操作了,直接右侧数据入栈:
操作数栈为:
此时i=2
③栈中数据被取出,赋值给变量i:
操作数栈为:
此时i=2
至此i=++i的执行分析完毕。
大家觉得不把握也可以写个代码测试一下:
int i=1;
i=++i;
printf("i=%d",i);
三、总结
赋值号左侧写左自增操作的人都多少沾点那啥……我只是做题遇到了,大家千万不要学……说实话即使是出题,这操作也算是贻笑大方……
顺便一提++i=i++执行完之后 i 是2,大家结合本文自己寻思寻思就行啦。代码和运行结果如下:
int i=1;
++i=i++;
printf("i=%d",i);