知识大全 函数调用方式的区别[thiscall,
Posted 函数
篇首语:蹉跎莫遣韶光老,人生唯有读书好。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 函数调用方式的区别[thiscall,相关的知识,希望对你有一定的参考价值。
通常在使用VC进行函数定义时会指定该函数调用方式 诸如 int __stdcall max(int a int b) return a>b?a:b; int __cdecl min(int a int b) return a<b?a:b; bool __fastcall equal(int a int b) return a=b?true:false; 首先 让我们来分个类 调用方法分为两大类另加一个较特殊的__thiscall 第一类 __stdcall类 别名 WINAPI CALLBACK PASCAL 该类特点是 主调函数负责参数入栈 由函数本身负责栈的恢复 第二类 __cdecl类 别名 C/C++中默认调用方式 若你定义函数未指定函数调用约定(Calling Conventions) 例如在VC 中下面两个函数的调用约定是等价的 int max(int a int b) return a>b?a:b; int __cdecl min(int a int b) return a<b?a:b; 该类调用约定的特点是 由主调函数负责参数入栈 并由主调函数负责线的恢复 第三类 __thiscall 该类比较特殊 只用于类成员函数调用 你甚至不能强制指定这个函数调用约定 它是由C/C++编译器自动添加的 在C/C++中类成员函数会默认传入一个this指针 对于此 在默入情况下 C/C++中类成员函数通过此类调用约定来指定this指针 接着介绍一下__thiscall __thiscall是关于类的一种调用方式 它与其他调用方式的最大区别是 __thiscall对每个函数都增加了一个类指针参数 class aa void bb(int cc) ; 实际上bb的函数原形是void bb(aa &this int cc) __cdecl的调用方式介绍 C和C++缺省调用方式 例子 void Input( int &m int &n) /*相当于void __cdecl Input(int &m int &n) */ 以下是相应的汇编代码 lea eax [ebp ] ;取[ebp ]地址(ebp ) 存到eax B push eax ;然后压栈 C lea ecx [ebp ] ;取[ebp ]地址(ebp ) 存到ecx F push ecx ;然后压栈 call @ILT+ (Input) ( a) 然后调用Input函数 add esp ;恢复栈 从以上调用Input函数的过程可以看出 在调用此函数之前 首先压栈ebp 然后压栈ebp 然后调用函数Input 最后Input函数调用结束后 利用esp+ 恢复栈 由此可见 在C语言调用中默认的函数修饰_cdecl 由主调用函数进行参数压栈并且恢复堆栈 下面看一下 地址ebp 和ebp 是什么? 在VC的VIEW >debug windows >Registers 显示寄存器变量值 然后在选debug windows >Memory 输入ebp 的值和ebp 的值(或直接输入ebp 和 ) 看一下这两个地址实际存储的是什么值 实际上是变量 n 的地址(ebp ) m的地址(ebp ) 由此可以看出 在主调用函数中进行实参的压栈并且顺序是从右到左 另外 由于实参是相应的变量的引用 也证明实际上引用传递的是变量的地址(类似指针) 总结 在C或C++语言调用中默认的函数修饰_cdecl 由主调用函数进行参数压栈并且恢复堆栈 实参的压栈顺序是从右到左 最后由主调函数进行堆栈恢复 由于主调用函数管理堆栈 所以可以实现变参函数 另外 命名修饰方法是在函数前加一个下划 线(_) _stdcall调用约定介绍 实际上就是PASCAL CALLBACK WINAPI 例子 void WINAPI Input( int &m int &n) 看一下相应调用的汇编代码 lea eax [ebp ] B push eax C lea ecx [ebp ] F push ecx call @ILT+ (Input) ( a)
从以上调用Input函数的过程可以看出 在调用此函数之前 首先压栈ebp 然后压栈ebp 然后调用函数Input 在调用函数Input之后 没有相应的堆栈恢复工作(为其它的函数调用 所以我没有列出)下面再列出Input函数本身的汇编代码 (实际此函数不大 但做汇编例子还是大了些 大家可以只看前和后 中间代码与此例子无关) : void WINAPI Input( int &m int &n) : push ebp mov ebp esp sub esp h push ebx push esi push edi lea edi [ebp h] C mov ecx h mov eax CCCCCCCCh rep stos dword ptr [edi] : int s i; : : while( ) mov eax D test eax eax F je Input+ C h ( d ) : : printf( \\nPlease input the first number m: ) push offset string \\nPlease input the first number m … ( b ) A call printf ( ) F add esp : scanf( %d &m) mov ecx dword ptr [ebp+ ] push ecx push offset string %d ( b ) B call scanf ( f ) add esp : : if ( m= s ) B mov eax dword ptr [ebp+ ] B mov ecx dword ptr [eax] B cmp ecx dword ptr [ebp ] BB jl Input+ AFh ( bf) : break; BD jmp Input+ C h ( d ) : else : printf( m < n*(n+ )/ Please input again!\\n ) BF push offset string m < n*(n+ )/ Please input agai … ( ) C call printf ( ) C add esp : CC jmp Input+ h ( ) : : D pop edi D pop esi D pop ebx D add esp h D cmp ebp esp D call __cesp ( b ) DE mov esp ebp E pop ebp E ret 之后 我们看到在函数末尾部分 有ret 明显是恢复堆栈 由于在 位C++中 变量地址为 个字节(int也为 个字节) 所以弹栈两个地址即 个字节 由此可以看出 在主调用函数中负责压栈 在被调用函数中负责恢复堆栈 因此不能实现变参函数 因为被调函数不能事先知道弹栈数量 但在主调函数中是可以做到的 因为参数数量由主调函数确定 下面再看一下 ebp 和ebp 这两个地址实际存储的是什么值 ebp 地址存储的是n 的值 ebp 存储的是m的值 说明也是从右到左压栈 进行参数传递 总结 _stdcall在主调用函数中负责压栈 在被调用函数中负责弹出堆栈中的参数 并且负责恢复堆栈 因此不能实现变参函数 参数传递是从右到左 另外 命名修饰方法是在函数前加一个下划线(_) 在函数名后有符号(@) 在@后面紧跟参数列表中的参数所占字节数( 进制) 如 void Input(int &m int &n) 被修饰成 _Input@ 对于大多数api函数以及窗口消息处理函数皆用CALLBACK 所以调用前 主调函数会先压栈 然后api函数自己恢复堆栈 如 push edx push edi push eax push ebx call getdlgitemtexta 最后 在SDK中输出API函数的时候 经常会利用WINAPI对函数进行约定 WINAPI在WIN 中 它被定义为__stdcall 函数调用约定有多种 这里简单说一下 __stdcall调用约定相当于 位动态库中经常使用的PASCAL调用约定 在 位的VC++ 中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall 除了__pascal 外 __fortran和__syscall也不被支持) 取而代之的是__stdcall调用约定 两者实质上是一致的 即函数的参数自右向左通过栈传递 被调用的函数在返回前清理传送参数的内存栈 但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明) _stdcall是Pascal程序的缺省调用方式 通常用于Win Api中 函数采用从右到左的 压栈方式 自己在退出时清空堆栈 VC将函数编译后会在函数名前面加上下划线前缀 在函数名后加上 @ 和参数的字节数 C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈 由调用者把参数弹出栈 对于传送参数的内存栈是由调用者来维护的(正因为如此 实现可变参数的函数只能使用该调用约定) 另外 在函数名修饰约定方面也有所不同 _cdecl是C和C++程序的缺省调用方式 每一个调用它的函数都包含清空堆栈的代码 所以产生的可执行文件大小会比调用_stdcall函数的大 函数采用从右到左的压栈方式 VC将函数编译后会在函数名前面加上下划线前缀 是MFC缺省调用约定 __fastcall调用约定是 人 如其名 它的主要特点就是快 因为它是通过寄存器来传送参数的(实际上 它用ECX和EDX传送前两个双字(DWORD)或更小的参数 剩下的参数仍旧自右向左压栈传送 被调用的函数在返回前清理传送参数的内存栈) 在函数名修饰约定方面 它和前两者均不同 _fastcall方式的函数采用寄存器传递参数 VC将函数编译后会在函数名前面加上 @ 前缀 在函数名后加上 @ 和参数的字节数 thiscall仅仅应用于 C++ 成员函数 this指针存放于CX寄存器 参数从右到左压 thiscall不是关键词 因此不能被程序员指定 naked call采用 的调用约定时 如果必要的话 进入函数时编译器会产生代码来保存ESI EDI EBX EBP寄存器 退出函数时则产生代码恢复这些寄存器的内容 naked call不产生这样的代码 naked call不是类型修饰符 故必须和_declspec共同使用 cha138/Article/program/c/201311/11110相关参考
现有某函数与对象如下: vardoubling=function(x)returnx*;;varobj=val:;函数调用模式时this被绑定到全局对象这种情况在对象的属性与方法被初始化时也能够
php函数method_exists()与is_callable()的区别在哪?在php面相对象设计过程中往往我们需要在调用某一个方法是否属于某一个类的时候做出判断常用的方法有method_exist
Oracle认证:Oracle随机函数调用 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!Oracl
Applet调用JavaScript函数 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 在做的的
C#调用WindowsAPI函数 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! Api函数是构筑
PHPWeb开发学习实录:PHP函数的调用运行结果 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
PHPWeb开发学习实录:PHP函数的调用实例描述 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
PHPWeb开发学习实录:PHP函数的调用实例应用 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
PHPWeb开发学习实录:PHP函数的调用实例分析 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
PHPWeb开发学习实录:基础知识-PHP函数的调用 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!