va_list 、va_start、 va_arg、 va_end 使用說明【轉】

  • 2019 年 10 月 11 日
  • 筆記

轉自:https://blog.csdn.net/f110300641/article/details/83822290

在ANSI C中,這些宏的定義位於stdarg.h中:

typedef char *va_list;

va_start宏,獲取可變參數列表的第一個參數的地址(list是類型為va_list的指針,param1是可變參數最左邊的參數):

#define va_start(list,param1) ( list = (va_list)&param1+ sizeof(param1) )

va_arg宏,獲取可變參數的當前參數,返回指定類型並將指針指向下一參數(mode參數描述了當前參數的類型):

#define va_arg(list,mode) ( (mode *) ( list += sizeof(mode) ) )[-1]

va_end宏,清空va_list可變參數列表:

#define va_end(list) ( list = (va_list)0 )

註:以上sizeof()只是為了說明工作原理,實際實現中,增加的位元組數需保證為為int的整數倍

如:#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) – 1) & ~(sizeof(int) – 1) )

為了理解這些宏的作用,我們必須先搞清楚:C語言中函數參數的記憶體布局。首先,函數參數是存儲在棧中的,函數參數從右往左依次入棧。

以下面函數為討論對象:

void test(char *para1,char *param2,char *param3, char *param4) { va_list list; …… return; }

在linux中,棧由高地址往低地址生長,調用test函數時,其參數入棧情況如下:

當調用va_start(list,param1) 時:list指針指向情況對應下圖:

最複雜的宏是va_arg。

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

附:可變參數應用實例

1.printf實現

  1. #include <stdarg.h>
  2. int printf(char *format, …)
  3. {
  4. va_list ap;
  5. int n;
  6. va_start(ap, format);
  7. n = vprintf(format, ap);
  8. va_end(ap);
  9. return n;
  10. }

2.訂製錯誤列印函數error

  1. #include <stdio.h>
  2. #include <stdarg.h>
  3. void error(char *format, …)
  4. {
  5. va_list ap;
  6. va_start(ap, format);
  7. fprintf(stderr, "Error: ");
  8. vfprintf(stderr, format, ap);
  9. va_end(ap);
  10. fprintf(stderr, "n");
  11. return;

【作者】張昺華

【出處】http://www.cnblogs.com/sky-heaven/