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