函数调用堆栈,调用约定

在这里插入图片描述
堆栈的具体作用:

1、传递参数(为被调用函数提供参数)
2、保存局部变量
3、保存中间变量
4、在系统中用堆栈保存任务的状态(例如各个寄存器的值)

1.什么是堆栈
编译器一般使用堆栈实现函数调用。堆栈是存储器的一个区域,嵌入式环境有时需要程序员自己定义一个数组作为堆栈。Windows为每个线程自动维护一个堆栈,堆栈的大小可以设置。编译器使用堆栈来堆放每个函数的参数、局部变量等信息。
函数调用经常是嵌套的,在同一时刻,堆栈中会有多个函数的信息,每个函数占用一个连续的区域。一个函数占用的区域被称作帧(frame)。(每个函数都占有一个帧区。就是为了区分开这个函数的框架)

2、编译器是从高地址开始使用堆栈。
在多线程(任务)环境,CPU的堆栈指针指向的存储器区域就是当前使用的堆栈。切换线程的一个重要工作,就是将堆栈指针设为当前线程的堆栈栈顶地址。
不同CPU,不同编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。

3、堆栈相关寄存器:
esp:堆栈指针(stack pointer),指向系统栈最上面一个栈帧的栈顶
ebp: 基址指针(base pointer),指向系统栈最上面一个栈帧的底部
cs:eip:指令寄存器(extended instruction pointer),指向下一条等待执行的指令地址
注:ebp在C语言中用作记录当前函数调用基址。

4、堆栈操作
push:以字节为单位将数据(对于32位系统可以是4个字节)压入栈,从高到低按字节依次将数据存入ESP-1、ESP-2、ESP-3、ESP-4的地址单元。
pop: 过程与PUSH相反。
call: 用来调用一个函数或过程,此时,下一条指令地址会被压入堆栈,以备返回时能恢复执行下条指令。
leave:当调用函数调用时,一般都有这两条指令:push %ebp mov %esp, %ebp 而,leave是这两条指令的反操作。
ret: 从一个函数或过程返回,之前call保存的下条指令地址会从栈内弹出到EIP寄存器中,程序转到CALL之前下条指令处执行。
注:
call指令的两个作用
①将下一条指令的地址A保存在栈顶
②设置eip指向被调用程序代码开始处

5、函数堆栈框架的形成
①执行call 程序之前
cs : eip原来的值指向call下一条指令,该值被保存到栈顶
然后cs : eip的值指向程序的入口地址
②进入程序
第一条指令: push % ebp //意为保存调用者的栈帧地址
第二条指令: mov % esp, %ebp //初始化程序的栈帧地址
然后函数体中的常规操作,可能会压栈、出栈
③退出程序
mov %ebp,%esp
popl %ebp
ret

6、函数调用约定
函数调用约定包括传递参数的顺序,谁负责清理参数占用的堆栈等,如下面这个主要的函数约定表显示的 :

函数调用约定 参数传递顺序 谁负责清理参数占用的堆栈
__pascal 从左到右 调用者
__stdcall 从右到左 被调函数
__cdecl 从右到左 调用者
调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。在Windows上,__cdecl是C/C++程序的缺省函数调用约定。在linux下gcc默认用的规则是__stdcall 。
在有的cpu上,编译器会用寄存器传递参数,函数使用的堆栈由被调函数分配和释放。这种调用约定在行为上和__cdecl有一个共同点:实参和形参数目不符不会导致堆栈错误。
不过,即使用寄存器传递参数,编译器在进入函数时,还是会将寄存器里的参数存入堆栈指定位置。参数和局部变量一样应该在堆栈中有一席之地。参数可以被理解为由调用函数指定初值的局部变量。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值