va_list va_start va_arg va_end va_copy vfprintf、vfnprintf 详细解析

1定义

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

typedef char *va_list;

#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 ) // 将指针置为无效

以上几个宏定义关键是_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。

 va_list va_start va_arg va_end va_copy vfprintf、vfnprintf 详细解析

如上,蓝色一个格为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 函数堆栈中的摆放

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

最后一个参数

倒数第二个参数

...

第一个参数

函数返回地址

函数代码段

4实例

1)int型实例:

#include <stdio.h>

#include <stdarg.h>

int demo(int format, ...);

int demo(int format, ...)

{

  va_list ap;

  int n;

  va_start(ap, format);

  while(1){

    n = va_arg(list, int);

    if(n != 0){

      printf("\n");

      break;

    }

    printf("%d  ",n);

  }

  va_end(ap);

  return 0;

}

void main()

{

  demo(1,2,3,4,5,6,7,8,9,0);

}

编译执行结果:

 

2)char型实例:

#include <stdio.h>

#include <stdarg.h>

int demo(int format, ...);

void demo(char *format, ...)

{

  va_list list;

  va_start(list,format);

  char *ch;

  while(1){

    ch = va_arg(list, char *);

   if(strcmp(ch,"") == 0){

     printf("\n");

     break;

   }

   printf("%s ",ch);

  }

  va_end(list);

}

int main()

{

  demo ("test","this","is","a","test","");

  return 0;

}

编译执行结果:

 

3)和vfprintf、vfnprintf的使用实例

#include <stdio.h>

#include <stdarg.h>

int demo(int format, ...);

char buffer[50];

void demo (char *format, ...)

{

  va_list ap;

  va_start(ap, format);

  fprintf(buffer, "Error: ");

  vfprintf(buffer, format, ap);

  va_end(ap);

  printf(“buffer%s \n”,buffer);

  return 0;

}

int main()

{

  demo ("","this","is","a","test","");

  return 0;

}

编译执行结果:

 

上一篇:Windows program design (Windows 程序设计提纲)第2章


下一篇:vs C#项目读取JSON配置文件