【问题标题】:Is the leading underscore for the function names for the __stdcall and __cdecl calling conventions optional?__stdcall 和 __cdecl 调用约定的函数名称的前导下划线是可选的吗?
【发布时间】:2018-01-18 05:47:52
【问题描述】:

__stdcall__cdecl 调用约定规定函数名称前面必须有一个下划线:

C (__cdecl)

同样的限制适用于 32 位世界和 16 位世界。 参数从右向左推送(这样第一个 参数最接近栈顶),调用者清理 参数。 函数名称由前导下划线修饰

__stdcall

这是用于 Win32 的调用约定,但以下情况除外 可变参数函数(必须使用 __cdecl)和极少数 使用 __fastcall 的函数。参数从右推到 离开 [上午 10:18 更正],被调用者清理堆栈。 功能 名称由前导下划线和尾随@符号装饰 后跟函数采用的参数的字节数。

我在 Visual C++ 中编译了以下代码(源文件的扩展名为.c,所以我猜源文件被编译为C 文件):

void __stdcall stdcallFunction(int i)
{
    int j = 12345;
}

void __cdecl cdeclFunction(int i)
{
    int j = 12345;
}

int main()
{
    stdcallFunction(123);

    cdeclFunction(123);

    return 0;
}

我注意到编译后的目标文件中的函数名没有前导下划线:

__stdcall__cdecl 调用约定的函数名称的前导下划线是可选的吗?

【问题讨论】:

  • 根据 MS 文档,在 64 位环境中,函数不会被修饰。
  • 它不是可选的,它是一个链接实现细节。声明和函数定义不匹配的故障恢复。否则,由于堆栈不平衡,这种不匹配会导致非常严重的运行时错误。

标签: c calling-convention


【解决方案1】:

名称装饰与调用约定没有直接关系。对于调用约定,调用者和被调用者使用相同的约定很重要,如果您在声明中提供正确的属性,编译器可以确保它们这样做。

所以名称装饰只是常规。有时能够从修饰名称推断出如何调用函数很有用。因此,在从库中公开函数时遵循约定是一种很好的做法。我假设如果你 dllexport 这些函数并将你的代码链接到 DLL 中,你最终会得到修饰名称。

【讨论】:

    【解决方案2】:

    函数装饰不是 C 标准的一部分。

    它是由所使用的开发环境定义的 ABI(抽象二进制接口)中定义的东西。

    具体问题中报告的规则是MS使用的规则。

    默认情况下,MS 编译器不使用 C 修饰(修饰),除非函数包含在 EXTERN 'C' { ... } 块中,或者使用了特定的编译器开关(通常是 -TC)。

    而且,正如在另一个答案中已经提到的,64 位代码的 MS ABI 使用不包含下划线前缀的不同 C 修饰。

    【讨论】:

    • 我注意到一个奇怪的事情,当使用 Visual C++ IDE 创建目标文件时,目标文件中的函数名不会有前导下划线。但是当从命令行(cl /c main.c)创建目标文件时,函数名会有一个前导下划线!
    • 从项目属性中检查设置了哪些开关 ;-)
    • 这些是开关:/Zi /nologo /W3 /WX- /Od /Oi /Oy- /GL /D "_MBCS" /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Release\1.pch" /Fa"Release\" /Fo"Release\" /Fd"Release\vc100.pdb" /Gd /analyze- /errorReport:queue。你知道哪个开关负责在函数名前不加下划线吗?
    • 这些都不应该对装饰产生影响。无论如何如果你正在编译 64 位,你永远不会得到下划线前缀,因为 X64 ABI 不允许它。
    猜你喜欢
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    • 2011-08-24
    • 2018-05-31
    • 2023-03-19
    • 2015-09-21
    • 2018-10-27
    相关资源
    最近更新 更多