Chinaunix首页 | 论坛 | 博客
  • 博客访问: 987975
  • 博文数量: 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汇编。
阅读(2117) | 评论(15) | 转发(0) |
给主人留下些什么吧!~~

网友评论2012-11-15 10:59:56

周星星
“这个文章感觉提法不太对,因为要解决楼主所说的问题,可以有楼上各位的多种办法解决不一定非要象楼主一样的去做的。”
--- 首先,你还没有明白我所说的问题是什么?我说的是“在编译期不知道参数数目的情况下调用变参数函数”,而不是如何如何设计使得不出现变参数函数……。很多情况下,必须在别人的接口上工作,所以这种设计好不好与本篇文章无关。

“想问一下楼主。那么在testfun里,如何知道传入了多少个参数呢?:) ”
--- 同样,这个问题和“在编译期不知道参数数目的情况下调用变参数函数”是无关的。如果你实在好奇,那么我告诉你实际情况是,testfun函数根据第一个参数-“编号”-去配置文件内查询。

“main函数是否就是解决此问题的一个典型示范呢?:) ”
--- ilovevc已经问过了,但我的回答是完全两回事。变参数 和 数组 机理/实现手段上都完全不一样。变参数函数的栈数据是一组数据,而数组作参数时栈数据是一个地址,只不过这个地址指向一组数据而已。

网友评论2012-11-15 10:59:41

昆仑
main函数是否就是解决此问题的一个典型示范呢?:)

网友评论2012-11-15 10:59:26

昆仑
想问一下楼主。那么在testfun里,如何知道传入了多少个参数呢?:)

网友评论2012-11-15 10:59:11

昆仑
这个文章感觉提法不太对,因为要解决楼主所说的问题,可以有楼上各位的多种办法解决不一定非要象楼主一样的去做的。
不过楼主的这个文章还是有点东东在里面的 :)

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

wq
也不一定非得用系统堆栈,自己构造一个堆栈作为实参也可以,
这样不会影响移植性