讲师中心 微信公众号
AI工具推荐 视频效率加速

C++ __stdcall与__cdecl区别 _ 函数调用约定详解【进阶】

落雪吖_4109

落雪吖_4109

发布时间:2026-04-10 13:33:29

|

961人浏览过

|

来源于php中文网

原创

__stdcall由被调用函数清理栈,__cdecl由调用方清理;Windows API用__stdcall,C标准库用__cdecl;约定不一致会导致栈失衡、崩溃或链接失败。

c++ __stdcall与__cdecl区别 _ 函数调用约定详解【进阶】

__stdcall 和 __cdecl 的栈清理责任不同

核心区别在于谁负责清理调用栈上的参数:__cdecl 由调用方清理,__stdcall 由被调用函数自己清理。这意味着同一个函数声明,如果调用方和被调用方约定不一致,ESP 会错位,轻则逻辑异常,重则直接崩溃——尤其在回调、动态链接或跨模块调用时特别危险。

常见错误现象:Access violation reading location、函数返回后栈指针错乱、局部变量值“随机”改变、调试时发现 ESP 在函数返回前/后不匹配。

  • Windows API 函数(如 MessageBoxA、CreateThread)全部使用 __stdcall,所以你写回调函数(比如线程入口、窗口过程 WndProc)必须显式声明为 __stdcall,否则系统调用你的函数后无法正确弹栈
  • C/C++ 标准库函数(如 printf、malloc)默认是 __cdecl,支持可变参数;而 __stdcall 不允许可变参数(编译器直接报错:error C2733: second C linkage of overloaded function 'xxx' not allowed)
  • 在头文件中混用约定易被忽略:比如你自己写的 DLL 导出函数声明为 __cdecl,但头文件没加修饰,调用方按默认 __cdecl 使用没问题;一旦某天你改成 __stdcall 却忘了同步更新头文件里的声明,调用方仍按 __cdecl 调用,就出问题

名字修饰(Name Mangling)规则完全不同

调用约定直接影响编译器生成的符号名,这是链接能否成功的关键。VC++ 下:__cdecl 函数修饰名为 _funcname(前面单下划线),__stdcall 是 _funcname@N(后面带 @+ 参数字节数)。

典型场景:手动 GetProcAddress 加载 DLL 中的函数时,传错名字就会返回 NULL:

C++ 算法竞赛自动化测试数据生成与校验框架
C++ 算法竞赛自动化测试数据生成与校验框架

根据原题生成新题面、验证器及完整测试数据,自动套用 testlib 模板,用于用户要求生成测试数据时。

下载

立即学习“C++免费学习笔记(深入)”;

// 假设 DLL 中导出的是 __stdcall int add(int a, int b);
// 正确写法(注意 @8 表示两个 int,共 8 字节)
FARPROC p = GetProcAddress(hMod, "add@8");

// 错误写法:用 "_add" 或 "add" 都找不到
FARPROC p = GetProcAddress(hMod, "_add"); // 找不到
FARPROC p = GetProcAddress(hMod, "add");   // 找不到
  • 使用 dumpbin /exports xxx.dll 可查看实际导出的修饰名,别猜
  • 用 extern "C" 可禁用 C++ 名字修饰,但不会改变调用约定;必须同时加 __stdcall 或 __cdecl 才有效
  • .def 文件里写裸函数名(如 add)时,链接器会自动按模块默认约定映射;但如果模块默认是 __cdecl,而函数实际是 __stdcall,仍需在 .def 中写 add@8 显式指定

混合使用时的链接与运行时风险

同一个工程里混用两种约定本身合法,但危险点在于「隐式依赖」:比如静态库 A 编译时用 __cdecl,你把它链接进一个默认 __stdcall 的 Win32 项目(比如 MFC DLL),只要没直接调用 A 里的函数,可能一直不暴露问题;一旦调用,栈就不平衡。

  • 检查方法:在函数入口加断点,观察 ESP 值;调用前后对比,若差值 ≠ 参数总字节数(对 __cdecl 还要看调用方是否清理),说明约定错配
  • VS 项目设置里可以统一指定默认调用约定(/Gd = __cdecl,/Gz = __stdcall),但仅影响未显式声明的函数;已有 __stdcall 修饰的函数不受影响
  • 第三方库文档没写调用约定?优先查其头文件——看函数声明有没有 WINAPI(即 #define WINAPI __stdcall)或 CDECL 宏;没有就反汇编或用 Dependency Walker 看导出符号格式

现代 C++ 开发中哪些地方还必须关心这个

纯新项目用不到,但维护旧代码、对接 Windows 底层、写驱动或逆向分析时绕不开。尤其要注意:C++ 成员函数隐式传 this 指针,其调用约定不是 __cdecl 或 __stdcall,而是 __thiscall(VC++ 默认),它把 this 放 ECX,其余参数按 __cdecl 规则压栈——所以不能把普通函数指针强制转成成员函数指针,也不能用 std::function 直接绑定裸 __stdcall 函数(类型不匹配)。

  • WinAPI 回调必须用 static 成员函数或全局函数 + __stdcall,不能用普通成员函数(__thiscall 无法被系统调用)
  • COM 接口所有函数都是 __stdcall,且第一个参数永远是 IUnknown*,这是硬性 ABI 要求
  • 写内联汇编时,若函数声明为 __stdcall,你手写的 ret 8 就得匹配参数大小;写成 ret 就等同于 __cdecl 行为,必然崩
实际踩坑最多的地方,是以为“只要函数能编译过去就没事”,结果在某个特定调用路径(比如多线程回调、异常展开时的栈回溯)才暴露栈失衡——这种问题往往复现困难、定位耗时。约定这事,必须从声明、实现、调用三方严格对齐,少一个环节都算没对齐。

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

音述AI
音述AI Hot

一款AI音频处理工具,主要用于音述AI是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区,适合需要提升相关任务效率的用户。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

529

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1658

2024.03.01

typedef和define区别
typedef和define区别

typedef和define区别在类型检查、作用范围、可读性、错误处理和内存占用等。本专题为大家提供typedef和define相关的文章、下载、课程内容,供大家免费下载体验。

313

2023.09.26

define的用法
define的用法

define用法:1、定义常量;2、定义函数宏:3、定义条件编译;4、定义多行宏。更多关于define的用法的内容,大家可以阅读本专题下的文章。

619

2023.10.11

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

889

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2420

2023.10.25

printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

628

2023.06.20

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1009

2023.11.28

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

120

2026.09.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Conan 2 Essentials 免费课程
Conan 2 Essentials 免费课程

共0课时 | 0人学习

CMake 与 Conan 集成实践
CMake 与 Conan 集成实践

共0课时 | 0人学习

Conan 2 高级依赖模型介绍
Conan 2 高级依赖模型介绍

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn