Chinaunix首页 | 论坛 | 博客
  • 博客访问: 988616
  • 博文数量: 158
  • 博客积分: 4380
  • 博客等级: 上校
  • 技术积分: 2367
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-21 10:45
文章分类

全部博文(158)

文章存档

2012年(158)

我的朋友

分类: C/C++

2012-11-15 10:55:23

  变参数函数例如printf平时也许用得很多,但她存在一个问题,假如在编译之前不知道参数数目就无法调用,比如有一个函数testfun,它的参数数目不定,参数(s)是运行时从配置文件中得到,那么怎样调用这个函数呢?一个想法简单但无法圆满实现的做法:

返回类型 testfun( int 编号, ... )
{
    ......
}
int main( void )
{
    返回类型 rv;
    if( 运行时知道此编号需要1个参数 )
        rv = testfun( 此编号, 参数1 );
    else if( 运行时知道此编号需要2个参数 )
        rv = testfun( 此编号, 参数1, 参数2 );
    else if( 运行时知道此编号需要3个参数 )
        rv = testfun( 此编号, 参数1, 参数2, 参数3 );
    ……
}

这样的代码即使你能容忍它的体积,你也无法全部写完,因为即使你写了100行能处理100个参数,但你也无法保证实际不会出现101个参数。
C/C++语法对这个问题是无能为力,但C/C++扩展是提供ASM支持的,如果不考虑移植的问题的话可以自己模拟_cdecl的操作

返回类型 testfun( int 编号, ... )
{
    ......
}
int main( void )
{
    for( int i=0; i<运行时知道此编号需要的参数数目n; ++i )
        __asm push 参数n-i                // 这只是个演示,后面将仔细说明
    返回类型 rv = testfun( 此编号 );
    __asm add esp, 所有参数占用的栈字节数 // 这只是个演示,后面将仔细说明

}

这样就简洁多了,且无论参数数目有多少都能全部解决,只是使用汇编模拟了一下_cdecl的压栈出栈操作而已。

说明:
[1]:只要是变参数函数就一定是_cdecl方式,即使申明它为_stdcall也没用。
[2]:_cdecl的实参是从右向左依次压入栈,由调用者清理栈,所以在函数结束之后需要将esp加上所有参数占用的栈字节数,这其中返回地址和第一个参数占用的栈字节数在这段代码中会由编译器自动释放,另一个更简单的做法是在push参数之前先保存好esp值,在函数调用结束之后再将esp值恢复。
[3]:参数压栈的规则:
    a. 整型量如果不足int长度,就扩展成int/unsigned。(整型量指的是char、unsigned、enum等)(扩展成int/unsigned和以int长度对齐是不同的含义)。
    b. 实参按int长度对齐(主要指自定义类型)。
    c. float类型扩展成double入栈。
变参数压栈的特别规则:
    a. 对于自定义类型,只进行位拷贝,而不调用copy constructor。
[4]:类成员函数也可以是变参数函数。
[5]:ASM扩展不属于标准C++语法,因此不能移植,如果在GCC中就应该使用AT&T汇编,而不是Intel汇编。
阅读(2119) | 评论(15) | 转发(0) |
给主人留下些什么吧!~~

网友评论2012-11-15 10:58:42

ishou
Sorry!
上面的
“如果定义的回调函数的参数个数,比实际需要的参数个数多,比如, 定义8个参数的回调函数 强制用于仅需要4个参数的窗口函数, 程序可以正常运行。。。。。。"

有误,应该是:
-------------------------
该 回调函数 必须是 变参数函数。如果是普通固定个数参数的函数,
程序不能运行,会崩溃!

网友评论2012-11-15 10:58:23

ishou
又,
如果定义的回调函数的参数个数,比实际需要的参数个数多,比如,
定义8个参数的回调函数 强制用于仅需要4个参数的窗口函数,
程序可以正常运行(当然,在程序里不能操作后面的4个参数),但是

也不知道会有什么后遗症?

网友评论2012-11-15 10:58:05

ishou
请教星星:

变参数函数可以 成为回调函数 吗?比如:

LRESULT CALLBACK( int n, ...)

粗看起来好象不行。我曾经用于窗口回调函数,程序可以正常运行,但是不知道会有什么后遗症?

如果可以,有时候很有实用价值!

网友评论2012-11-15 10:57:48

ilovevc
我的意思是将参数放入一个不定长的数组中,类似main函数原型。例如

testFun(int argv, int args[])
{
int first = args[0];
const char * second = args[1];
}

调用:
int args[2];
args[0] = 100;
args[1] = "abcd";
testFun(2, args);

网友评论2012-11-15 10:57:32

兔狸熊
Loki库里已经实现了