先看一个例题:
#include
int main(){
int a=1;
printf("%d, %d, %d\n", ++a, a, a++);
return 0;
}
大家可以思考一下?输出结果是什么? 2,2,2? 3,2,1? 3,3,1?
正确输出结果是:3,3,1
分析:
1、++a表示先把a+1,然后再用;a++表示先用a后把a+1,
2、printf()语句多个参数的执行顺序,从右往左
3、从右往左:a++,先a=1,然后a+1,这时a变成2,++a,a先加1,a=3,打印出来:3,3,1。中间的a是++a之后的值即是3。
编译的时候从右向左,输出的时候从左往右。
为什么参数从右至左压栈? (1)可以动态变化参数个数。通过栈堆分析可知,自左向右的入栈方式,最前面的参数被压在栈底。这样的话,除非知道参数个数,否则是无法通过栈指针的相对位移求得最左边的参数。这样就变成了左边参数的个数不确定,正好和动态参数个数的方向相反。 (2)更符合习惯。 采用这种顺序,是为了让程序员在使用C/C++的“函数参数长度可变”这个特性时更方便。 什么是“函数参数长度可变”?printf就是一个例子,它的参数的个数就是可变的。比如,printf(“%d %d %d”,1,2,3),在采用从右向左的参数入栈顺序时,参数出栈顺序时"%d %d %d",1,2,3。如果采用从左向右的入栈顺序,则出栈顺序变为3,2,1,“%d %d %d”,这样就不能提前知道会有多少个参数
#include<stdio.h>
int main(){
int a;
a=1; printf("%d %d\n",a,a++);
a=1; printf("%d %d\n",a++,a);
a=1; printf("%d %d %d\n",a,a++,a);
a=1; printf("%d %d %d %d\n",a,++a,a++,a);
return 0;
}
注:只要是输出a,一定是加完之后输出的a
输出结果:
2 1
1 2
2 1 2
3 3 1 3
底层原理: 主要是因为栈(压栈),栈是先进后出,后进先出。 C函数的参数压栈顺序是从右到左,printf和scanf函数都是,采用压栈从右到左的原因如下: printf函数的原型是:
printf(const char* format,…)
它是一个不定参函数,我们在实际使用中是怎么样知道它的参数个数呢?这就要靠format了,编译器通过format中的%占位符的个数来确定参数的个数。
现在我们假设参数的压栈顺序是从左到右的,这时,函数调用的时候,format最先进栈,之后是各个参数进栈,最后pc进栈,此时,由于format先进栈了,上面压着未知个数的参数,想要知道参数的个数,必须找到format,而要找到format,必须要知道参数的个数,这样就陷入了一个无法求解的死循环了。
而如果把参数从右到左压栈,函数调用时,先把若干个参数都压入栈中,再压format,最后压pc,这样一来,栈顶指针加2便找到了format,通过format中的%占位符,取得后面参数的个数,从而正确取得所有参数。
所以,如果不存在这种不定参的函数,则参数的压栈顺序无论是从左到右还是从右到左都是没关系的。
函数有多个参数时计算总得有个顺序吧?不是从左至右,就是从右至左,抑或从中间向两边;一句话选定一个顺序后就“大家都这么办”,总不能有些函数从左至右,有些函数从右至左,那编译器就太难做了。当初选择从右至左肯定是这样有好多方便之处,比如printf中的参数表,由于C是基于栈操作的,栈又是后进先出的,从右至左计算压栈,然后按弹出顺序输出到屏幕上刚好顺应了大多数文本从左至右的习惯,很是方便;若从左至右计算压栈,处理就没有这么方便了。
|