社区
C语言
帖子详情
__stdcall 可以像__cdecl 使用可变参数吗??
testtest2002
2011-03-18 03:39:04
像这样:
void __cdecl operator()(DWORD_PTR dwCategory, UINT nLevel, const char *pszFmt, ...)
...全文
262
14
打赏
收藏
__stdcall 可以像__cdecl 使用可变参数吗??
像这样: void __cdecl operator()(DWORD_PTR dwCategory, UINT nLevel, const char *pszFmt, ...)
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
14 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
BianChengNan-BCN-BCN
2011-12-30
打赏
举报
回复
[Quote=引用 13 楼 maozefa 的回复:]
引用 10 楼 dourgulf 的回复:
maozefa
已经解析得很清楚了,只是对“使用插入汇编手工清除变参部分”认为不是很现实,本质上只有调用者(caller)负责处理参数的堆栈才可以正确实现变参数的清理,所以一个函数的编写者是无法在函数的实现中进行具体的参数堆栈清理工作(即使嵌入汇编),实际上C对变参数的支持是在编译的时候对调用参数加入清理堆栈的处理的:形式代码如下:
声明:
……
[/Quote]多谢maozefa,我看了一下汇编代码,是由系统平衡的堆栈,可是你上面说的参数将留在堆栈中,我不明白,既然系统已经帮助清理了堆栈,怎么还会残留呢?请指教
阿发伯
2011-03-19
打赏
举报
回复
[Quote=引用 10 楼 dourgulf 的回复:]
maozefa
已经解析得很清楚了,只是对“使用插入汇编手工清除变参部分”认为不是很现实,本质上只有调用者(caller)负责处理参数的堆栈才可以正确实现变参数的清理,所以一个函数的编写者是无法在函数的实现中进行具体的参数堆栈清理工作(即使嵌入汇编),实际上C对变参数的支持是在编译的时候对调用参数加入清理堆栈的处理的:形式代码如下:
声明:
void func(int, ...);
调……
[/Quote]
对定义了变参的__sdtcall插入汇编手工清栈是可行的。只不过,我刚才想测试一下,却无法办到,因为C编译器遇到__stdcall中带变参,就自动按__cdecl处理了(我用的BCB2007)。我想这才是LZ“可以编译并运行成功”的真正原因。如果LZ会反汇编,如果查看一下你的函数最后的ret指令:如果是ren xxxx(数字)则是按__stdcall编译的,没有xxxx则是忽略了你的__stdcall定义!
漫步者、
2011-03-19
打赏
举报
回复
[Quote=引用 6 楼 testtest2002 的回复:]
可是我测试了这个代码,可以编译并运行成功,
void _stdcall OutputDebugStringf( char *fmt, ... )
{
va_list args;
char buf[DPRINTF_BUF_SZ];
va_start( args, fmt );
vsprintf( buf, fmt, args );
OutputDebugStringA( buf )……
[/Quote]//标准的调用函数,可变函数在stdrag.h文件中
Lactoferrin
2011-03-19
打赏
举报
回复
[Quote=引用 10 楼 dourgulf 的回复:]
maozefa
已经解析得很清楚了,只是对“使用插入汇编手工清除变参部分”认为不是很现实,本质上只有调用者(caller)负责处理参数的堆栈才可以正确实现变参数的清理,所以一个函数的编写者是无法在函数的实现中进行具体的参数堆栈清理工作(即使嵌入汇编),实际上C对变参数的支持是在编译的时候对调用参数加入清理堆栈的处理的:形式代码如下:
声明:
void func(int, ...);
调……
[/Quote]
可以在显式规定的参数中指明可变参数的个数,这样就可以手工退栈
子达如何
2011-03-19
打赏
举报
回复
maozefa
已经解析得很清楚了,只是对“使用插入汇编手工清除变参部分”认为不是很现实,本质上只有调用者(caller)负责处理参数的堆栈才可以正确实现变参数的清理,所以一个函数的编写者是无法在函数的实现中进行具体的参数堆栈清理工作(即使嵌入汇编),实际上C对变参数的支持是在编译的时候对调用参数加入清理堆栈的处理的:形式代码如下:
声明:
void func(int, ...);
调用:
func(foo,bar1,bar2);
汇编(伪代码):
PUSH foo
PUSH bar1
PUSH bar2
CALL FUNC
POP
POP
POP
如果是函数自己负责清理堆栈则:
void func(int foo, int bar1)
{
//实现
//编译器加入代码:
POP
POP
}
调用时候:
PUSH foo
PUSH bar1
CALL func
//OK
对比而言,后者产生的代码要小一点点
阿发伯
2011-03-19
打赏
举报
回复
补充一点:如果调用__stdcall函数后,使用插入汇编手工清除变参部分,也是可以的。这也就是为什么LZ“可以编译并运行成功”的原因。
阿发伯
2011-03-19
打赏
举报
回复
所以,__stdcall定义变参,不是编译系统支不支持的问题,而是不能在__stdcall中定义变参,这是一个基本常识。
阿发伯
2011-03-19
打赏
举报
回复
[Quote=引用 6 楼 testtest2002 的回复:]
可是我测试了这个代码,可以编译并运行成功,
void _stdcall OutputDebugStringf( char *fmt, ... )
{
va_list args;
char buf[DPRINTF_BUF_SZ];
va_start( args, fmt );
vsprintf( buf, fmt, args );
OutputDebugStringA( buf )……
[/Quote]
__cdecl是由调用函数者清栈(参数所占内存),这个编译系统可以帮你处理;_stdcall是由被调用函数清栈,而被调用函数只能按固定形参个数清除,如此一来,变参部分所占内存会一直保存在程序栈中而得不到清除。
如果频繁使用变参调用这个函数,将会使系统崩溃!因为C/C++的栈是很有限的。
testtest2002
2011-03-19
打赏
举报
回复
可是我测试了这个代码,可以编译并运行成功,
void _stdcall OutputDebugStringf( char *fmt, ... )
{
va_list args;
char buf[DPRINTF_BUF_SZ];
va_start( args, fmt );
vsprintf( buf, fmt, args );
OutputDebugStringA( buf );
char buf1[1024];
sprintf( buf1,"file : %s (%d)", __FILE__,__LINE__);
OutputDebugStringA(buf1);
}
这是为什么???
ouyh12345
2011-03-18
打赏
举报
回复
不能,只有__cdecl支持变参
「已注销」
2011-03-18
打赏
举报
回复
The __stdcall calling convention is used to call Win32 API functions. The callee cleans the stack, so the compiler makes vararg functions __cdecl.
hedy007
2011-03-18
打赏
举报
回复
vc++ 6.0可以的。
superarhow
2011-03-18
打赏
举报
回复
不行。因为stdcall是由被调用端清理堆栈的,被调用端是不知道调用端传了多少参数的。不过可以用变通的方法实现。
bdmh
2011-03-18
打赏
举报
回复
应该不支持
函数的调用规则(__
cd
ecl
,__
std
call,__fastcall,__pascal)
函数的调用规则(__
cd
ecl
,__
std
call,__fastcall,__pascal) 关于函数的调用规则(调用约定),大多数时候是不需要了解的,但是如果需要跨语言的编程,比如VC写的dll要delphi调用,则需要了解。 microsoft的vc默认的是__
cd
ecl
方式,而windows API则是__
std
call,如果用vc开发dll给其他语言用,则应该指定__
std
call方式。堆栈由谁清除这个很重要,如果是要写汇编函数给C调用,一定要小心堆栈的清除工作,如果是__
cd
ecl
方式的函数,则函数本身(如果不用汇编写)则不需要关心保存参数的堆栈的清除,但是如果是__
std
call的规则,一定要在函数退出(ret)前恢复堆栈。
__
cd
ecl
与__
std
call区别[项目代码]
本文详细介绍了C/C++中两种主要的函数调用约定__
cd
ecl
和__
std
call的区别与联系。__
cd
ecl
是C/C++默认的调用约定,参数从右至左入栈,由调用者清理堆栈,适用于
可变参数
函数,但生成的可执行文件较大。__
std
call是Win32 API的标准调用方式,参数同样从右至左入栈,但由被调用函数清理堆栈,必须在编译时确定参数个数,调用者需严格控制参数传递。两者在参数入栈顺序上相同,但在堆栈清理主体、参数个数要求和代码空间占用上有显著差异。此外,文章还简要介绍了其他调用约定如__fastcall、__thiscall和naked call的特点及适用场景,并讨论了跨平台调用和DLL导出时的注意事项。
关于函数调用方式__
std
call和__
cd
ecl
详解
下面小编就为大家带来一篇关于函数调用方式__
std
call和__
cd
ecl
详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
C/C++中__
std
call
使用
时机[可运行源码]
本文探讨了C/C++中__
std
call调用方式的
使用
时机及其与__
cd
ecl
的区别。__
std
call参数从右向左传递,由被调用函数清退堆栈,适用于COM组件和DLL,以确保与其他语言的兼容性。相比之下,__
cd
ecl
由调用函数清退堆栈,适用于
可变参数
函数如printf。文章还通过一个有趣的例子展示了调用方式对参数传递顺序的影响,进一步说明了理解调用规范的重要性。
__
std
call调用约定、C调用约定和__fastcall调用约定
__
std
call调用约定、C调用约定和__fastcall调用约定 __
std
call调用约定、C调用约定和__fastcall调用约定 __
std
call调用约定、C调用约定和__fastcall调用约定 __
std
call调用约定、C调用约定和__fastcall调用约定
C语言
70,035
社区成员
243,246
社区内容
发帖
与我相关
我的任务
C语言
C语言相关问题讨论
复制链接
扫一扫
分享
社区描述
C语言相关问题讨论
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章