专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »博文摘选 » 可变参数函数:有关C中函数的可变参数va_list...(转) »正文

可变参数函数:有关C中函数的可变参数va_list...(转)

来源: 发布时间:星期日, 2009年9月27日 浏览:2次 评论:0
◎用法: func( Type para1, Type para2, Type para3, ... ) { /****** Step 1 ******/ va_list ap; va_start( ap, para3 ); //一定要“...”之前的那个参数 /****** Step 2 ******/ //此时ap指向第一个可变参数 //调用va_arg取得里面的值 Type xx = va_arg( ap, Type ); //Type一定要相同,如: //char *p = va_arg( ap, char *); //int i = va_arg( ap, int ); //如果有多个参数继续调用va_arg /****** Step 3 ******/ va_end(ap); //For robust! } ◎研究: typedef char * va_list; #define va_start _crt_va_start #define va_arg _crt_va_arg #define va_end _crt_va_end #define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) ) #define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define _crt_va_end(ap) ( ap = (va_list)0 ) va_list argptr; C语言的函数是从右向左压入堆栈的,调用va_start后, 按定义的宏运算,_ADDRESSOF得到v所在的地址,然后这个 地址加上v的大小,则使ap指向第一个可变参数如图: 栈底 高地址 | ....... | 函数返回地址 | ....... | 函数最后一个参数 | .... | 函数第一个可变参数 <--va_start后ap指向 | 函数最后一个固定参数 | 函数第一个固定参数 栈顶 低地址 然后,用va_arg()取得类型t的可变参数值, 先是让ap指向下一个参数: ap += _INTSIZEOF(t),然后在减去_INTSIZEOF(t),使得表达式结果为 ap之前的值,即当前需要得到的参数的地址,强制转换成指向此参数的 类型的指针,然后用*取值 最后,用va_end(ap),给ap初始化,保持健壮性。 example:(chenguiming) #include #include #include #include int average( int first, ... ) //变参数函数,C++里也有 { int count=0,i=first,sum=0; va_list maker; //va_list 类型数据可以保存函数的所有参数,做为一个列表一样保存 va_start(maker,first); //设置列表的起始位置 while(i!=-1) { sum+=i; count++; i=va_arg(maker,int);//返回maker列表的当前值,并指向列表的下一个位置 } return sum/count; } void main(void) { printf( "Average is: %d\n", average( 2, 3, 4,4, -1 ) ); } 先来个简单的例子: #include #include int sum(int num,...); int sum(int num,...) { int result = 0; va_list argptr; va_start(argptr, num); while(num--) { //result += va_arg(argptr, int); printf("%s ",va_arg(argptr, char *)); } va_end(argptr); return result; } int main() { sum(3, "hello", "world", "!"); // output: hello world ! //printf("%d\n", sum(4, 1, 2, 3 ,4)); //printf("%d\n", sum(2, 1, 2, 3 ,4)); return 0; } 可变参数中个数不定可是传入的是一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活。 可变参数是由宏实现的,但是由于硬件平台的不同,编译器的不同,宏的定义也不相同,下面是VC6.0中x86平台的定义: typedef char * va_list; // TC中定义为void* #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) ) //为了满足需要内存对齐的系统 #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 ) C语言的函数形参是从右向左压入堆栈的,以保证栈顶是第一个参数,而且x86平台内存分配顺序是从高地址到低地址。因此似函数fun(int var1,int var2,...,int varN)内存分配大致上是这样的:(可变参数在中间) 栈区: |栈顶 低地址 |第一个参数var1 <-- &v |第二个参数var2 <-- va_start(ap,v)后ap指向地址 |... |函数的最后varN |... |函数的返回地址 |... |栈底 高地址 va_start(ap,v);后ap = (va_list)&v + _INTSIZEOF(v)指向第二个参数地址 调用va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数 不过被定义为宏的东西用起来要小心,我现在用不着va_list,不过先了解点皮毛也好。 下面是msdn中的例子: #include #define ANSI /* Comment out for UNIX version */ #ifdef ANSI /* ANSI compatible version */ #include int average( int first, ... ); #else /* UNIX compatible version */ #include int average( va_list ); #endif void main( void ) { /* Call with 3 integers (-1 is used as terminator). */ printf( "Average is: %d\n", average( 2, 3, 4, -1 ) ); /* Call with 4 integers. */ printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) ); /* Call with just -1 terminator. */ printf( "Average is: %d\n", average( -1 ) ); } /* Returns the average of a variable list of integers. */ #ifdef ANSI /* ANSI compatible version */ int average( int first, ... ) { int count = 0, sum = 0, i = first; va_list marker; va_start( marker, first ); /* Initialize variable arguments. */ while( i != -1 ) { sum += i; count++; i = va_arg( marker, int); } va_end( marker ); /* Reset variable arguments. */ return( sum ? (sum / count) : 0 ); } #else /* UNIX compatible version must use old-style definition. */ int average( va_alist ) va_dcl { int i, count, sum; va_list marker; va_start( marker ); /* Initialize variable arguments. */ for( sum = count = 0; (i = va_arg( marker, int)) != -1; count++ ) sum += i; va_end( marker ); /* Reset variable arguments. */ return( sum ? (sum / count) : 0 ); } #endif 在来一个简单的例子: #include #include void print(char *format,...); //自定义输出格式 void print(char *format,...) { va_list argptr; va_start(argptr, format); while(*format != '\0') { switch(*(format++)) { case 's': printf("%s ", va_arg(argptr, char *)); break; case 'i': printf("%d ", va_arg(argptr, int)); break; case 'c': printf("%c ", va_arg(argptr, char)); break; case 'f': printf("%.1f\n", va_arg(argptr, double)); break; default: break; } } va_end(argptr); } int main() { print("sicft","laomou",24,'M',120.0); // 输出格式依次为 string, integer, char, float return 0; } 转自:http://www.cnblogs.com/AndyGe/archive/2009/09/09/1563372.html
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: