结合print讲解va_start和va_end使用

来源:互联网 发布:mac双系统怎么设置默认系统 编辑:程序博客网 时间:2024/05/16 17:38

1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表

void foo(...);
void foo(parm_list,...);
这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。


2.函数参数的传递原理

  函数参数是以数据结构:栈的形式存取,从右至左入栈。

  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, float y, char z);
  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
  下面是 <stdarg.h> 里面重要的几个宏定义如下:
typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ANSI version */
type va_arg ( va_list ap, type ); 
void va_end ( va_list ap ); 
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
<Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。

 例子:

  1. #include <stdio.h>  
  2. #include <stdarg.h>  
  3.   
  4.   
  5. //va_start(arg,format),初始化参数指针arg,将函数参数format右边第一个参数地址赋值给arg  
  6. //format必须是一个参数的指针,所以,此种类型函数至少要有一个普通的参数,   
  7. //从而提供给va_start ,这样va_start才能找到可变参数在栈上的位置。   
  8. //va_arg(arg,char),获得arg指向参数的值,同时使arg指向下一个参数,char用来指名当前参数型  
  9. //va_end 在有些实现中可能会把arg改成无效值,这里,是把arg指针指向了 NULL,避免出现野指针   
  10.   
  11.   
  12. void print(const char *format, ...)  
  13. {  
  14.     va_list arg;  
  15.     va_start(arg, format);  
  16.   
  17.     while (*format)  
  18.     {  
  19.         char ret = *format;  
  20.         if (ret == '%')  
  21.         {  
  22.             switch (*++format)  
  23.             {  
  24.             case 'c':  
  25.             {  
  26.                         char ch = va_arg(arg, char);  
  27.                         putchar(ch);  
  28.                         break;  
  29.             }  
  30.             case 's':  
  31.             {  
  32.                         char *pc = va_arg(arg, char *);  
  33.                         while (*pc)  
  34.                         {  
  35.                             putchar(*pc);  
  36.                             pc++;  
  37.                         }  
  38.                         break;  
  39.             }  
  40.             default:  
  41.                 break;  
  42.             }  
  43.         }  
  44.         else  
  45.         {  
  46.             putchar(*format);  
  47.         }  
  48.         format++;  
  49.     }  
  50.     va_end(arg);  
  51. }  
  52. int main()  
  53. {  
  54.     print("%s %s %c%c%c%c%c!\n""welcome""to"'C''h''i''n''a');  
  55.     system("pause");  
  56.     return 0;  
  57. }
输出结果:

wKioL1ZQfY2hQOOGAAAZkTUFq_w029.png


printf输出类型简单介绍:

printf(“格式控制字符串”, 输出表列)
其中格式控制字符串用于指定输出格式。格式控制串可由格式字符串和非格式字符串两种组成。格式字符串是以%开头的字符串,在%后面跟有各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。如:

  • “%d”表示按十进制整型输出;
  • “%ld”表示按十进制长整型输出;
  • “%c”表示按字符型输出等。


类型字符用以表示输出数据的类型,其格式符和意义如下表所示:格式字符意义d以十进制形式输出带符号整数(正数不输出符号)o以八进制形式输出无符号整数(不输出前缀0)x,X以十六进制形式输出无符号整数(不输出前缀Ox)u以十进制形式输出无符号整数f以小数形式输出单、双精度实数e,E以指数形式输出单、双精度实数g,G以%f或%e中较短的输出宽度输出单、双精度实数c输出单个字符s输出字符串




参考资料:

http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html

http://blog.csdn.net/iynu17/article/details/51588199

0 0