c语言函数的几种调用约定和细节问题

一、函数几种调用约定

调用约定 参数压栈顺序 平衡堆栈
__cdecl 从右至左入栈 调用者清理栈
__stdcall 从右至左入栈 自身清理堆栈
__fastcall ECX/EDX传送前两个 自身清理堆栈
  剩下:从右至左入栈  

 

用法:返回值类型 (调用约定)函数名()

不写调用约定的话,默认第一种调用约定,就是我们平常用的函数调用

下面举例说明下:

1、int __cdecl Plus(int a, int b)				
{				
	return a+b;			
}				
对应汇编代码:				
push        2				
push        1				
call        @ILT+15(Plus) (00401014)				
add         esp,8		;这个就是外面平衡堆栈,传入两个参数,且为int型4byte,所以是8		
2、int __stdcall Plus(int a, int b)				
{				
	return a+b;			
}				
对应汇编代码:				
push        2				
push        1				
call        @ILT+10(Plus) (0040100f)				
				
函数内部:				
				
ret         8	;这句是call对应的,内平栈		

3,4都属于第三种情况 

3、int __fastcall Plus(int a, int b)					
{					
	return a+b;				
}					
					
mov         edx,2		;因为传入寄存器不需要平衡堆栈			
mov         ecx,1					
call        @ILT+0(Plus) (00401005)					
					
函数内部:					
					
ret         					
					
4、int __fastcall Plus4(int a, int b,int c,int d)					
{					
	return a+b+c+d;				
}					
					
push        4		;当参数大于两个时,编译器会倒着存入堆栈,剩两个存入寄存器里			
push        3					
mov         edx,2					
mov         ecx,1					
call        @ILT+5(Plus) (0040100a)					
					
函数内部:					
					
ret         8		;外平栈,平衡传入堆栈的那一部分			

二、细节之处:

1、看下面代码有没有发现什么异常

#include
char fun(char x,char y)
{
    return x+y;
}
int main()
{
    char s=fun(2,3);
    return 0;
}

对应汇编代码(主要的部分):
push        3  
push        2  
call        fun (01213DEh)  
add         esp,8  ;这里平衡堆栈的时候也是8个字节,但是我们传入的明明是两个char类型

这里发现传入char类型和传入int类型(参考调用约定第一个例子)的压入堆栈都是按照4byte压入的,short我不测试了,也是按照4byte压入堆栈的,这是什么原理?

因为本机原理(参考了这位兄台的博客https://www.cnblogs.com/zimudao/p/8505530.html)

在32位的系统中,系统默认最合适的数据类型,就是32个bit,即4字节。同理,64位的系统就是8字节。

也就是说,如果使用小于4个字节的局部变量来进行参数传递,vs编译器仍然会按照4个字节来进行传递,但是多余的部分并不会使用。

我们可以看看 long long传入的时候是怎么传入的

#include
long long fun(long long x, long long y)
{
	return x + y;
}
int main()
{
	long long s = fun(2, 3);
	return 0;
}
对应汇编:
push        0  
push        3  
push        0  
push        2  
call        fun (013513E3h)  
push        ebp  
mov         ebp,esp  
sub         esp,0C0h  
push        ebx  
push        esi  
push        edi  
lea         edi,[ebp-0C0h]  
mov         ecx,30h  
mov         eax,0CCCCCCCCh  
rep stos    dword ptr es:[edi]  
mov         eax,dword ptr [x]  
add         eax,dword ptr [y]  
mov         edx,dword ptr [ebp+0Ch]  
adc         edx,dword ptr [ebp+14h] 
pop         edi  
pop         esi  
pop         ebx  
add         esp,0C0h  
cmp         ebp,esp  
mov         esp,ebp  
pop         ebp  
ret  
add         esp,10h  
mov         dword ptr [s],eax 

这时候发现传入了2个参数,可是压入堆栈一共压入了4个,

因为long long8字节,所以正好两个地址存一个long long类型的,上面的存高位,下面的存低位。

你可能感兴趣的:(C语言)