在dll项目中,函数是这样的:
extern "C" __declspec(dllexport) void foo(const wchar_t* a, const wchar_t* b, const wchar_t* c)
Run Code Online (Sandbox Code Playgroud)
在另一个项目中,我将使用foo函数,但我foo在头文件中声明函数
extern "C" __declspec(dllimport) void foo(const wchar_t* a, const wchar_t* b)
Run Code Online (Sandbox Code Playgroud)
我只用两个参数调用它.
结果是成功,我认为这是关于__cdecl电话,但我想知道这是如何以及为什么这样做.
Jas*_*n C 32
默认调用约定是__cdecl,这意味着调用者从右到左将参数压入堆栈,然后在调用返回后清理堆栈.
所以在你的情况下,来电者:
此时堆栈看起来像这样(例如假设4个字节的指针,并且记住堆栈指针在你推动时向后移动):
+-----+ <--- this is where esp is after pushing stuff
| ret | [esp]
+-----+
| a | [esp+4]
+-----+
| b | [esp+8]
+-----+ <--- this is where esp was before we started
| ??? | [esp+12 and beyond]
+-----+
Run Code Online (Sandbox Code Playgroud)
太好了.现在问题发生在被叫方身上.被调用者期望参数位于堆栈上的某些位置,因此:
a 假设是在 [esp+4]b 假设是在 [esp+8]c 假设是在 [esp+12]这就是问题所在:我们不知道到底是什么[esp+12].因此,调用者将看到正确的价值观a和b,但会解释一切未知的垃圾恰好是在[esp+12]为c.
那时它几乎是未定义的,取决于你的功能实际上做了什么c.
在所有这些结束并且被调用者返回之后,假设您的程序没有崩溃,调用者将恢复esp并且堆栈指针将返回它应该的位置.所以从调用者的POV来看,一切都很好,堆栈指针最终回到原来的位置,但是被调用者看到了垃圾c.
64位计算机上的机制不同,但最终结果大致相同.Microsoft 在64位计算机上使用以下调用约定,无论其是__cdecl什么等等(您指定的任何约定都被忽略,并且所有约定都被视为相同):
rcx,rdx,r8,和r9以该顺序,左到右.xmm0,xmm1,xmm2,和xmm3,顺序,由左到右.esp以及在调用后恢复所有易失性寄存器的值.所以在你的情况下,来电者:
a在rcx.b在rdx.但被调用者期待:
a假设在rcx(检查!)b假设在rdx(检查!)c假设在r8(问题)因此,与32位的情况下,无论发生被叫方解释是在r8为c和潜在hijinks随之而来,与根据与被叫做什么最终效果c.当它返回时,假设程序没有崩溃,调用者恢复所有易失性寄存器(rcx以及rdx通常包括r8和朋友)并恢复esp.
| 归档时间: |
|
| 查看次数: |
1753 次 |
| 最近记录: |