C++ - __stdcall 与 __cdecl

c++中的__stdcall、__declspec(dllexport)、__declspec(dllimport) 如果未定义 MATHLIBRARY_EXPORTS 阅读详情

摘录一

_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除(也就是自己清除),函数在编译的时候就必须清楚参数个数。

_cdecl 是CDeclaration的缩写,表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除。被调用者(也就是自己)不需要求调用者传递多少参数。
这类函数类似printf(...);
scanf(...);

两者共通点:

参数都是从右到左依次入栈


名字修饰约定:

1.C编译时函数名修饰约定:

[cpp]  view plain copy
  1. #ifdef __cplusplus  
  2. extern "C"  
  3. {  
  4. #endif  
  5. __declspec(dllexport) int __stdcall add(int x,int y)  
  6. {  
  7.     return x+y;  
  8. }  
  9. #ifdef __cplusplus  
  10. }  
  11. #endif  
①__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数,格式为_functionname@number,例如 :function(int a, int b),其修饰名为:_function@8
上例中就为
_add@8

②__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname
上例子就为_add


2.C++编译时函数名修饰约定:

如例子
__declspec(dllexport) void __stdcall add(int x,char y)
{
int s= x+y;
}

__stdcall方式名字为
?add@@YGXHD@Z

①__stdcall规则如下:

1.以'?'标识函数的开始
2.函数名后面跟'@@YG'表示参数的开始
3.参数表以代号表示
X--void
D--char
E--unsigned char
H--int
I--unsigned int
J--long
K--unsigned long
M--float
N--double
_N--bool
...

PA--表示指针,后面的代号表示指针类型,如果相同类型的指针后面出现了,以'0'代替,一个'0'代表一次重复
如
__declspec(dllexport) void __stdcall add(int *x,char *a,int* y)
{

}
就表示为?add@@YGXPAHPAD0@Z
4.参数表的第一项为该函数的返回类型,其后依次为参数的数据类型.
5.参数表后以'@z'标识整个名字的结束,如果函数无参数,则以'Z'结束.(没有参数同void)

如__declspec(dllexport) __stdcall int add()
{
return 1;
}

名字为:?add@@YGHXZ


②__cdecl规则如下:

同__stdcall只是只是函数名后面跟'@@YG'变为了@@YA


摘录二

一、概念
1)_stdcall调用
   _stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作, 被调函数自身在返回前清空堆栈。
   WIN32 Api都采用_stdcall调用方式,这样的宏定义说明了问题:  #define WINAPI _stdcall
   
   按C编译方式,_stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_f unctionname@number.
   Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12.
   按C++编译方式,可参看(三)

 2)_cdecl调用
   
_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式, 由调用者完成压栈操作 ,传送参数的内存栈由调用者维护 。
   _cedcl约定的函数只能被C/C++调用
,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。
   按C编译方式,_cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
   按C++编译方式,可参看(三)

二、区别
     几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,首先,需要了解两者之间的区别: WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?如果我 们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不 尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发) 平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。那么为什么还需要_cdecl呢?当我们遇到这样的函数如 fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用 _cdecl。到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。

三、编译时函数名修饰约定规则
1)首先介绍一下函数修饰名,C” 或者“C++”函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如 在模块定义文件里头指定输出“C++”重载函数、构造函数、析构函数,又如在汇编代码里调用“C””或“C++”函数等。修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同。下面详细说明在C++编译器中修饰名的约定方法,在C编译器中的变化已经在(一)中阐述。

2)C++编译时函数名修饰约定规则
    __stdcall调用约定: 
  1)、以"?"标识函数名的开始,后跟函数名; 
  2)、函数名后面以"@@YG"标识参数表的开始,后跟参数表; 
  3)、参数表以代号表示: 
  X--void , 
  D--char, 
  E--unsigned char, 
  F--short, 
  H--int, 
  I--unsigned int, 
  J--long, 
  K--unsigned long, 
  M--float, 
  N--double, 
  _N--bool, 
  PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复; 
  4)、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前; 
  5)、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。 
  其格式为" ?functionname@@YG*****@Z "或" ?functionname@@YG*XZ ",例如   int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”   void Test2()  -----“?Test2@@YGXXZ ” 
  __cdecl调用约定: 
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。 

四、在C++编译时,引入c库里面的函数的处理
    如果Add(int a, int b)是在c语言编译器编译,而在c++文件使用,由(三)知,因为c编译器和c++编译器对函数名的解释不一样(c++编译器解释函数名的时候要考虑函数参数,这样是了方便函数重载,而在c语言中不存在函数重载的问题),会出现链接错误。 哪么该如何处理呢?这是关键字extern就派上用场了。
   而在c++文件使用c编译器编译过的函数,则需要在c++文件中声明:extern "C" Add(int a, int b),使用extern "C",实质就是告诉c++编译器,该函数是c库里面的函数。请保持我的名称,不要给我生成用于链接的函数修饰名。
   另外顺便说一下extern的另一个用途,extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。#inlcude "***.***"     和 int   f()  等价于   extern int f() ;即 extern 申明的函数前不需要加引用头文件就能用。

参考文献:
博客: http://www.cnblogs.com/Winston/archive/2008/09/11/1289391.html

函数的“握手协议”:揭秘C++调用惯例(cdecl, stdcall)的秘密 本文深入解析C++函数调用惯例(cdecl和stdcall)的工作原理与区别。调用惯例作为函数调用的"握手协议",规定了参数传递顺序、栈清理责任和名称修饰规则。cdecl是C/C++默认约定,参数从右至左入栈,由调用方清理栈,支持可变参数;而stdcall由被调用方清理栈,广泛应用于Windows API。文章通过汇编代码示例展示了两种约定的执行过程,并分析了名称修饰对函数重载的影响。最后强调调用惯例不匹配会导致栈损坏,理解这些底层机制有助于正确使用外部库和调试程序。 阅读详情

相关推荐

【科研绘图】【数据拟合曲线】:附Origin详细画图流程

拟合曲线图

weixin_50236590的博客 4万+

(一)C++学习之__declspec(dllimport)、__declspec(dllexport)和__stdcall、__cdecl

1、__declspec(dllimport)、__declspec(dllexport) __declspec(dllexport)与__declspec(dllimport)都是DLL内的关键字,即导出与导入。他们是将DLL内部的类与函数以及数据导出与导入时使用的。dllexport是在这些类、函数以及数据的申明的时候使用。用他表明这些东西可以被外部函数使用,即(dllexport)是把 D...

qq_34706266的博客 1597

【 Git学习记录(三)】推荐四款主流的Git代码托管平台 —— GitHub、GitLab、Gitee、Coding

了解过Git之后,作为一个程序猿最基本的,你还需要了解目前主流的在线代码托管平台,并至少选择其中一款在平时开发中使用。这里特别介绍以下四款: 国外平台 GitHub —— 全球最流行的代码托管平台 GitLab —— 基于git的仓库管理程序 国内平台 Gitee —— 开源中国旗下的代码托管平台 Coding —— 支持社交元素的代码托管平台 一. GitHub 链接地址:https://github.com/ GitHub目前是全球最流行的代码托管平台(别称:全球最大的同性交友.

JZevin的博客 6091

__declspec(dllimport)与__declspec(dllexport)及__stdcall 作用总结

调用协议常用场合 __stdcall:Windows API默认的函数调用协议。 __cdecl:C/C++默认的函数调用协议。 __fastcall:适用于对性能要求较高的场合。 函数参数入栈方式 __stdcall:函数参数由右向左入栈。 __cdecl:函数参数由右向左入栈。 __fastcall:从左开始不大于4字节的参数放入CPU的ECX和EDX寄存器,其余参数从右向左入栈。 问题一:__fastcall在寄存器中放入不大于4字节的参数,故性能较高,适用于需要高性能的场合。 栈内数据清除方式 _.

Alone的博客 3775

【KDD2024】ShapeFormer:用于多元时间序列分类的Shapelet Transformer

多元时间序列分类(MTSC)因其在现实世界中的广泛应用而吸引了大量研究关注。最近,Transformer在MTSC中取得了先进的性能。然而,

m0_52597077的博客 4743

ZLMediaKit源码分析(二)推流创建

ZLMediaKit推流结构体创建是在RtspSession::onRecv()之后。如何触发onRecv()请参考上一篇文章这里不在赘述。

cliffordl的专栏 5074

C/C++ 常见函数调用约定(__stdcall,__cdecl,__fastcall等):介绍常见函数调用约定的基本概念、用途和作用

C/C++函数调用约定

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 2989

Win64 驱动内核编程-16.WFP网络监控驱动(防火墙)

WFP驱动监控网络 WFP是微软推出来替代TDIHOOK、NDISHOOK等拦截网络通信的方案,WFP的框架非常庞大,在RING3和RING0各有一套类似的函数,令人兴奋的是,即使在R3使用WFP,也可以做到全局拦截访问网络。由于WFP的范围太广,实在难以一言概括,感...

墨鱼菜鸡 1033

c++ 中__declspec 的用法详解

c++ 中__declspec 的用法如下,想要了解的继续往下看吧。 语法说明: __declspec ( extended-decl-modifier-seq ) 扩展修饰符: 1:align(#) 用__declspec(align(#))精确控制用户自定数据的对齐方式 ,#是对齐值。 e.g C++ __declspec(align(32)) struct Str1{ int a, b, c, d, e; }; 它与#pragma pack()是一对兄弟,前者规定了对齐的最小值,后者规定了对齐的最大值

weixin_43004008的博客 1516

升级conda时出现Malformed version string invalid chars错误解决方案

但有时,当你使用“conda upgrade”的命令时,你可能会遇到一个错误,提示你的环境解析失败,出现“Malformed version string invalid chars”错误消息。如果以上两个步骤都没有解决你的问题,那么你可以考虑更新conda的依赖项。如果你的conda版本已经是最新的,那么你需要清理conda的缓存。最后,如果以上方法都无法解决问题,你可以尝试使用pip进行更新。如果你的conda版本过旧,你需要先更新conda。首先,你需要确认你当前使用的conda版本。

BUG?不存在的! 1409

matlab 质心重合法实现点云配准【2026最新版】

质心重合法是将源点云和目标点云的质心重合,以此缩小两点云集之间的平移误差,从而为下一步粗配准提供更好的初始位置姿态。

点云侠的博客 509

C/C++ __cdecl和__stdcall

C/C++ __cdecl和__stdcall

dream_to_dream的博客 1674

BES2300x笔记(36) -- OTA升级

哈喽大家好,这是该系列博文的第三十六篇~ 篇~ <<【系列博文索引】快速通道 >> 一、前言 BES 蓝牙方案自带 OTA(Over The Air)功能,该功能可以对蓝牙固件进行在线升级,目前该功能支持通过安卓手机(苹果手机也有对应APP,未实际测试)对蓝牙固件进行升级,以后还会支持通过 PC 端进行在线升级。在使用 OTA 功能之前,用户需安装 BES 提供的手机 apk 软件,将需要升级的固件拷贝到手机,然后通过该 apk 软件进行在线升级。

文化人的博客 5356

C++中cdecl、stdcall、fastcall、thiscall异同——10分钟弄清

本文对比了C++中四种调用约定:cdecl(C默认,调用者清栈,支持可变参数)、stdcall(Windows API标准,被调函数清栈)、fastcall(前两个参数用寄存器传递)和thiscall(C++成员函数专用,this指针通过ECX传递)。通过参数传递方式、清栈责任、适用场景和汇编特征等方面进行对比,并给出典型示例。cdecl适合可变参数函数,stdcall是Windows API标准,fastcall优化性能,thiscall专用于类成员函数。调试时需观察栈和寄存器变化验证调用约定。

Byronloong 的博客 552

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

C++ _stdcall,_cdecl方式

如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将函数的调用方式声明为__stdcall方式,WINAPI都采用这种方式,而C/C++缺省的调用方式却为__cdecl。__stdcall方式与__cdecl对函数名最终生成符号的方式不同。若采用C编译方式(在C++中需将函数声明为extern "C"),__stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数

shandiantianying的专栏 956

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

关于C/C++的STDCALL和_cdecl调用约定

动态库输出函数的约定有两种:调用约定和名字修饰约定。 1)调用约定(Callingconvention):决定函数参数传送时入栈和出栈的顺序,由调用者还是被调用者把参数弹出栈,以及编译器用来识别函数名字的修饰约定。 函数调用约定有多种,这里简单说一下: 1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持...

王春雨的博客 1020

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

[C++反汇编] 调用约定 cdecl stdcall fastcall 等

该系列文章是依据本人平时对反汇编的学习,归纳总结,所做的学习笔记。如有错误或待改善之处,请留下您宝贵的意见或建议。 调用约定是指调用方放置函数调用所需的参数的具体位置。具体的位置可以是特定的寄存器、程序栈、亦或者寄存器和栈中。调用约定还有一个重要的任务:函数调用完成后(被调用函数结束后),是谁(调用方还是被调用方)完成栈平衡工作(也就是清理栈中的参数)。有的调用约定由调用方完成栈平衡工作(如

智者千虑 必有一失 2656

C++函数调用方式(_stdcall, _pascal, _cdecl...)总结

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __stdcall:

mmqqyyqqyyq的博客 317

C/C++ Calling covention 调用约定:_stdcall _cdecl CALLBACK WINAPI PASCAL

宏定义: #define CALLBACK__stdcall #define WINAPI__stdcall #define WINAPIV__cdecl #define APIENTRYWINAPI #define APIPRIVATE__stdcall #define PASCAL__stdcall 调用约定(Calling...

cuizhiyi2008的专栏 374

C++的函数名修饰,__stdcall,__cdecl,__fastcall

在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链接器。这项技术通常被称为名称改编(Name Mangling)或者名称修饰(Name Decoration)。

weixin_40267061的博客 810

C/C++函数调用约定:__cdecl、__stdcall、__fastcall和__thiscall

在C和C++编程中,函数调用约定(Calling Convention)定义了函数如何接收参数、如何返回值以及由谁来清理栈上的参数。不同的平台和编译器可能会支持不同的调用约定。在Windows平台上,尤其是在使用Microsoft的编译器(如MSVC)时,常见的调用约定包括__cdecl__stdcall__fastcall和对于一个基本函数,C++ 底层通过帧栈来描述函数调用的过程,一个帧栈过程大致包含了以下过程:栈空间的开辟,也叫栈拉伸。调用子函数参数准备,这个过程会把子函数需要的参数压栈。

红军不怕远征难,万水千山只等闲 1627

C++函数调用方式约定stdcall,cdecl,pascal,naked,thiscall,fastcall

https://www.cnblogs.com/xiangtingshen/p/11014514.html C++函数调用约定_cdecl约定:参数:从右向左依次入栈堆栈平衡:调用方平衡 #include "pch.h" #include <iostream> int __cdecl getMaxNumber(int a, int b, int c) { ...

weixin_30745641的博客 220

C++函数调用方式 stdcall pascal cdecl 总结

__stdcall: _stdcall 调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回...

weixin_30468137的博客 129

关于c/c++代码中的__cdecl和__stdcall

  __cdecl是C Declaration的缩写,表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不需要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。   __stdcall是Standard Call的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话...

172

【C/C++开发】__stdcall,__cdecl,__fastcall的区别

__stdcall和__cdecl的区别 __stdcall和__cdecl是两种函数名字修饰。(注意是连续的两个下划线) Windows上 windows上不管是C还是C++,默认使用的都是__stdcall方式。 不论__stdcall还是__cdecl函数参数都是从可向左入栈的,并且由调用者完成入栈操作。对于__stdcall方式被调用者自身在函数返回前清空堆栈;而__cde

ZhangPY的专栏 2727

C/C++ __cdecl、__fastcall、__stdcall 的区别

定义__cdecl__cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。__stdcall被这个关键字修饰的函数,其参数都是从右向左通过堆栈传递的(__fastcall

司马懿的西山居 1992
上一篇: C - 函数调用的栈分配
下一篇: C++ - extern "C"
G机器猫
博客等级 码龄16年 106粉丝 74原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值