C语言中,确切说,是标准库里,对于变类型变参量是通过头文件<stdarg.h>中的一组宏实现的,定义了如何遍历参数。
其中,定义了一个类型va_list,用于声明(不是定义)一个变量,该变量将依次引用各个参数。
在Linux中,可以通过
$man 3 va_arg
来查看相关的手册。
在<stdarg.h>中定义了几个宏,分别是va_start, va_arg, va_end,va_copy,其中va_copy是在C99时引入的,早期的C89并没有定义这个宏,所以在经典的《the c programming language》中看不到va_copy的身影。
void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); void va_copy(va_list dest, va_list src);
va_start宏,用于初始化va_list类型变量ap,把ap指向已知类型的最后一个变量,ap在后续调用va_arg和var_end时会使用到。
va_arg宏,用于返回ap指向变量的下一个变量,并且把ap指向当前变量的下一个变量(ap初始指向已知类型的最后一个变量),依次
调用va_arg,可变参量就会依次被遍历。
va_end宏,用于清理ap等工作。
一个简单的应用就是《the c programming language》里面的miniprintf()函数的例子:
#include<stdarg.h> #include<stdio.h> /*miniprintf , show some functionalities of std printf()*/ void miniprintf(char *fmt, ...) { va_list ap; char *p, *sval; int ival; double dval; va_start(ap, fmt); for( p = fmt; *p; p++) { if (*p != '%') { putchar(*p); continue; } switch (*++p) { case 'd': ival = va_arg(ap, int); printf("%d", ival); break; case 'f': dval = va_arg(ap, double); printf("%f", dval); break; case 's': for(sval = va_arg(ap,char *); *sval; sval++) putchar(*sval); break; default: putchar(*p); break; } } va_end(ap); }
va_copy宏用于的情况是:有时需要把一个va_list类型的变量赋值给另外一个va_list变量,
一般的可以这么写:
va_list aq ;
ap = ap;
但是有些系统的va_list的实现为指针数组(当然数组长度为1),所以此时就该这么写:
va_list aq;
*aq = *ap;
还有的机器中,参数是在寄存器(registers)传递的,此时,就要为va_start分配内存,存储参数,等等。比较复杂!!
所以C99就提出了一个新的宏,用来实现跨机器的va_list型值的操作----------va_copy(va_list dest, va_list src)
********************************************************************************************************************************
另:其实va_list在出错处理或者需要IO交互时,用的很常见。下面的代码片段是从nano-2.2.6的源代码里切出来的(nano-2.2.6/src/nano.c)
nano实现了一个die()函数,功能很简单,就向代码注释中说的:使nano优雅的死亡。
/* Make nano die gracefully. */ void die(const char *msg, ...) { va_list ap; /*声明一个va_list类型变量*/ endwin(); /* Restore the old terminal settings. */ tcsetattr(0, TCSANOW, &oldterm); va_start(ap, msg); /*初始化ap*/ vfprintf(stderr, msg, ap); /* 和fprintf()函数功能相同,只不过是...变成了ap*/ va_end(ap); /*完成,ap变成未定义的了*/ /* Save the current file buffer if it's been modified. */ if (openfile && openfile->modified) { /* If we've partitioned the filestruct, unpartition it now. */ if (filepart != NULL) unpartition_filestruct(&filepart); die_save_file(openfile->filename #ifndef NANO_TINY , openfile->current_stat #endif ); } #ifdef ENABLE_MULTIBUFFER /* Save all of the other modified file buffers, if any. */ if (openfile != NULL) { openfilestruct *tmp = openfile; while (tmp != openfile->next) { openfile = openfile->next; /* Save the current file buffer if it's been modified. */ if (openfile->modified) die_save_file(openfile->filename #ifndef NANO_TINY , openfile->current_stat #endif ); } } #endif /* Get out. */ exit(1); }