Chinaunix首页 | 论坛 | 博客
  • 博客访问: 492621
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1693
  • 用 户 组: 普通用户
  • 注册时间: 2013-09-13 09:23
个人简介

前EMC高级软件工程师,现小米分布式存储码农,关注分布式存储,文件系统,Linux内核。微博: http://weibo.com/u/2203007022

文章分类

全部博文(17)

文章存档

2015年(1)

2014年(6)

2013年(10)

分类: C/C++

2014-05-03 00:55:04

博客:
微博:           


(1) C语言可变参数

我们可以从C语言的printf得出可变参数的作用,printf函数的原型如下:
int printf ( const char * format, ... );
通过使用可变个数参数,就是传入的参数个数是可变的,如printf需要根据format实参传入多个实参。

(2) C语言可变参数的使用

下面一个函数myprintf是自己实现的比较简单的printf函数,不完整但是可以说明可变参数的用法。
点击(此处)折叠或打开
  1. /*
  2.  * Author: guojun07
  3.  */

  4. #include <stdio.h>
  5. #include <stdarg.h>
  6. #include <unistd.h>
  7. #include <string.h>

  8. void myprintf(char *format, ...) {
  9.   va_list ap;
  10.   int pos = 0;
  11.   int int_val = 0;
  12.   float f_val;
  13.   char buf[64];
  14.   memset(buf, 0, 64);
  15.   // 得到所有的参数放到下一个list中ap中
  16.   va_start(ap, format);
  17.   while (format[pos] != '\0') {
  18.     // 判断'%',表示要得到下一个参数
  19.     if (format[pos] == '%') {
  20.       pos ++;
  21.       switch(format[pos]) {
  22.         case 'd':
  23.         case 'u':
  24.           // 得到ap中的下一个参数
  25.           int_val = va_arg(ap, int);
  26.           sprintf(buf, "%d", int_val);
  27.     // 将数据写到标准输出
  28.           write(STDOUT_FILENO, buf, strlen(buf));
  29.           memset(buf, 0, 64);
  30.           pos ++;
  31.     break;
  32.         case 'f':
  33.           // 得到ap中的下一个参数
  34.           f_val = (float)va_arg(ap, double);
  35.           sprintf(buf, "%f", f_val);
  36.     // 将数据写到标准输出
  37.           write(STDOUT_FILENO, buf, strlen(buf));
  38.           memset(buf, 0, 64);
  39.           pos ++;
  40.           break;
  41.         default:
  42.           break;
  43.       }
  44.     } else {
  45.       write(STDOUT_FILENO, &(format[pos]), 1);
  46.       pos ++;
  47.     }
  48.   }
  49. }

  50. int main(void){
  51.   myprintf("this is a testing, i = %d, u = %u, f = %f\n", -1, 5, 0.2);
  52.   return 0;
  53. }

程序的数据结果如下:
guojun8@guojun8-desktop:~/test/valist$ ./main
this is a testing, i = -1, u = 5, f = 0.200000

(3) 实现

下面介绍C语言可变长度参数的实现,其实现与一个数据结构(va_list)和三个宏(va_start, va_end, va_arg)相关,从源码中可以看到这些实现下面的来自linux内核源码中的文件(include/acpi/platform/acenv.h)
点击(此处)折叠或打开
  1. #ifndef _VALIST
  2. #define _VALIST
  3. typedef char *va_list;
  4. #endif /* _VALIST */

  5. /*
  6.  * Storage alignment properties
  7.  */
  8. #define _AUPBND (sizeof (acpi_native_int) - 1)
  9. #define _ADNBND (sizeof (acpi_native_int) - 1)

  10. /*
  11.  * Variable argument list macro definitions
  12.  */
  13. #define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
  14. #define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
  15. #define va_end(ap) (void) 0
  16. #define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
a) va_list
从实现中可以看出va_list类型实际上就是一个指针;
b) va_start
这个宏的作用是将T所指向的参数后面的内容放到ap中,其中_bnd (A,_AUPBND)是返回A的size并与系统的机器位数对齐,因为参数在栈中的地址一定是与系统的字长对齐的,其中acpi_native_int就表示机器字长;
c) va_end
这个宏的作用就是返回0;
d) va_arg
个宏的作用是取得ap指向的当前的参数,并将ap指向参数列表中的下一个参数;  
阅读(8297) | 评论(2) | 转发(6) |
给主人留下些什么吧!~~

guojun072014-05-05 15:03:30

Ubuntu_LW:请问一下,这个va_list(char *)是不是就是指的是最后一个具体参数之后,最后一个括号之前之间的字符串,对应上面的例子就是“-1,5,0.2”?

这个char*不是指向字符串的,而是指向压入栈中的参数,C语言默认调用方式是从后向前压入堆栈,va_start(ap, A)调用后,这个指针的内容是-1这个参数在栈中的地址。

回复 | 举报

Ubuntu_LW2014-05-05 14:22:21

请问一下,这个va_list(char *)是不是就是指的是最后一个具体参数之后,最后一个括号之前之间的字符串,对应上面的例子就是“-1,5,0.2”?