我们最常用的调用约定有以下2种,__cdecl和__stdcall, __cdecl 是c/c++的默认调用约定(calling convention), __stdcall是windows api 函数的调用约定。这2种调用约定的参数传递方式是一样的, 都是从右至左; 在堆栈的维护方式上, __cdecl要求调用者清除堆栈, 而__stdcall由被调用函数自己清除堆栈;名称修饰上,__cdecl直接在原有的函数名称上加一个下划线_,而__stdcall方式不仅加下划线而且还在后面加一个“@参数占用字节数”,具体如何,请看下面的例子。
#include <stdio.h>
int __cdecl funcA(int a, int b);
int __stdcall funcB(int a, int b);
int main()
{
int a, b, c, d;
a = 3;
b = 4;
c = funcA(a, b);
d = funcB(a, b);
return 0;
}
int __cdecl funcA(int a, int b)
{
return a + b;
}
int __stdcall funcB(int a, int b)
{
return a + b;
}
上面2个函数funcA和funcB, 一个采用__cdecl调用方式, 一个采用__stdcall调用方式,在main函数中,我们来调用这2个函数, 看看产生的汇编代码有什么不同:
11: int a, b, c, d;
12:
13: a = 3;
00401048 mov dword ptr [ebp-4],3
14: b = 4;
0040104F mov dword ptr [ebp-8],4
15:
16: c = funcA(a, b); // __cdecl 调用方式
00401056 mov eax,dword ptr [ebp-8]
00401059 push eax
0040105A mov ecx,dword ptr [ebp-4]
0040105D push ecx
0040105E call @ILT+0(_funcA) (00401005)
00401063 add esp,8 // 由调用者main平衡堆栈
00401066 mov dword ptr [ebp-0Ch],eax
17: d = funcB(a, b); // __stdcall调用方式
00401069 mov edx,dword ptr [ebp-8]
0040106C push edx
0040106D mov eax,dword ptr [ebp-4]
00401070 push eax
00401071 call @ILT+10(_funcB@8) (0040100f)
00401076 mov dword ptr [ebp-10h],eax
18:
19: return 0;
00401079 xor eax,eax
堆栈:
|
……
|
a
|
Ebp - 4
|
b
|
Ebp – 8
|
c
|
Ebp – 0Ch
|
d
|
Ebp - 10h
|
|
…..
|
由汇编代码可以看出,程序定义的四个local变量a,b,c,d分别对应于栈空间的ebp-4,
ebp-8, ebp-0Ch, ebp-10h
程序中a=3, 这个赋值语句对应的汇编代码是
mov dword ptr[ebp-4], 3
同样,赋值语句b=4, 对应的汇编代码是
mov dword ptr[ebp-8], 4
函数funcA调用后的返回值保存在c中,
mov dword ptr[ebp-0Ch], eax
函数funcB调用后的返回值保存在d中,
mov dword ptr[ebp-10h], eax
现在看看__cdecl调用方式的特点:
从参数传递上来看:
c=funcA(a, b);
对应汇编:
mov eax, dword ptr[ebp-8]
push eax ;压入参数b
mov ecx, dword ptr[ebp-4]
push ecx ;压入参数a
从这里的汇编可以很明显的看出, 先传入参数b,然后传入参数a, 即参数传递由右到左
从堆栈清除来看:
call @ILT+0(_funcA) (00401005)
add esp,8
函数funcA调用之后,main函数又用了一句代码add esp, 8来恢复堆栈, 即__cdecl调用方式是由调用者清除堆栈的
从函数修饰来看:
call @ILT+0(_funcA) (00401005)
从这里可以看出,原有的函数funcA被修改成了_funcA,即__cdecl调用方式是直接在原有的函数前面加一个下划线来修饰的。
接着,我们来看看__stdcall方式调用的特点:
d = funcB(a, b);
从参数传递来看:
mov edx,dword ptr [ebp-8]
push edx ;传入参数b
mov eax,dword ptr [ebp-4]
push eax ;传入参数a
很明显,跟__cdecl调用方式一样,参数都是从右到左的
从清除堆栈来看:
call @ILT+10(_funcB@8) (0040100f)
函数调用之后,并没有任何清除堆栈的操作,而堆栈肯定是要保持平衡的,主函数main里面没有清除堆栈的操作,那么也就意味着堆栈的清除是由被调用函数自己做的, 即__stdcall调用方式,由被调用函数自己清除堆栈
从函数修饰来看:
call @ILT+10(_funcB@8) (0040100f)
函数被修改成了_funcB@8, 即修饰后的函数是原有的函数的前面加上了下划线,后面加上参数所占空间大小,中间用@隔开。因为有2个参数int a, int b,而sizeof(int)=4, 2个加起来等于8。 如果是这样int funcB(int a, double 8), 则修饰后的函数是_funcB@12, 因为sizeof(int)+sizeeof(double)=4+8=12。
把__cdecl调用方式和__stdcall调用方式用表格总结一下:
调用方式
|
参数传递
|
堆栈清除
|
函数修饰
|
__cdecl
|
从右到左
|
调用者清除堆栈
|
funcA(int a, int b); =>_funcA
|
__stdcall
|
从右到左
|
被调用者自己清除堆栈
|
funcB(int a, int b);
=> _funcB@8
|
__cdecl因为是由调用者清除堆栈的,故可以使用可变参数,最明显的一个例子就是printf函数,采用的就是__cdecl调用方式, 同时由于__cdecl是调用者清除堆栈,所以,每当调用一个函数,都会增加清除堆栈的代码,故产生的最终代码要比__stdcall方式要大,占用磁盘空间也大。
原为地址:http://blog.chinaunix.net/u/5391/showart_1772654.html
分享到:
相关推荐
6. **默认和变体调用约定**:C#默认使用stdcall调用约定,而VC++中默认是cdecl。确保两者之间的一致性,或者在`DllImport`特性中明确指定调用约定。 7. **文件路径**:如果你的DLL不在项目的可执行文件同一目录下,...
C++支持多种调用约定,其中__cdecl和__stdcall是两种常见的调用约定。 10. 回调函数:回调函数是一个被作为参数传递给另一个函数的函数,它可以被后续调用。 11. STL:标准模板库(Standard Template Library)...
- **1.4 stdcall与cdecl**:讨论不同的调用约定(calling convention)对函数参数传递方式的影响,这对于理解函数调用机制至关重要。 - **1.5 extern与static**:解释这两个关键字的作用,特别是它们在控制变量作用...
scratch少儿编程逻辑思维游戏源码-超级马力欧兄弟.zip
scratch少儿编程逻辑思维游戏源码-城堡躲避.zip
内容概要:本文探讨了基于气动力学的导弹姿态控制技术,并详细介绍了其MATLAB仿真方法。文章首先阐述了气动力学的基本概念以及其在导弹设计中的重要性,随后讲解了导弹姿态控制系统的构成,包括传感器、控制器和执行器的功能。接下来,重点介绍了如何利用MATLAB进行导弹飞行过程和姿态控制的仿真,包括建立导弹模型、设定环境参数、编写仿真代码等步骤。最后,通过仿真展示了气动力学在提升导弹飞行稳定性、机动性和作战效能方面的重要作用,并对未来的研究方向进行了展望。 适合人群:航空航天工程领域的研究人员、导弹系统设计师、从事飞行器控制研究的专业人士。 使用场景及目标:适用于希望深入了解导弹姿态控制原理及其仿真的专业人士,旨在提高导弹飞行性能和作战能力。 其他说明:文中提供的MATLAB代码仅为简化的示例,实际应用时需考虑更多复杂的因素和算法。
scratch少儿编程逻辑思维游戏源码-电镀盒子.zip
内容概要:本文详细介绍了DSP28335与STM32F407在电源逆变系统中的锁相环(PLL)程序应用。首先概述了锁相环的基本概念及其在逆变系统中的重要性,然后深入探讨了DSP28335锁相环程序的特点和功能,如正弦波锁定、频率和相位跟踪、全桥逆变等功能。接着阐述了具体的实现步骤,包括系统配置、PLL算法选择、滤波器设计、正弦波生成与输出,最后进行了性能分析,强调了该程序在提高系统性能、稳定性和效率方面的优势。 适合人群:从事电力电子、嵌入式系统开发的技术人员,特别是对锁相环和逆变系统感兴趣的工程师。 使用场景及目标:适用于需要深入了解锁相环在电源逆变系统中应用的研发人员,旨在帮助他们掌握PLL的工作原理、实现方法及其优化技巧,以提高逆变系统的性能和可靠性。 其他说明:文中提供的技术细节和实现方法有助于读者更好地理解和应用锁相环技术,特别是在高频、高精度的逆变场合。
scratch少儿编程逻辑思维游戏源码-Shape Smasher.zip
少儿编程scratch项目源代码文件案例素材-审美乌托邦.zip
少儿编程scratch项目源代码文件案例素材-潜水艇.zip
scratch少儿编程逻辑思维游戏源码-3D忍者.zip
内容概要:本文介绍了基于EMD-ARMA的组合风光出力预测方法,详细阐述了经验模态分解(EMD)和自回归移动平均(ARMA)模型的应用步骤。首先,通过EMD将原始发电数据分解为多个本征模态函数(IMF),然后用ARMA模型对各IMF分量进行建模和预测,最后将预测结果叠加重构,获得最终的风光功率预测值。文中还提供了简化的Python代码示例,帮助读者理解和实现该方法。 适合人群:从事新能源研究和技术开发的专业人士,尤其是对风光发电预测感兴趣的科研人员和工程师。 使用场景及目标:适用于需要提高风光发电预测精度的项目,旨在通过先进的数学模型优化电力调度和资源配置。 其他说明:本文提供的代码示例仅用于教学目的,实际应用中需根据具体情况调整和完善。此外,建议在实践中参考更多专业文献和寻求专家意见以确保预测模型的准确性和可靠性。
scratch少儿编程逻辑思维游戏源码-宝石消消乐.zip
少儿编程scratch项目源代码文件案例素材-染色奔跑.zip
内容概要:本文详细介绍了使用Comsol仿真软件绘制超构表面光子晶体动量空间拓扑荷识别图的方法。首先简述了超构表面光子晶体的基本概念及其重要性,然后逐步讲解了如何在Comsol中建立模型、设置仿真参数并运行仿真,最终生成动量空间拓扑荷识别图。文中还附有简单代码示例,帮助读者更好地理解整个流程。最后对所学内容进行了总结,并展望了未来的研究方向。 适合人群:对光学、物理学以及仿真软件感兴趣的科研人员和技术爱好者。 使用场景及目标:适用于希望深入了解光子在超构表面光子晶体中传播特性的研究人员,旨在提高他们对该领域的认识水平,促进相关科学研究的发展。 阅读建议:由于涉及较多专业术语和复杂概念,在阅读时建议先掌握基本理论知识,并结合实际案例进行练习,以便更好地消化吸收文中内容。
内容概要:本文介绍了一种结合卷积神经网络(CNN)、长短时记忆网络(LSTM)以及SE注意力机制的混合模型用于时序数据分类预测的方法,并提供了具体的MATLAB实现方法。文中详细解释了模型的工作流程,从卷积层的空间特征提取开始,经过SE注意力模块对特征进行加权处理,再到LSTM层的时间序列建模,最终完成分类任务。此外,还讨论了一些优化技巧,如数据预处理、动态学习率设置、特征压缩等,以提高模型性能。 适合人群:有一定机器学习基础的研究人员和技术开发者,特别是那些从事医疗健康、工业监控等领域时序数据分析工作的专业人士。 使用场景及目标:适用于需要高效处理复杂时序数据的应用场合,如医疗诊断、工业设备状态监测等。主要目的是为了改善传统单一模型在特征利用方面的局限性,提供一种更加精准有效的解决方案。 其他说明:文中提供的代码片段可以直接应用于实际项目中,只需根据具体情况调整参数配置和数据格式。同时提醒使用者关注数据预处理步骤,确保输入数据的质量和一致性对于获得良好结果至关重要。
scratch少儿编程逻辑思维游戏源码-超级摇摆小猫.zip
scratch少儿编程逻辑思维游戏源码-奔跑吧!糖豆人.zip
内容概要:本文档详细介绍了旋翼无人机(UAV)的动力学建模方法,重点在于六自由度模型的构建。首先定义了一个简化的四旋翼无人机动力学类,涵盖了质量、惯性矩阵、重力加速度等基本物理参数,并实现了推力矩阵的计算,用于将电机转速转化为推力和力矩。接着讨论了姿态更新过程中四元数的作用,提供了四元数更新的具体实现方式,强调了归一化操作的重要性。此外,还探讨了传感器噪声对飞行控制系统的影响,提出了一个较为真实的陀螺仪噪声模型,考虑到了随机游走特性和低通滤波效果。最后给出了一个简易的数值积分循环示例,展示了如何利用上述模型进行仿真。 适用人群:从事无人机研究、开发的技术人员,尤其是对飞行器动力学建模感兴趣的工程师。 使用场景及目标:适用于希望深入了解无人机内部工作原理的研究者和技术开发者,旨在帮助他们掌握从理论到实践的关键步骤,包括但不限于动力学建模、姿态控制、传感器误差补偿等方面的知识。 其他说明:文中提供的代码片段均为简化版本,实际应用中可根据具体需求调整参数设置和算法细节。同时推荐使用专业数学库来提高代码效率和准确性。