先看个例子:
代码语言:javascript复制int i=1;
printf("%d====%d", i,i );
输出结果:3====1
是不是很意外,我开始也很意外。。。请看解析。
解析:
C语言中, i
表示先运算后赋值,i
表示先赋值后运算。这个知识点相信只要会点编程的人都知道。
而C语言中,printf
中自增自减运算符却有另一片天地。其实上面例子如果将C语言代码换成汇编语言,能清晰的看出来代码的执行流程,只是放出来汇编代码怕是不懂汇编的就更懵了。
所以这里我就不放汇编了,直接用最通俗的方式记录我的理解。
代码语言:javascript复制// 在printf中,运算规则变为从右向左,输出规则为从左向右
/* 运算部分 */
// 由于运算是从右向左
i // 由于i 是先赋值后运算,会先将1赋值,最后这个位置输出肯定是1,运算后i=2
i // 上一步运算后i=2, i是先运算后赋值,所以i=3。下面开始输出
/* 输出部分 */
// 输出是从左向右
i // 上面已经经过运算,所以这里输出i=3
i // 上面也说了,i 是先赋值后运算,所以这里输出为上面最初运算到这里的值i=1
// 所以输出3,1
PC端观看代码注释可能会更舒服一点。
再看一个例子:
代码语言:javascript复制int i=1;
printf("%d====%d====%d",i , i, i);
解析过程:
代码语言:javascript复制/* 运算部分,从右向左*/
i // 先运算后赋值,i=2=i 1
i // 还是先运算后赋值,i=3=i 1
i // 先赋值后运算,所以输出i=3,再运算i=4=i 1
// 运算后的值为 i=4
/* 输出部分,从左向右*/
i // 运算之前,i=3,所以输出3
i // 此时i=4,输出当前值 i=4
i // 此时i=4,输出当前值 i=4
其实,如果运算过程中,遇到i
这样需要先赋值后运算的情况,编译器会将运算前的值存储在寄存器中,以便在运算完成之后运行输出,所以后面输出的其实是寄存器中之前存储下来的值。
而像 i
这样先运算后赋值的情况则无需寄存器来保存运算之前的值,因为运算之前的值保存下来毫无意义,它会输出运算之后的值。
i--
和--i
同理。
扩展汇编代码:
i
的汇编代码:
movl -4(%rbp), �x // 将i赋值给ax寄存器, ax=5
leal 1(%rax), �x // 将ax寄存器的值加1赋值给dx寄存器, dx=ax 1=6
movl �x, -4(%rbp) // 将dx寄存器的值赋值给i, i=dx=6
i
的汇编代码:
addl $1, -4(%rbp) // 将i的值增加1赋值给i, i=6