碰到如题所述的问题,很尴尬,有些库函数可以正常使用,而有些一用就game over。还以为是库有问题。
调用接口函数时,崩掉???
根据网上同仁给出的解决方法,是在定义函数指针类型时添加一个_stdcall。但也存在一个缺陷,只能在Windows平台上帮助解决问题。如下所示
typedef _stdcall int (*ABOUT)();
那么问题来了,为什么加_stdcall修饰的函数,就可以呢?
这里涉及参数入栈问题。
从头来:假设一个函数: int fun(int a, int b);
当我们调用这个函数,如在main函数有如此调用 int res = fun(10,12); 在这里碰到一个问题:
对于参数的传递来说,计算机提供了一个被称为栈的数据结构来支持参数的传递。栈是一个先进后出的数据结构,和弹夹类似,所以有压栈的说法。
栈有一个栈区(存储区)、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(称为栈顶)。我们可以在栈顶 上方向栈中加入数据,这个操作称为压栈(Push)。压栈后栈顶自动变成新加入的数据项,同时栈顶指针也指向新加入项目的地址。 相反的操作称为出栈(Pop)。
在函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用后,在从堆栈中取得数据,并进行计算。
在函数调用结束时,或者由调用者或者由函数本身修改栈,是堆栈恢复原装。-----清理堆栈
在参数传递过程中,有两个很重要的问题必须明确说明:
1、当参数个数多于一个时,按照什么顺序把参数压入堆栈;——参数传递顺序
2、函数调用结束后,由谁来把堆栈恢复原状。——栈的维护者
因此,在高级语言中,通过函数调用约定来解决这两个问题。
何谓函数调用约定:描述参数是如何传递和由谁平衡堆栈的,以及返回值。
常见的函数调用约定有:
__stdcall __cdcel __fastcall thiscall
关键字 栈的维护者(平衡) 参数传递顺序
__cdcel 调用者 参数反序入栈(右-->左)
__stdcall 被调用函数 参数反序入栈(右-->左)
__fastcall 被调用函数 参数先存寄存器,接着入栈
thiscall(非关键字) 被调用函数 参数入栈,this指针存ECX
__stdcall调用约定更多的时候称为pascal调用约定,这是因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定就是__stdcall.
在Microsft C++系列的C/C++编译器中,常常用PASCAL宏来声明__stdcall这个调用约定,还有WINAPI和CALLBACK宏亦是。
int __stdcall fun(int a, int b);也就是b先入栈,在a入栈。 由__stdcall产生的名字修饰是在函数名前加下划线_,并在其后加"@"和函数参数字节数
本例就是 _fun@8 (8表示所需的栈空间)
-----看汇编VC反汇编-----
在main函数 类似于中断的现场保护
@ILT+0(_Max):
00401015 E9 26 00 00 00 jmp _fun (00401030) 在函数前加_
__cdcel调用约定,又称为C调用约定,是C语言的缺省的调用约定。
int fun(int a, int b); //不加修饰就是C调用约定 ==== int __cdcel fun(int a, int b);
C语言中默认参数压栈顺序是从右到左,和__stdcall相同。不同的是,函数本身不清理堆栈,而是有调用者来清理堆栈。————————由于这种变化,C调用约定允许函数的参数个数是不固定的,这也是C语言的一大特色。
在C++中,可以在函数声明或定义时用关键字__stdcall来指定调用约定。
__stdcall调用约定经常在Windows程序或API函数中使用。