stdarg.h頭文件源代碼分析


談到C語言中可變參數函數的實現(參見C語言中可變參數函數實現原理),有一個頭文件不得不談,那就是stdarg.h

本文從minix源碼中的stdarg.h頭文件入手進行分析:

 1 #ifndef _STDARG_H
 2 #define _STDARG_H
 3 
 4 
 5 #ifdef __GNUC__
 6 /* The GNU C-compiler uses its own, but similar varargs mechanism. */
 7 
 8 typedef char *va_list;
 9 
10 /* Amount of space required in an argument list for an arg of type TYPE.
11  * TYPE may alternatively be an expression whose type is used.
12  */
13 
14 #define __va_rounded_size(TYPE)  \
15   (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
16 
17 #if __GNUC__ < 2
18 
19 #ifndef __sparc__
20 #define va_start(AP, LASTARG)                                           \
21  (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
22 #else
23 #define va_start(AP, LASTARG)                                           \
24  (__builtin_saveregs (),                                                \
25   AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
26 #endif
27 
28 void va_end (va_list);          /* Defined in gnulib */
29 #define va_end(AP)
30 
31 #define va_arg(AP, TYPE)                                                \
32  (AP += __va_rounded_size (TYPE),                                       \
33   *((TYPE *) (AP - __va_rounded_size (TYPE))))
34 
35 #else    /* __GNUC__ >= 2 */
36 
37 #ifndef __sparc__
38 #define va_start(AP, LASTARG)                         \
39  (AP = ((char *) __builtin_next_arg ()))
40 #else
41 #define va_start(AP, LASTARG)                    \
42   (__builtin_saveregs (), AP = ((char *) __builtin_next_arg ()))
43 #endif
44 
45 void va_end (va_list);        /* Defined in libgcc.a */
46 #define va_end(AP)
47 
48 #define va_arg(AP, TYPE)                        \
49  (AP = ((char *) (AP)) += __va_rounded_size (TYPE),            \
50   *((TYPE *) ((char *) (AP) - __va_rounded_size (TYPE))))
51 
52 #endif    /* __GNUC__ >= 2 */
53 
54 #else    /* not __GNUC__ */
55 
56 
57 typedef char *va_list;
58 
59 #define __vasz(x)        ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int) -1))
60 
61 #define va_start(ap, parmN)    ((ap) = (va_list)&parmN + __vasz(parmN))
62 #define va_arg(ap, type)      \
63   (*((type *)((va_list)((ap) = (void *)((va_list)(ap) + __vasz(type))) \
64                             - __vasz(type))))
65 #define va_end(ap)
66 
67 
68 #endif /* __GNUC__ */
69 
70 #endif /* _STDARG_H */
stdarg.h源代碼

從代碼中可以看到,里面編譯器的版本以及相關的大量宏定義

第5行: #ifdef __GNUC__
作用是條件編譯,__GNUC__為GCC中定義的宏。GCC的版本,為一個整型值。如果你需要知道自己的程序是否被GCC編譯,可以簡單的測試一下__GNUC__,假如你代碼需要運行在GCC某個特定的版本下,那么你就要小心了,因為GCC的主要版本在增加,如果你想定義宏的方式直接實現控制,你可以寫如下的代碼(參見伯克利大學網站):

/* 測試 GCC > 3.2.0 ? */
#if __GNUC__ > 3 || \
    (__GNUC__ == 3 && (__GNUC_MINOR__ > 2 || \
                       (__GNUC_MINOR__ == 2 && \
                        __GNUC_PATCHLEVEL__ > 0))

你還可以使用下面一個類似的方法:

#define GCC_VERSION (__GNUC__ * 10000 \
                     + __GNUC_MINOR__ * 100 \
                     + __GNUC_PATCHLEVEL__)
...
/*測試 GCC > 3.2.0 ?*/
#if GCC_VERSION > 30200

第8行: 使用typedef進行了一個聲明:typedef char *va_list;

第14行:定義了用於編譯器的內存對齊宏(參見C語言內存對齊詳解(3)):

#define __va_rounded_size(TYPE)  \
     (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

第17行:#if __GNUC__ < 2,進行GCC的版本判斷,看當前版本是否大於2

第19行:#ifndef __sparc__ 可擴充處理器架構宏(以后再深入研究)

第20行:使得ap指向函數中的第一個無名參數的首地址的宏:

#define va_start(AP, LASTARG)                                           \
   (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))

第31行:

#define va_arg(AP, TYPE)                                                \
   (AP += __va_rounded_size (TYPE),                                       \
    *((TYPE *) (AP - __va_rounded_size (TYPE))))

va_arg宏使得ap指向下一個參數,已經處理了內存對齊,其中參數的類型為TYPE

第48行:

void va_end (va_list);          /* Defined in gnulib */

定義在gnulib中,va_end 與va_start成對使用.在有些代碼中定義為:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM