C语言函数参数压栈顺序为何是从右到左?(从左向右的话,碰到printf的会陷入死循环)

上学期学习了汇编语言,并在操作系统实验中使用了汇编+C语言混合编程,中间也了解了一些C语言与汇编语言的对应关系。
由于汇编语言是底层的编程语言,各种函数参数都要直接控制栈进行存取,在混合编程中,要用汇编来调用C函数,当然就要知道参数的压栈情况了。
当知道C函数的参数压栈顺序是从右到左时,我觉得很奇怪,因为大多数情况下,人们的习惯是从左到右的,难不成设计者学咱们中国古代写字从右到左的习惯不成?
当时只是记下了这个规则而已,并没有去探究这其中的缘由,后来在实验中自己用汇编实现了printf和scanf函数的基本功能,尝到了压栈从右到左的好处,但是仍然没有多想。
直到前阵子看斯坦福大学公开课的时候,老师解释了一番才恍然大悟!
那么,这是为什么呢?
要回答这个问题,就不得不谈一谈printf()函数,printf函数的原型是:printf(const char* format,…)
没错,它是一个不定参函数,那么我们在实际使用中是怎么样知道它的参数个数呢?这就要靠format了,编译器通过format中的%占位符的个数来确定参数的个数。
现在我们假设参数的压栈顺序是从左到右的,这时,函数调用的时候,format最先进栈,之后是各个参数进栈,最后pc进栈,此时,由于format先进栈了,上面压着未知个数的参数,想要知道参数的个数,必须找到format,而要找到format,必须要知道参数的个数,这样就陷入了一个无法求解的死循环了!!
而如果把参数从右到左压栈,情况又是怎么样的?函数调用时,先把若干个参数都压入栈中,再压format,最后压pc,这样一来,栈顶指针加2便找到了format,通过format中的%占位符,取得后面参数的个数,从而正确取得所有参数。
所以,如果不存在…这种不定参的函数,则参数的压栈顺序无论是从左到右还是从右到左都是没关系的。

http://m.blog.csdn.net/article/details?id=47381597

时间: 2024-12-29 09:44:19

C语言函数参数压栈顺序为何是从右到左?(从左向右的话,碰到printf的会陷入死循环)的相关文章

函数参数压栈,栈帧ebp,esp怎样移动的?

压栈一次esp-4,ebp不变 esp是栈顶指针寄存器,堆栈操作只和esp有关比如有一个函数a,有两个参数,一般是这样的PUSH 1 参数2压栈,esp-4PUSH 2 参数1压栈,esp-4CALL a 调用 a:PUSH EBP 保存ebpMOV EBP,ESP 改变栈帧,以后访问参数通过ebp,访问局部变量通过espSUB ESP,8 分配局部变量空间 ...ADD ESP,8POP EBP 恢复ebpRETN 8 返回,esp+8 C语句对应汇编语句: 例如函数: int aaa(int

C语言函数调用参数压栈的相关问题

参数入栈的顺序 以前在面试中被人问到这样的问题,函数调用的时候,参数入栈的顺序是从左向右,还是从右向左.当时没有想清楚,随口就说从右向左.其实这个回答是不完全正确的.因为其实入栈的顺序,不同的体系架构是不一样的,举例来说, 看下面的代码: #include <stdio.h> int test(int a, int b) { printf("address of a %x.\n", &a); printf("address of b %x.\n"

C语言中函数参数入栈的顺序 - Fangzhen - 博客园

.wiz-todo, .wiz-todo-img {width: 16px; height: 16px; cursor: default; padding: 0 10px 0 2px; vertical-align: -10%;-webkit-user-select: none;} .wiz-todo-label { display: inline-block; padding-top: 7px; padding-bottom: 6px; line-height: 1.5;} .wiz-todo

函数参数的压栈顺序

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu 转载请标明来源) 先来看一道面试题: 设int arr[]={1,2,3,4 }; int *ptr=arr; printf("%d,%d",*ptr,*(++ptr)); 面试题的答案是: 2, 2 这个面试题为什么会这样呢? 原因就跟函数的压栈顺序有关,先压栈哪一个,哪一个就会被先计算,后压栈哪一个,那一个就会被后计算. 对于常见的C++程序,像缺省_cdecl或使用_stdcall的函数压栈顺序都

汇编对比函数参数进堆栈顺序

1.C语言函数参数是按照倒序进栈,即函数调用时,最右边的参数最先压栈,由调用者恢复堆栈指针. 2.Pascal语言函数约定和C约定正好相反,它规定参数是从左向右传递,由被调用者恢复堆栈. 3. __stdcall 这是一种函数调用方式. __stdcall方式函数的参数压栈顺序从右到左,是Pascal 缺省调用方式,通常用于win32 API中,自己在退出时清空栈. __stdcall将参数压栈是按C语言的顺序(从右到左),但与C语言不同的是它是由被调用者将参数从栈中清除,所以它的编译文件比_c

函数压栈顺序 -- C

基础知识: 一个程序的运行起来后,其在内存中有5个区域 1. 程序代码区 这个很简单,代码要执行,肯定要加载进内存, 我们不必关心. 2. 文字常量区 一般我们这样定义一个字符串时,其是在文字常量区的: char* s1 = "hello, world"; char* s2 = "hello, world"; if(s1 == s2) printf("s1和s2指向同一个在文字常量区的字符串"); 这里, s1和s2指向的是同一个字符串 3. 静

Swift 1.1语言函数参数的特殊情况本地参数名外部参数名

Swift 1.1语言函数参数的特殊情况本地参数名外部参数名 7.4  函数参数的特殊情况 声明定义有参函数时,为函数的每一个参数都定义了参数名称.根据参数名定义的形式不同,函数参数包括本地参数和外部参数名两种本文选自swift入门很简单. 7.4.1  本地参数名 本地参数名就是定义函数名时,在参数列表中所写的参数名,它只可以在函数主体内使用.如以下的一个代码片段,它定义了一个函数名为fun的函数,在此函数的参数列表中定义的就是本地参数名文选自swift入门很简单. func fun(star

printf压栈顺序之 i++ 及 ++i

i++ 与 ++i 的不同 1 i=1; printf("%d %d\n",i,i++); //结果 2 1 2 i=1; printf("%d %d\n",i++,i); //结果 1 2 3 i=1; printf("%d %d %d\n",i,i++,i); //结果 2 1 2 4 i=1; printf("%d %d %d %d\n",i,++i,i++,i); //结果 3 3 1 3 解析: 已知前提: prin

C/C++---printf/cout 从右至左压栈顺序实例详解

__cdecl压栈顺序实例 明白计算:计算是从右到左计算的 栈和寄存器变量:x++,是将计算结果存放到栈空间,最后是要出栈的:而++x和x是将计算结果直接存放到某个寄存器变量中(是同一个),所以计算完最后输出时,++x和x的结果总是相同的. ??用个小例子来说明下: int x=5; printf("%d %d\n",x,x++); int y=5; printf("%d %d\n",y++,y); int z=5; printf("%d %d %d\n&