您的位置:首页 > 编程语言 > Qt开发

关于Qt动态加载dll时,函数指针取地址OK,使用时却有问题

2018-03-15 22:28 495 查看
碰到如题所述的问题,很尴尬,有些库函数可以正常使用,而有些一用就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函数        类似于中断的现场保护

0040108F 6A 03 push 11                        //前后两个操作,可以看出参数是从右向左传递,参数也被扩展为一个字(4个字节)。00401091 6A 02 push 1000401093 E8 81 FF FF FF call @ILT+20(_Max) (00401019)   //这是函数调用操作。在进行call操作之后,会自动将call的下一条语句作为FC mov dword ptr [ebp-4],eax    //由寄存器eax带回返回值                           //函数的返回地址保存在栈中——即地址00401014。     @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函数中使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: