c语言函数调用压栈,C语言函数调用压栈顺序

C语言函数调用栈

程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。

公司专注于为企业提供成都网站建设、网站制作、微信公众号开发、成都商城网站开发,微信平台小程序开发,软件按需网站制作等一站式互联网企业服务。凭借多年丰富的经验,我们会仔细了解各客户的需求而做出多方面的分析、设计、整合,为客户设计出具风格及创意性的商业解决方案,创新互联更提供一系列网站制作和网站推广的服务。

不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。

寄存器是处理器加工数据或运行程序的重要载体,用于存放程序执行中用到的数据和指令。因此函数调用栈的实现与处理器寄存器组密切相关。

AX(AH、AL):累加器。有些指令约定以AX(或AL)为源或目的寄存器。输入/输出指令必须通过AX或AL实现,例如:端口地址为43H的内容读入CPU的指令为INAL,43H或INAX,43H。目的操作数只能是AL/AX,而不能是其他的寄存器。 [5]

BX(BH、BL): 基址寄存器 。BX可用作间接寻址的地址寄存器和 基地址寄存器 ,BH、BL可用作8位通用数据寄存器。 [5]

CX(CH、CL):计数寄存器。CX在循环和串操作中充当计数器,指令执行后CX内容自动修改,因此称为计数寄存器。 [5]

DX(DH、DL):数据寄存器。除用作通用寄存器外,在 I/O指令 中可用作端口 地址寄存器 ,乘除指令中用作辅助累加器。 [5]

2.指针和 变址寄存器

BP( Base Pointer Register):基址指针寄存器。 [5]

SP( Stack Pointer Register): 堆栈指针寄存器 。 [5]

SI( Source Index Register):源变址寄存器。 [5]

DI( Destination Index Register):目的变址寄存器。 [5]

函数调用栈的典型内存布局如下图所示:

图中给出主调函数(caller)和被调函数(callee)的栈帧布局,"m(%ebp)"表示以EBP为基地址、偏移量为m字节的内存空间(中的内容)。该图基于两个假设:第一,函数返回值不是结构体或联合体,否则第一个参数将位于"12(%ebp)" 处;第二,每个参数都是4字节大小(栈的粒度为4字节)。在本文后续章节将就参数的传递和大小问题做进一步的探讨。 此外,函数可以没有参数和局部变量,故图中“Argument(参数)”和“Local Variable(局部变量)”不是函数栈帧结构的必需部分。

其中,主调函数将参数按照调用约定依次入栈(图中为从右到左),然后将指令指针EIP入栈以保存主调函数的返回地址(下一条待执行指令的地址)。进入被调函数时,被调函数将主调函数的帧基指针EBP入栈,并将主调函数的栈顶指针ESP值赋给被调函数的EBP(作为被调函数的栈底),接着改变ESP值来为函数局部变量预留空间。此时被调函数帧基指针指向被调函数的栈底。以该地址为基准,向上(栈底方向)可获取主调函数的返回地址、参数值,向下(栈顶方向)能获取被调函数的局部变量值,而该地址处又存放着上一层主调函数的帧基指针值。本级调用结束后,将EBP指针值赋给ESP,使ESP再次指向被调函数栈底以释放局部变量;再将已压栈的主调函数帧基指针弹出到EBP,并弹出返回地址到EIP。ESP继续上移越过参数,最终回到函数调用前的状态,即恢复原来主调函数的栈帧。如此递归便形成函数调用栈。

EBP指针在当前函数运行过程中(未调用其他函数时)保持不变。在函数调用前,ESP指针指向栈顶地址,也是栈底地址。在函数完成现场保护之类的初始化工作后,ESP会始终指向当前函数栈帧的栈顶,此时,若

x86上为什么C语言调用一个函数要先把参数压栈,之后才是返回地址

调用函数时,实际上是从某一函数跳转到了被调用函数,这个时候,程序当前运行时所用的参数如果不进行保存的话,那么当执行完被调用函数跳转回原先的函数时,缺少必要的参数,程序就无法正常执行。因此,在调用函数之前,需要先保存现场信息,即先把参数压栈,然后再返回地址

C语言中函数参数压栈方式为什么是从右到左

栈是先入后出的数据结构.

函数参数从右到左, 那么到函数内部出栈的时候就是从左到右的顺序了.

对于普通函数无区别. 但对于可变参函数, 会根据左侧参数来决定共计有多少参数, 每个类型是什么.

比如 printf scanf这类的.

于是 就设计成从右到左的压栈方式了.

c语言中函数递归调用中的压入堆栈是什么意思啊

我通俗点说:栈这个东西,你可以想象成手枪的弹夹,你装入子弹后,最后装入的子弹一定是最先打出去的,对吧?这就是栈的先进后出原则。递归函数,你想想,一定是最后执行的函数最先执行完,然后执行倒数第二个...以此类推。压入栈的意思就是现在内存给你一个弹夹,函数就是你的子弹,你把没运行一次这个函数,就是向弹夹里装了一个子弹,这就是压栈。出栈就是当找到递归函数出口后,再倒着一个一个解决刚才一大堆的函数,就像手枪吧子弹一个一个在打出去。关于栈,建议你学学汇编,一下就明白了

什么是堆和栈?函数压栈是怎么回事?

什么是堆和栈?

一个由c/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放

4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

函数压栈是怎么回事?

函数压栈的本质是参数传递

这又跟汇编语言连系起来了.汇编语言的过程即proc可以理解成函数

比如一个最简单的计算两数之和函数

如果用汇编来写估计是这样的

sub proc

pop ax ;从stack取a 并放在AX寄存器中

pop bx ;从stack取b 并放在BX寄存器中

add ax,bx ; 计算a+b

ret //返回

sub endp

显然要调用这个函数,你应当先把b值push进stack,然后再push a

因为stack是先进后出的

所以调用汇编像这样

比如计算4+5

push 5;

push 4;

call sub; //返回值在AX中

在这个例子中先压5或先压4得到的结果没有变化

但大多数程序,如果参数的顺序错误将是灾难性的

因为不管什么高级语言最终都要编译成汇编语言,然后是机器语言

同样下面这个C程序,计算a+b值,必然会编译成上面的汇编代码

int sub(int a ,int b) {return a+b;}

所以C在调用这个函数sub时,必须要压栈(即传入参数)但这些工作,在C语言里,并不需要你来完成.你只要写出

sub(7,9);

编译器在编译成汇编时就会自动完成相关的压栈工作.

根据函数调用方式和参数压入顺序目前存在三种约定:

stdcall

cdecl

fastcall

这都相关压栈顺序和栈的清理工作约定

他们的细节都不相同,但有一点是肯定的,参数比须从右向左压入栈中

stdcall中 函数必须自已清理栈

cdecall 由调用者清除堆栈 C的默认函数调用方式 所以这样C支持可变参数

fastcall 是把函数参数列表的前三个参数放入寄存器eax,edx,ecx,其他参数压栈

源代码:

int function(int a, int b)

{

return a + b;

}

void main()

{

function(10, 20);

}

1.__cdecl

_function

push ebp

mov ebp, esp

mov eax, [ebp+8] ;参数1

add eax, [ebp+C] ;加上参数2

pop ebp

retn

_main

push ebp

mov ebp, esp

push 14h ;参数 2入栈

push 0Ah ;参数 1入栈

call _function ;调用函数

add esp, 8 ;修正栈

xor eax, eax

pop ebp

retn

2.__fastcall

@function@8

push ebp

mov ebp, esp ;保存栈指针

sub esp, 8 ;多了两个局部变量

mov [ebp-8], edx ;保存参数 2

mov [ebp-4], ecx ;保存参数 1

mov eax, [ebp-4] ;参数 1

add eax, [ebp-8] ;加上参数 2

mov esp, ebp ;修正栈

pop ebp

retn

_main

push ebp

mov ebp, esp

mov edx, 14h ;参数 2给EDX

mov ecx, 0Ah ;参数 1给ECX

call @function@8 ;调用函数

xor eax, eax

pop ebp

retn

3.__stdcall

_function@8

push ebp

mov ebp, esp

mov eax, [ebp] ;参数 1

add eax, [ebp+C] ;加上参数 2

pop ebp

retn 8 ;修复栈

_main

push ebp

mov ebp, esp

push 14h ;参数 2入栈

push 0Ah ;参数 1入栈

call _function@8 ;函数调用

xor eax, eax

pop ebp

retn


当前标题:c语言函数调用压栈,C语言函数调用压栈顺序
浏览地址:http://myzitong.com/article/dscjech.html