社区
C语言
帖子详情
__stdcall 可以像__cdecl 使用可变参数吗??
testtest2002
2011-03-18 03:39:04
像这样:
void __cdecl operator()(DWORD_PTR dwCategory, UINT nLevel, const char *pszFmt, ...)
...全文
219
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支持变参
proghua
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
打赏
举报
回复
应该不支持
浅谈C/C++中
可变
参数
的原理
要理解
可变
参数
,首先要理解函数调用约定, 为什么只有
__cdecl
的调用约定支持
可变
参数
,而
__stdcall
不支持? 实际上
__cdecl
和
__stdcall
函数
参数
都是从右到左入栈,它们的区别在于由谁来清栈,
__cdecl
由外部调用函数清栈,而
__stdcall
由被调用函数本身清栈, 显然对于
可变
参数
的函数,函数本身没法知道外部函数调用它时传了多少
参数
,所以没法支持被调用函数本身清栈(
__stdcall
), 所以
可变
参数
只能用
__cdecl
l。 另外还要理解函数
参数
传递过程中堆栈是如何生长和变化的,从堆栈低地址到高地址,依次存储 被调用函数局部变量,上一函数堆栈桢基址,函数
C#通过PInvoke调用c++函数的备忘录的实例详解
目前知道的情况被调用的C/C++函数只能是全局函数 不能调用类中的成员方法 被调用的C函数必须
使用
extern “C“包含,保证采用的导出函数名生成规则和.NET一致 函数调用约定通常
使用
WINAPI也就是
__stdcall
,.net默认也是
__stdcall
.net可以和c++同时用cdecl调用约定,这样可以支持
可变
参数
个数 c函数必须
使用
__declspec(dllexport)前缀来导出 PInvoke assistant工具可以辅助生成C#和VB的引入声明,还可以查看常见的常量枚举 能否调用重载的c++函数导出还没有试验,目前找到的信息看还是不行 字符串只支持C里的char* w_
MingW VC 之.a .lib .dll .def 关系
MSVC vs. MinGW 之 (lib,dll,def,obj,exe) vs (a,dll,def,o,exe) 玩转攻略手记 一份粗糙的研究记录,有待补完和整理。 MinGW: c -> o gcc -c a.c c -> exe gcc a.c libs.o -o a.exe (从主程序a.c,附加libs,生成a.exe) o -> exe gcc a.o b.o ... -o main.exe c -> dll,def,a gcc a.c -shared -o a.dll -Wl,--output-def,a.def,--out-implib,liba.a a -> dll a2dll liba.a dll -> a: dlltool --dllname a.dll --def a.def --output-lib liba.a (需要def文件) a -> def: dumpbin /exports lib.a > lib.def (在windows上调用,def需要修改) dll -> def : pexports a.dll -o > a.def (这里的-o是指给函数标序号) lib -> def : reimp -d a.lib lib -> a: (for
__cdecl
functions in most case) reimp a.lib; (for
__stdcall
functions) MSVC: c -> lib cl /LD a.c (注意已经定义了export列表) c -> dll cl /LD a.c c -> obj cl /c a.c c -> exe cl a.c /out:a.exe dll ->lib lib /machine:ix86 /def:a.def /out:a.lib (需要def文件) obj ->lib lib a.obj b.obj... /out:mylib.lib dll ->def DUMPBIN a.dll /EXPORTS /OUT:a.def (生成的def需要做修正) lib ->def reimp -d a.lib (这个要在MSYS+MinGW下用) 关于这些工具的适用范围可以很容易的理解和记忆。 dll和exe都是PE文件,所以可以
使用
pexports. lib和a是静态库文件,都是归档类型,不是PE格式。所以不能
使用
pexports. dll可以
使用
dlltool. lib可以
使用
lib, 和reimp(lib->a工具) 所有的bin文件,包括dll,exe,lib,a都可以
使用
dumpbin. 参考: http://hi.baidu.com/kaien_space/blog/item/5e77fafa2ba9ff16a8d3110a.html Mingw官网文档: http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs http://oldwiki.mingw.org/index.php/CreateImportLibraries http://www.mingw.org/wiki/FAQ http://hi.baidu.com/opaquefog/blog/item/9b21b6deb324e25dccbf1ab7.html http://qzone.qq.com/blog/8330936-1238659272 http://hi.baidu.com/jzinfo/blog/item/b0aa1d308de99f9da8018e00.html 本篇测试用代码: 1. main.cpp #include #include #include "mylib.h" using namespace std; int main() { char str[]="Hello world!"; printhello(str); return 0; } 2. mylib.cpp #include #include #include "mylib.h" using namespace std; void EXPORT printhello(char *str) { cout << str << endl; } 3. mylib.h #define EXPORT __declspec(
关于
__stdcall
和
__cdecl
调用方式的理解
__stdcall
和
__cdecl
都是函数调用约定关键字,先给出这两者的区别,然后举实例分析:
__stdcall
:
参数
由右向左压入堆栈;堆栈由函数本身清理。
__cdecl
:
参数
也是由右向左压入堆栈;但堆栈由调用者清理。 另外,这两者在同一名字修饰约定下,编译过后变量和函数的名字也不一样,具体见另一博文:名字修饰约定extern "C"与extern "C+
__cdecl
和
__stdcall
的区别和联系
1、
__cdecl
和
__stdcall
__cdecl
是CDeclaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有
参数
从右到左依次入栈,这些
参数
由调用者清除,称为手动清栈。被调用函数不会要求调用者传递多少
参数
,调用者传递过多或者过少的
参数
,甚至完全不同的
参数
都不会产生编译阶段的错误。
__stdcall
是StandardCall的缩写,是C++的...
C语言
69,377
社区成员
243,075
社区内容
发帖
与我相关
我的任务
C语言
C语言相关问题讨论
复制链接
扫一扫
分享
社区描述
C语言相关问题讨论
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章