对i++,i--,++i,--i深刻认识以及printf()函数打印的过程
2017-10-23 11:55
267 查看
先来看一个例子:
有一部分人认为打印的结果应该是0,0,0.
下面是VS2013运行出来的结果:
大家心里肯定会有疑惑,为什么会是这样的结果?我们不妨先来分析一下。
大家都很清楚,printf()这个函数在打印参数内容时,是从右往左的,当有多个参数时(依次入栈的原因),printf()函数执行时是遍历一个参数打印一个参数呢?还是先遍历一遍参数然后在依次打印呢?大家有没有考虑过呢?
我们不妨假设printf()函数执行时遍历一个参数打印一个参数:
1、首先处理的是最右边的表达式i++,打印出其结果为0,而后i的值变为1;
2、接下来处理的是表达式–i,由于i的值已变为1,所以打印的结果为0,而后i的值变为0;
3、最后处理的是最左边的表达式i++,此时i值为0,所以打印的结果为0,而后i的值变为1。
通过上面的分析,可以看出打印的结果是0,0,0,可真的是这样吗?
我们先来看一下上述代码的汇编代码:
1、在红色框里,处理表达式i++时,先通过寄存器eax先将内存中i的值(为0)放入一个系统临时开辟的整型大小的地址空间(ptr [ebp-0D0h]开始的地址空间)中保存起来,之后通过寄存器ecx将内存中i的值加1再将加1后的结果放到i原来的内存中去,此时内存中i的值变为1;
2、红色框里的指令是用来处理表达式–i的,可以看出在处理表达式–i时,是直接通过寄存器edx将内存中i的值减1之后再将减1后的结果放到i原来的内存中去,此时i的值变为0.;
3、接下来处理最左边的表达式i++,可以看出红色框内的代码和处理最最右边的表达式i++的代码几乎一样,只是存放i值的地址不一样,这次是通过寄存器eax将i的值0存放到内存dword ptr [ebp-0D4h]开始的内存单元中。
接下来便是入栈了:
第一步是通过寄存器edx将内存ptr [ebp-0D0h]开始的连续四个地址空间的值入栈,该值为0;
第二步是将内存中i的值通过寄存器eax入栈,此时i的值为1;
第三步通过通过寄存器edx将内存ptr [ebp-0D4h]开始的连续四个地址空间的值入栈,该值为0。
之后系统将当前指令的下一条指令的地址入栈(现场保护),然后调用printf()函数依次打印上述表达式的结果,得到的结果是0,1,0
通过上面的分析,相信大家对i++,i–,++i,–i以及printf()函数打印的过程已经深刻理解了。
最后,总结一下:
对于前置++、–处理的时候系统会直接操作不产生临时量,只需三条代码;对于后置++、–处理的时候会先产生一个临时量,而后通过临时量返回结果,需要五条代码,所以在同样的环境下前置++或–要比后置++或–效率要高。
对于printf()函数的处理过程,我们可以看到,是先将参数从右到左遍历一遍,最后才执行打印操作,而不是遍历一个打印一个。
#inlcude <stdio.h> int main() { int i = 0; printf("%d,%d,%d\n", i++, --i, i++); }
有一部分人认为打印的结果应该是0,0,0.
下面是VS2013运行出来的结果:
大家心里肯定会有疑惑,为什么会是这样的结果?我们不妨先来分析一下。
大家都很清楚,printf()这个函数在打印参数内容时,是从右往左的,当有多个参数时(依次入栈的原因),printf()函数执行时是遍历一个参数打印一个参数呢?还是先遍历一遍参数然后在依次打印呢?大家有没有考虑过呢?
我们不妨假设printf()函数执行时遍历一个参数打印一个参数:
1、首先处理的是最右边的表达式i++,打印出其结果为0,而后i的值变为1;
2、接下来处理的是表达式–i,由于i的值已变为1,所以打印的结果为0,而后i的值变为0;
3、最后处理的是最左边的表达式i++,此时i值为0,所以打印的结果为0,而后i的值变为1。
通过上面的分析,可以看出打印的结果是0,0,0,可真的是这样吗?
我们先来看一下上述代码的汇编代码:
1、在红色框里,处理表达式i++时,先通过寄存器eax先将内存中i的值(为0)放入一个系统临时开辟的整型大小的地址空间(ptr [ebp-0D0h]开始的地址空间)中保存起来,之后通过寄存器ecx将内存中i的值加1再将加1后的结果放到i原来的内存中去,此时内存中i的值变为1;
2、红色框里的指令是用来处理表达式–i的,可以看出在处理表达式–i时,是直接通过寄存器edx将内存中i的值减1之后再将减1后的结果放到i原来的内存中去,此时i的值变为0.;
3、接下来处理最左边的表达式i++,可以看出红色框内的代码和处理最最右边的表达式i++的代码几乎一样,只是存放i值的地址不一样,这次是通过寄存器eax将i的值0存放到内存dword ptr [ebp-0D4h]开始的内存单元中。
接下来便是入栈了:
第一步是通过寄存器edx将内存ptr [ebp-0D0h]开始的连续四个地址空间的值入栈,该值为0;
第二步是将内存中i的值通过寄存器eax入栈,此时i的值为1;
第三步通过通过寄存器edx将内存ptr [ebp-0D4h]开始的连续四个地址空间的值入栈,该值为0。
之后系统将当前指令的下一条指令的地址入栈(现场保护),然后调用printf()函数依次打印上述表达式的结果,得到的结果是0,1,0
通过上面的分析,相信大家对i++,i–,++i,–i以及printf()函数打印的过程已经深刻理解了。
最后,总结一下:
对于前置++、–处理的时候系统会直接操作不产生临时量,只需三条代码;对于后置++、–处理的时候会先产生一个临时量,而后通过临时量返回结果,需要五条代码,所以在同样的环境下前置++或–要比后置++或–效率要高。
对于printf()函数的处理过程,我们可以看到,是先将参数从右到左遍历一遍,最后才执行打印操作,而不是遍历一个打印一个。
相关文章推荐
- 关于数据库存储过程和函数的区别,优缺点以及各家之言
- newlib 中的打印函数printf,sprintf及asprintf
- 通过编写一个程序,来说明字符数组 以及操作字符数组的函数的用法。该程序读入一组文本行,并把最长的文本行打印出来。
- oracle查看包存储过程,函数,以及存储过程参数,函数参数
- java中函数的认识以及格式-作者:逝秋
- printf_系统调用过程分析_write() putc() 函数实现
- C++反汇编第一讲,认识构造函数,析构函数,以及成员函数
- printf函数打印二进制
- 总结几种log打印printf函数的宏定义
- sigaction()函数的调用以及在调用过程中,未决集和阻塞集的变化情况
- 通过函数来实现数组的逆置以及数组的初始化,并能够打印出来
- Java 中的 int 与 Integer 用于 List<Integer> 时,以及通过打印变量检测程序运行和函数调用次数计数
- linux从上电到到启动流程简要过程以及关键函数
- Oracle存储过程、存储函数以及Java程序调用存储过程和存储函数
- C++类的静态成员函数在多线程的工作机制以及运行过程中强制结束线程实验
- mysql 导入导出数据库以及函数、存储过程
- [转]mysql 导入导出数据库以及函数、存储过程的介绍
- 黑马程序员—c语言基础—Printf函数的介绍以及注意事项
- 触发器、索引、存储过程以及函数
- STM32M CUBE实现printf打印调试信息以及实现单字节接收