__stdcall与__cdecl的区别




搬运自我的百度空间

csdn上回答了别人这个问题,码了好多字,还是放在这里不枉我这些时间吧

 

http://bbs.csdn.net/topics/390435293?page=1#post-394308806

可以这样理解,不同call的方式相当于调用者和函数之间不同的协议,就像tcp与udp协议一样,对于发送端和服务器端的行为做了规定,函数call的"协议"也就是调用者与函数之间行为责任的划分
cdecl只是c语言的默认定义罢了,c语言的默认"协议"是cdecl,所以在c语言内部调用函数不会涉及call方式的不同,但是系统调用可能涉及不同语言之间协作,比如vb只能支持stdcall,那么c语言写的vb支持库只能用stdcall调用,还有stdcall比较节约空间,所以用于windows api

 

调整esp指的是释放函数内部变量,函数参数的空间
这些事情编译器根据call方式的不同自动添加,所以初学者应该不用考虑太多

 

先要明白堆栈的含义,可以认为他们是一堆书,你只能从书堆的顶部放入或者拿出书本
现在举个例子,把“我”想象为函数的调用者,“服务员”想象为函数体
(注意,一个线程只会有一个系统堆栈,认为“新开”堆栈的想法是错误的,同一线程所有函数的操作都在同一个堆栈完成)

stdcall就是“我”把书(参数)依次放在了书堆顶(压入堆栈)(书上写的是我对他的命令),事先与服务员约定好我已经放了N本书(N个字节),“服务员”按照约定查看N本书(我压入的参数),做好他的工作后,他按照约定的书本个数N,来把N本书从书堆上移除(清理堆栈,释放那几个参数的空间,而不是释放整个堆栈)

cdelcall是“我”把书(参数)依次放在了书堆顶,没有与服务员约定书的多少,服务员查看我给的书,然后呼叫我,我自己把刚刚放上的书移除

使用堆栈的目的:我叫上服务员后,可能服务员必须叫上后勤人员,服务员也按照我的方法,把命令写在了新的书上,也放在书堆顶部,呼叫后勤人员来完成(这叫做递归调用)
清理堆栈之类的操作能确保所有工作完成后书堆与原来无异

 

cdecl:内存中“实际”代码

b函数
{
do something
return
}

a函数调用b函数
{
push 参数(参数进栈)
call 函数
sub esp…(恢复堆栈)
}

c函数调用b函数
{
push 参数(参数进栈)
call 函数
sub esp…(恢复堆栈)
}

指令都是保存在内存中,看到a和c分别调用b,内存中就有两处恢复堆栈的代码

stdcall

b函数
{
do something
恢复堆栈
return
}

a函数调用b函数
{
push 参数(参数进栈)
call 函数//这里没有恢复堆栈的代码
}

c函数调用b函数
{
push 参数(参数进栈)
call 函数
}
可以看到,只有b函数内部有一份恢复堆栈的代码,调用函数的地方不再有重复代码

 

cdecl是调用者恢复堆栈的,假设有一百个不同的函数调用函数a
那么内存中就有一百端恢复堆栈的代码,是不是很浪费空间呢?
stdcall是函数恢复堆栈,只有在函数代码的结尾出现一次恢复堆栈的代码,所以节约空间
注意,这些恢复堆栈的代码是编译器根据你给他的call方式自动生成的,所以你无需考虑…而告诉编译器call方式的意义就在这里,如果一方用cdecl一方用stdcall可能出现没有人释放堆栈的情况,这明显是不允许的

 

cdecl的优势在于他可以不定参数个数,参考printf函数
原因在于是调用者存入参数,调用者释放参数占有的空间,都是调用者完成的,所以有参数个数的自由性
stdcall在结束函数时,回复的空间是编译时决定的,函数负责释放,但他无法知道你实际压入几个参数,于是stdcall在编译时就规定了参数个数,无法实现不定个数的参数调用

 

.......我擦,这要有多少字数啊。。。。。

你可能感兴趣的:(__stdcall与__cdecl的区别)