va_list ,va_start ,va_arg ,va_copy ,va_end ,vsprintf ,vsnprintf 详细解析

1定义

这些宏在stdarg.h,定义如下:

 1 #include <stdarg.h>  
 2 typedef char *va_list;
 3 #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
 4 #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) //第一个可选参数地址
 5 #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ) //下一个参数的值
 6 #define va_end(ap) ( ap = (va_list)0 ) // 将指针置为无效
 7 
 8 void va_start(va_list ap, last);     
 9 type va_arg(va_list ap, type);       
10 void va_end(va_list ap);   
11 void va_copy(va_list dest, va_list src);

以上几个宏定义关键是_INTSIZEOF(n)的含义。

2 #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

含义是向上取整成 sizeof(int) 的整数倍,用来地址对齐。

1)  数学整除的例子

举个简单的数学整除运算例子

如5/2=2, 7/2=3,再进行如下运算:

(5/2)*2=4, (7/2)*3=6

即可得到小于被除数的最大的除数的整数倍。

2)字节对齐的含义:

现在有以数据长度为L = 4M+m,如果按照M字节对齐, 对齐后需占用5个M,但实际长度记为4M+m。

 

如上,蓝色一个格为M字节,红色为m字节,m < M。

经过上述数学整除运算后L变成4M = (L/M) * M。

但是与所需的5M 少了一个M。那怎么办呢?

如果将原数据长度增加一格(L+M),再进行上述运算,即((L+M)/M) * M = 5M

3)但是呢,会存在如下的问题

假设数据长度为Y = 6M,刚好为一格长度M的整数倍。

经过以上运算为:((Y+M)/M) * M = 7M,那如果不加M ,只加M-1呢?

((Y+M-1)/M) * M = 6M,对头,这样就完美了。

4)言归正传,解释宏定义。

(sizeof(n)+sizeof(int)-1) & (~(sizeof(int) - 1)) = _INTSIZEOF(n)

((Y+M-1)                        /M) * M                    = 6M

&符号左边部分相当于(Y+M-1),右边&~(sizeof(int) - 1),对应/M*M。对二进制来说就是右移M位,再左移M位。因为右移会丢掉低位的,再次左移后相当于把低位清零了。

若a为2的n次幂,那就是最高位为1,其余位即低位为n个0。

a-1的二进制相反,低位n个1,最高位为0。再按位取反就是a,即(~(sizeof(int) - 1))。

最后再按位与,就可以实现将A的低n位清零,即&(~(sizeof(int) - 1))。

3 函数堆栈中的摆放

在进程中,堆栈地址是从高到低分配的.当执行一个函数的时候,将参数列表入栈,压入堆栈的高地址部分,然后入栈函数的返回地址,接着入栈函数的执行代码 ,堆栈地址不断递减。最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分.参数在堆栈中的分布情况如下:

最后一个参数

倒数第二个参数

...

第一个参数

函数返回地址

函数代码段

void va_copy(va_list dest, va_list src);很简单,src copy到 dest,不在详述。

4实例

1)int型实例:

 1 #include <stdio.h>
 2 #include <stdarg.h>
 3 //int demo(int format, ...);
 4 int demo(int format, ...)
 5 {
 6   va_list ap;
 7   int n;
 8   va_start(ap, format);
 9   while(n != 0{
10     n = va_arg(list, int);
11    // if(n != 0){
12       printf("%d
",n);
13      // break;
14    // }
15     printf("para #%d is:%d ",sum,n);
16   }
17   va_end(ap);
18   return 0;
19 }
20 void main()
21 {
22   demo(1,2,3,4,5,6,7,8,9,0);
23 }

编译执行结果:

 ./test

para #0 is : 2

para #0 is : 3

para #0 is : 4

para #0 is : 5

para #0 is : 6

para #0 is : 7

para #0 is : 8

para #0 is : 9

para #0 is : 0

2)char型实例:

 1 #include <stdio.h>
 2 #include <stdarg.h>
 3 //int demo(int format, ...);
 4 void demo(char *format, ...)
 5 {
 6   va_list list;
 7   va_start(list,format);
 8   char *ch;
 9   while(ch != ""){
10     ch = va_arg(list, char *);
11     //if(strcmp(ch,"") == 0){
12      printf("%s
",ch);
1314     //}
15    printf("%s ",ch);
16   }
17   va_end(list);
18 }
19 int main()
20 {
21   demo ("test","this","is","a","test","");
22   return 0;
23 }

编译执行结果:

 ./test

para #0 is : this

para #0 is : is

para #0 is : a

para #0 is : test

para #0 is : 

3)vsprintf ,vsnprintf的使用实例

 1 #include <stdio.h>
 2 #include <stdarg.h>
 3 char buffer1[80] 4 char buffer2[80];
 5 int demo (char *format, ...)
 6 {
 7   va_list ap;
 8   va_start(ap, format);
 9   vsfprintf(buffer1, format,ap);
10   vsnprintf(buffer2, 17,format, ap);
11   va_end(ap);
12   printf("vsprintf:%s 
",buffer1);
13   printf("vsnprintf:%s 
",buffer2);
14   return 0;
15 }
16 int main()
17 {
18   int num = 30;
19   float flat = 90.000000;
20   char c_data [4]= "abc";
21   demo ("%d %f %s",num,flat,c_data);
22   printf("sizeof(int):%d,sizeof(float):%d,sizeof(string):%d 
",sizeof(int),sizeof(float),sizeof(string));
23   return 0;
24 }

sizeof(int):4,sizeof(float):4,sizeof(string):4

vsprintf :30 90.000000 abc

vsnprintf:30 90.000000 abc

行胜于言,自强不息。
原文地址:https://www.cnblogs.com/xinghuo123/p/12862871.html