C: printf参数执行顺序与前置后置自增自减的影响
起源: 今天在了解副作用side-effect的过程中,看到了下面的网页,把我带到了由printf引起的一系列问题,纠结了一整天,勉强弄懂。
第一个代码没什么好解释的。而第二个printf("return of swap is %d\tx=%d,y=%d\n",swap(&x,&y),x,y)居然是"return of swap is 1 x=1,y=0",输出的x和y的值并没有改变!
原因在于C语言函数参数的处理是从右到左的压栈顺序(这个我在看第一个代码时还不知道,因为第一个代码从左往右操作的话,结果正好符合预期),但也跟编译器有关(这个下面再说)。
此时的①理解时:先处理y,y为0将0压入栈,再处理x,x为1将1压入栈内,最后处理swap,值为1将1压入栈内,所以输出的是110。
但我之后又浏览到了有关自增自减与printf的问题,
#include <stdio.h>
int main()
{
int x;
x=;
printf("%d %d\n",x,x++);
x=;
printf("%d %d\n",x++,x);
x=;
printf("%d %d %d\n",x,x++,x);
x=;
printf("%d %d %d %d\n",x,++x,x++,x);
return ;
}
按照①理解得出的答案应该是:
2 1
1 1
2 1 1
3 3 1 1
但GCC编译下来的结果是
2 1
1 2
2 1 2
3 3 1 3
为什么要特指GCC下的结果呢,因为在后来的浏览中,发现,不同的编译器会得出不同的结果(在第一个网页里有人就贴出了第一个代码在vc6.0下的结果,没重视,后来不断有这类的信息出现,自己重装了VC6.0才发现还真是不一样!)。
在VC6.0下的:
1 1
1 1
1 1 1
2 2 1 1
这更邪乎了,在vc6.0里后置符在printf里都不管用了。
经过查阅得知,在GCC下,是先处理好所有参数,然后push,在遇到后置符时会立即输出此时的值。
而VC6.0是处理好一个参数push一个且后置符在整条printf完成后才会+1。
以这条为例
x=;
printf("%d %d %d %d\n",x,++x,x++,x);
GCC下:
处理x,此时x为1。
处理x++:temp = x, x = x + 1此时x为2,temp为1
处理++x:x = x + 1,此时x为3
处理x,此时x为3
将x,temp,x,x压入栈,然后一次弹出。结果为3 3 1 3。但如果前置符是表达式中的一部分的话,则会输出此时的值进行计算,例如:++x, ++x + 3,x(x初值是1)则输出的是3 5 1而不是3 6 1
在VC6.0下:
处理x,此时x为1,压栈。
处理x++,此时x为1,压栈。
处理++x,此时x为2,压栈。
处理x,此时x为2,压栈。
依次弹出,输出结果为2 2 1 1,然后处理x++,此时x为3,即在后面加printf输出x的值会输出3。
但又有问题了,按照GCC的方式,则printf("return of swap is %d\tx=%d,y=%d\n",swap(&x,&y),x,y)因该是1 0 1啊,为什么x 和 y的值没有换呢,难道是因为指针?
而且我又发现在进行指针处理后,之后在prinf中只有在该变量自增自减处理之后的才遵守上面GCC的操作,而在该变量自增自减处理之前的该变量则输出当时的值。如:
#include <stdio.h>
int swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
return ;
} int main()
{
int x = , y = ;
printf("x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("%d %d %d\n", x, ++x, x);
return ;
}
照着上面GCC的处理,则printf("%d %d %d\n", x, ++x, x)应输出一样的值2 2 2,但实际在GCC编译后输出的值是2 2 1。
我是彻底搞混了,但最终的结论是:这跟编译器如何处理有关(有人说是C/C++中未定义的行为,所以不同编译器有不同的操作),且在实际中不应该写出这样的代码,重点是知道C中函数参数是从右到左的压栈顺序和自增自减是如何操作。
参考文章连接:
C语言初探 之 printf压栈顺序(printf("%d %d %d %d %d %d\n",a++, ++a, a++, ++a, a++, ++a ))
写给初学者的有关printf("%d,%d,%d,%d\n", ++a,a++,--aa--);之类的表述
C: printf参数执行顺序与前置后置自增自减的影响的更多相关文章
- pytest_前置后置
今天总结下pytest,pytest简直就是python自动化中的高富帅,各种操作,哈哈 这次总结主要涉及到了以下几点: 1.unittest中的setUp.tearDown.setUpClass.t ...
- printf的执行顺序
printf()函数的参数,在printf()函数读取时是从左往右读取的,然后将读取到的参数放到栈里面去, 最后读取到的就放在栈顶,处理参数的时候是从栈顶开始的,所以是从右边开始处理的. --prin ...
- 实现简单的AOP前置后置增强
AOP操作是我们日常开发经常使用到的操作,例如都会用到的spring事务管理.今天我们通过一个demo实现对一个类的某一个方法进行前置和后置的增强. //被增强类 public class PetSt ...
- C和C++区别——前置自增与后置自增
一.先看下面两段完全一样的代码块 /* test.cpp */ int main() { int a = 5; ++a = 7; printf("%d\n", a); return ...
- C++之前置自增与后置自增
关于前置自增与后置自增的区别我是参考这里:http://bbs.bccn.net/thread-454977-1-1.html 简单复述下,比如++x; 与 x++; 在C中,++x这个表达式的值为原 ...
- unittest的前置后置,pytest的fixture和共享机制conftest.py
Unittest setUp/tearDown setUp当中得到的变量,用self.xxx = value传递给测试用例 setUpClass/tearDownClass setupClass当中得 ...
- spring 切面 前置后置通知 环绕通知demo
环绕通知: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http:// ...
- AOP 环绕通知 集成了前置 后置 返回通知等功能
AOP 环绕通知 集成了前置 后置 返回通知等功能
- Spring Bean前置后置处理器的使用
Spirng中BeanPostProcessor和InstantiationAwareBeanPostProcessorAdapter两个接口都可以实现对bean前置后置处理的效果,那这次先讲解一下B ...
随机推荐
- 第1章 程序设计和C语言
1.1什么是计算机程序 程序,就是一组计算机能识别和执行的指令.每一条指令使计算机执行特定的操作.只要让计算机执行这个程序,计算机就会“自动地”执行各条指令,有条不紊地进行工作. 1.2什么是计算机语 ...
- Python爬虫入门教程 51-100 Python3爬虫通过m3u8文件下载ts视频-Python爬虫6操作
什么是m3u8文件 M3U8文件是指UTF-8编码格式的M3U文件. M3U文件是记录了一个索引纯文本文件, 打开它时播放软件并不是播放它,而是根据它的索引找到对应的音视频文件的网络地址进行在线播放. ...
- eclipse升级Android SDK Tool版本到25.2.5后运行项目报错Unable to build: the file dx.jar was not loaded from the SDK folder
概述 由于最近通过SDK-Manager更新了build-tools,当要用到dx.jar这个包时,自动调用最新版本Android SDK build-tools中dx.jar,但是运行android ...
- java8的函数式接口
函数式接口 就是在java8里允许你为一个接口(只有一个实现的,声明为FunctionalInterface注解的)实现一个匿名的对象,大叔感觉它与.net平台的委托很类似,一个方法里允许你接收一个方 ...
- 大名鼎鼎的红黑树,你get了么?2-3树 绝对平衡 右旋转 左旋转 颜色反转
前言 11.1新的一月加油!这个购物狂欢的季节,一看,已囊中羞涩!赶紧来恶补一下红黑树和2-3树吧!红黑树真的算是大名鼎鼎了吧?即使你不了解它,但一定听过吧?下面跟随我来揭开神秘的面纱吧! 一.2-3 ...
- Sentinel 流程分析
最近公司开始做新的项目.新项目准备用点新的技术.之前我们采用的是spring cloud的那一套.之前几个月看到阿里开始拥抱springcloud,推出好几个组件无缝兼容现有springcloud.我 ...
- .Net Framework项目引用.NetStandard标准库出现版本冲突解决办法
今天在工作中出现一个引用问题,害我找问题找了很久.起因是在一个Winform项目下需要引用一个.NetStandard标准库,标准库引用了System.ComponentModel.Annotatio ...
- sql server 生成数据库字典 sql语句
SELECT TOP 100 PERCENT --a.id, CASE WHEN a.colorder = 1 THEN d.name ELSE '' END AS 表名, ...
- PLSQL 登录出现 ora12638 credential retrieval failed
参考:http://blog.csdn.net/yjq8116/article/details/4203168 解决了 开始 -> 程序 -> Oracle -> Configura ...
- C# NuGet包管理命令
NuGet Package Manager Console 内置于 Visual Studio 在 Windows 2012 和更高版本. (不包含在 Visual Studio 用于 Mac 或 V ...