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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhejfl/article/details/79559365

碰到如题所述的问题,很尴尬,有些库函数可以正常使用,而有些一用就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 10
00401093 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函数中使用。







猜你喜欢

转载自blog.csdn.net/zhejfl/article/details/79559365
今日推荐