变长参数引起的问题
来源:互联网 发布:网络卫星直播电视 编辑:程序博客网 时间:2024/04/29 21:30
http://blog.csdn.net/akirya/archive/2009/03/19/4005258.aspx
首先我们写一个这样的函数,
void Sum(int *e, ...),计算除第一个参数外的和,第一个参数为最终的结果.
很快写下这样的代码
- void Sum(int *e, ...)
- {
- assert( e );
- int n = 0;
- va_list ap;
- va_start(ap, e);
- do{
- n = va_arg(ap, int);
- *e = *e + n ;
- }while( n );
- }
- int main(int argc,char* argv[])
- {
- int s = 0;
- Sum( &s , 1,2,3, 0 );
- printf( "%d/n" ,s );
- return 0;
- };
经测试一切都没问题.这时候发现第一个参数是指针形式.看起来不太爽.于是改成了引用形式.
改成如下形式
- void Sum(int &e, ...)
- {
- int n = 0;
- va_list ap;
- va_start(ap, e);
- do{
- n = va_arg(ap, int);
- e = e + n ;
- }while( n );
- }
- int main(int argc,char* argv[])
- {
- int s = 0;
- Sum( s , 1,2,3, 0 );
- printf( "%d/n" ,s );
- return 0;
- };
少了一个断言,少了几个*号,看起来比较简洁.
运行一下结果大出意料结果是1088609049而且每次都不一样.这是怎么回事?
问题出在使用引用的参数.
我们看一下va_start和va_arg的实现(在这里我只说明Win32系统VC9的情况),将最初正常运行的代码中的宏展开.(VC9下使用/P参数)展开后代码如下
- void Sum(int *e, ...)
- {
- assert( e );
- int n = 0;
- va_list ap;
- ( ap = (va_list)( &reinterpret_cast<const char &>(e) ) + ( (sizeof(e) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) );
- do{
- n = ( *(int *)((ap += ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) - ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) );
- *e = *e + n ;
- }while( n );
- }
将代码编译一下,增加临时变量,看看每一步的作用.
- void Sum(int *e, ...)
- {
- int n = 0;
- va_list ap;
- const char* lpAddressE = &reinterpret_cast<const char &>(e) ;
- ap = (va_list)( lpAddressE );
- ap +=( (sizeof(e) + sizeof(int) - 1) & ~(sizeof(int) - 1) );
- //ap +=( ( 4 + 4 - 1) & ~(4 -1) );
- //ap +=( ( 7 &~3 ) );
- //ap +=4;
- //( ap = (va_list)( &reinterpret_cast<const char &>(e) ) + ( (sizeof(e) + sizeof(int) - 1) & ~(sizeof(int) - 1) ) );
- do{
- //n = ( *(int *)((ap += ( (4 + 4 - 1) & ~(4 - 1) )) - ( (4 + 4 - 1) & ~(4 - 1) )) );
- //n=( *(int *)((ap += ( 7 & ~3 )) - ( 7 & ~3 )) );
- //n= *(int *)((ap += 4) - 4) ;
- ap +=4;
- n = * (int*)( ap-4 );
- //n = ( *(int *)((ap += ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) - ( (sizeof(int) + sizeof(int) - 1) & ~(sizeof(int) - 1) )) );
- *e = *e + n ;
- }while( n );
- }
可以看到先取e的地址,然后将地址的值+4,其中va_list是别名定义如下typedef char * va_list;
我们先来分析一下,在进入函数调用的时候栈是下面这种情况
return address//低地址
e
1
2
3
0 //高地址
而va_start的作用就是1的地址赋值给ap,
在va_arg的时候现将ap的地址+4,然后取ap-4位置的内容
也就是说va_arg的时候得到1,然后ap的值+4
依次调用就能变长参数的所有参数值.
va_arg宏中的一系列sizeof计算则保证了移动的时候肯定是4的倍数.
到这里就会清楚在使用 引用做开始参数的时候.也就是va_start的时候会对参数取地址
非引用的时候就会取到在栈上面的参数的地址.要是引用的时候就有问题了,取到的是main函数中s变量的值.
以这个错误的开始地址取内容所得到的结果自然就不对了.
- 变长参数引起的问题
- _kgl_large_heap_warning_threshold参数引起的问题
- 参数赋值引起的问题
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的 Tracer
- 变长参数的实现
- 变长参数的使用
- 小制作- -屏幕粉笔 TOM搬家
- 中国网友这样说奥巴马访华
- Network 开发
- x
- CSDN今天抛异常了 (图)
- 变长参数引起的问题
- 轻轻松松 用U盘安装WIN7
- 好生意一年历程(5/10)
- MFC钩子简单应用
- sockaddr与sockaddr_in结构体简介
- struts2对对象的强大支持
- 小制作- -炫彩水晶钟 TOM搬家
- 类成员函数大小问题
- delphi中如何让主窗体在程序运行之初不显示