C函数调用与入栈顺序.doc
文本预览下载声明
C函数调用与入栈顺序
C函数调用与入栈顺序2011-07-05 11:06一.函数修饰符:
函数名字修饰(Decorated Name)方式
函数的名字修饰(Decorated Name)就是编译器在编译期间创建的一个字符串,用来指明函数的定义或原型。LINK程序或其他工具有时需要指定函数的名字修饰来定位函数的正确位置。多数情况下程序员并不需要知道函数的名字修饰,LINK程序或其他工具会自动区分他们。当然,在某些情况下需要指定函数的名字修饰,例如在C++程序中,为了让LINK程序或其他工具能够匹配到正确的函数名字,就必须为重载函数和一些特殊的函数(如构造函数和析构函数)指定名字装饰。另一种需要指定函数的名字修饰的情况是在汇编程序中调用C或C++的函数。如果函数名字,调用约定,返回值类型或函数参数有任何改变,原来的名字修饰就不再有效,必须指定新的名字修饰。C和C++程序的函数在内部使用不同的名字修饰方式,下面将分别介绍这两种方式。
1._cdecl
按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于C函数或者变量,修饰名是在函数名前加下划线。对于C++函数,有所不同。
如函数void test(void)的修饰名是_test;对于不属于一个类的C++全局函数,修饰名是?test@ZAXXZ。
这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。
2._stdcall
按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于C函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号@及参数的字节数,如函数int func(int a,double b)的修饰名是_func@12。对于C++函数,则有所不同。
Win32 API函数都遵循该约定。
3._fastcall
头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于C函数或者变量,修饰名以@为前缀,然后是函数名,接着是符号@及参数的字节数,如函数int func(int a,double b)的修饰名是@func@12。对于C++函数,有所不同。
未来的编译器可能使用不同的寄存器来存放参数。
4.thiscall
仅仅应用于C++成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。
5.naked call
采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。
naked call不是类型修饰符,故必须和_declspec共同使用,如下:
__declspec(naked)int func(formal_parameters)
{
//Function body
}
?xml:namespace prefix=o ns=urn:schemas-microsoft-com:office:office/
二.函数调用
千万要注意,C不支持默认参数
--
注释:默认参数:比如说下面的函数
int fun(int a,int b,int c=3)
{
}
c就是指定的默认实参,通常在函数原型中指定。这里给了3作为默认参数。用平常的时候调用这个函数fun(4,5,6);那么就是a=4,b=4,c=6。如果这样调用fun(1,2)那么就是a=1,b=2,c=3,这里c没有指定,因为c是默认实参,已经有了默认值,这里c就是采用默认值3。
为什么默认实参必须是函数参数表中最右边的参数。把上面的函数改下
int fun(int a=3,int b,int c)
{
}
这样调用fun(1,2),这样就是a=1,b=2,而c根本就没有赋到值,就出错了。这些参数都是一一对应的。
--
C/C++支持可变参数个数的函数定义,这一点与C/C++语言函数参数调用时入栈顺序有关,
首先引用其他网友的一段文字,来描述函数调用,及参数入栈:
C支持可变参数的函数,这里的意思是C支持函数带有可变数量的参数,最常见的例子就
是我们十分熟悉的printf()系列函数。我们还知道在函数调用时参数是自右向左压栈的。如果可变参数函数的一般形式是:
f(p1,p2,p3,…)
那么参数进栈(以及出栈)的顺序是:
…
push p3 push p2 push p1 call f
pop p1 pop p2 pop p3
…
我可以得到这样一个结论:如果支持可变参数的函数,那么参数进栈的顺序几乎必然是
自右向左的。并且,参数出栈也不能由函数自己完成,而应该由调用者完成。
这个结论的后半部分是不
显示全部