1.map value可重复
2. 运行时错误
The value of ESP was not properly saved across a function call.
This is usually a result of calling a function declared
with one calling convention with a function pointer declared with
a different calling convention
错误的原因是声明相应的函数未匹配。函数定义的调用规则,和实际的调用规则不同
极有可能是其中一个函数指针有问题
编译器默认是__cdecl,而函数指针是由void*传进去的__stdcall方式,由于
编译时不会报错,结果出现运行时错误。
3. __stdcall和__cdecl
函数调用时需要用到栈,当函数调用完成后,栈需要清除,这里就是关键问题
如何清除??
使用__cdecl,那么栈的清除工作是由调用者完成,这样带来一个棘手的问题
不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?
答案是不能
使用__stdcall,上边的问题就解决了,函数自己决绝清除工作
所以在跨平台开发中,我们都是用__stdcall
那问什么还需要__cdecl呢??当遇到函数如fprintf他的参数时可变的
不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常进行
这种情况只能使用__cdecl。
结论:如果程序中没有涉及可变参数,最好使用__stdcall
4.__stdcall
在C语言中,假设我们有这样的一个函数:
int function(int a,int b)
调用时只要用result = function(1,2)这样的方式就可以使用这个函数
但是,当高级语言被编译成计算机可识别的机器码时,有一个问题就凸显出来
在cpu中,计算机没办法知道一个函数需要多少个、什么样的参数,也没有硬件可以保存这些参数
也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由调用者和函数本身来协调
为此,计算机提供了一种被称为栈的数据结构来支持参数传递
栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改
函数调用时,调用者依次 把参数 压栈,然后调用函数,函数被调用以后,在堆栈中
取得数据,并进行计算。函数计算结束后,或者调用者或者函数本身修改堆栈,时堆栈恢复原状
5.
在参数传递中,有两个很重要的问题必须得到明确说明:
当参数个数多于一个时,按照什么顺序把参数压入堆栈
函数调用后,由谁来把堆栈恢复原装
在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:
stdcall的调用约定意味着:1)参数从右向左压入堆栈,2)函数自身修改堆栈 3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
基本概念:
(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。