编译器一般使用堆栈实现函数调用。
堆栈是存储器的一个区域,嵌入式开发环境有时需要程序员自己定义一个数组作为堆栈。Windows为每个线程自动维护一个堆栈,堆栈的大小可以自己设置。编译器使用堆栈来存放每个函数的参数,局部变量等信息。
由于函数经常被嵌套,在同一时刻堆栈中会存储多个函数的信息,每个函数又占用一个连续的区域,一个函数占用的区域通常被称为帧(frame)。编译器是从高地址开始使用堆栈的。
在多线程任务环境中,CPU的堆栈指针指向的存储器区域就是当前使用的堆栈。切换线程的一个重要任务就是将堆栈指针设置为当前线程的堆栈栈顶指针。不同CPU,编译器的堆栈布局、调用方法可能不同,但是堆栈的基本概念都是一样的。
当一个函数被调用时,进程内核对象为其在的进程地址空间的堆栈部分分配一定的栈内存给该函数使用,函数堆栈用于:
- 1)在进入函数之前,保存“返回地址”和环境。返回地址是指该函数结束之后,从进入该函数之后的地址。
- 2)在进入函数之后,保存实参或实参复制、局部变量
函数原型
[连接规范] 函数类型 [调用约定] 函数名 参数列表{}
调用约定
调用约定是决定函数参数或实参复制进入和退出函数堆栈的方式以及函数堆栈释放的方式。在win32下,有以下四种调用方式:
1)_cdcel
它是C/C++默认的调用方式。实参是以参数列表依次从右向左入栈,出栈相反,函数堆栈由调用方来释放。主要用于带有可变参数的函数上。由于每个调用方都需要包含清空堆栈的代码,所以产生的可执行文件的大小大于_stdcall函数。
2)_stdcall
它是WIN API的调用约定,其实COM接口等只要是申明定义接口,都需要显示指定其调用约定为_stdcall。实参是以参数列表依次从右向左入栈,出栈相反,函数堆栈由被调用方自己来释放。但是,若函数含有可变参数,即使显示指定_stdcall,编译器也会自动将其变为_cdcel。
3)_thiscall
他是类的非静态成员函数默认的调用约定,不能在含有可变参数的函数上使用,否则会编译错误。实参是以参数列表依次从右向左入栈,出栈相反,函数堆栈由被调用方自己来释放。类的非静态成员函数内部都隐含一个this指针,该指针直接存放在CPU寄存器上。
4)_fastcall
快速调用。它们的实参并不是存放在函数堆栈上,而是直接存放在CPU寄存器上,所以不存在入栈、出栈、函数堆栈释放。