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 ...
随机推荐
- 轻量级数据库Sqlite的使用
SqLite是什么? SQLite是一个进程内的库,实现了自给自足的.无服务器的.零配置的.事务性的 SQL 数据库引擎.它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置. 就像 ...
- 为什么有那么多人愿意喝"鸡汤"?
什么是心灵鸡汤 心灵鸡汤,就是“充满知识与感情的话语”,柔软.温暖,充满正能量.心灵鸡汤是一种安慰剂,可以怡情,作阅读快餐:亦可移情,挫折.抑郁时,疗效直逼“打鸡血”.这也是“心灵鸡汤”风靡不衰的原因 ...
- 使用 ASP.NET Core MVC 创建 Web API(三)
使用 ASP.NET Core MVC 创建 Web API 使用 ASP.NET Core MVC 创建 Web API(一) 使用 ASP.NET Core MVC 创建 Web API(二) 十 ...
- [.NET] 使用 Senparc.Weixin 接入微信公众号开发:简单实现自动回复
使用 Senparc.Weixin 接入微信公众号开发:简单实现自动回复 目录 一.前提 二.基本配置信息简析 三.配置服务器地址(URL) 四.请求处理 一.前提 先申请微信公众号的授权,找到或配置 ...
- iOS视频边下载边播放
随着视频行业的发展,很多用户对于观看体验也有了更高的要求,以前的习惯是下载好了在观看,而现在是希望1分钟都不要等,ZUI好一边看着一边下载,等把这个视频看完也下载完了,也就是我们常说的“视频边下载边播 ...
- vue实例化
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 使用Huginn抓取Discourse论坛
Hi! I don't know why the xpath does not work, but have an easier solution. Discourse also has a JSON ...
- 解决在圆角手机(如小米8)上自定义Dialog无法全屏的问题
在小米8等一系列圆角的手机上测试项目时,发现我的自定义dialog无法全屏了,这时我的dialog全屏的解决方案还是和网上大部分人是一样的 Window window = getWindow(); i ...
- Visual Studio插件开发基础
Visual Studio插件主要有两种:Add-in 和 VSX(Visual Studio eXtensibility) 两者区别可参考这篇文章:Visual Studio Extensions ...
- django 问题综合
orm部分 本篇文章我会持续更新,把开发中遇到的一切orm相关的问题都放在这里 mysql索引报错 使用django 的orm,数据库用的mysql,在使用makemigrations和migrate ...