c可變引數研究

2021-09-30 08:42:36 字數 3116 閱讀 7479

們知道va_start,va_arg,va_end是在stdarg.h中被定義成巨集的,  

由於1)硬體平台的不同   2)編譯器的不同,所以定義的巨集也有所不同,下  

面以vc++中stdarg.h裡x86平台的巨集定義摘錄如下(』/』號表示折行):  

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)主要是為了某些需要記憶體的對齊的系統.c語言的函  

數是從右向左壓入堆疊的,圖(1)是函式的引數在堆疊中的分布位置.我  

們看到va_list被定義成char*,有一些平台或作業系統定義為void*.再  

看va_start的定義,定義為&v+_intsizeof(v),而&v是固定引數在堆疊的  

位址,所以我們執行va_start(ap,   v)以後,ap指向第乙個可變引數在堆  

棧的位址,如圖:  

高位址|-----------------------------|  

|函式返回位址   |  

|-----------------------------|  

|.......   |  

|-----------------------------|  

|第n個引數(第乙個可變引數)   |  

|-----------------------------| <--va_start後ap指向  

|第n-1個引數(最後乙個固定引數)|  

低位址|-----------------------------| <--   &v  

圖(   1   )  

然後,我們用va_arg()取得型別t的可變引數值,以上例為int型為例,我  

們看一下va_arg取int型的返回值:  

j=   (   *(int*)((ap   +=   _intsizeof(int))-_intsizeof(int))   );  

首先ap+=sizeof(int),已經指向下乙個引數的位址了.然後返回  

ap-sizeof(int)的int*指標,這正是第乙個可變引數在堆疊裡的位址  

(圖2).然後用*取得這個位址的內容(引數值)賦給j.  

高位址|-----------------------------|  

|函式返回位址   |  

|-----------------------------|  

|.......   |  

|-----------------------------| <--va_arg後ap指向  

|第n個引數(第乙個可變引數)   |  

|-----------------------------| <--va_start後ap指向  

|第n-1個引數(最後乙個固定引數)|  

低位址|-----------------------------| <--   &v  

圖(   2   )  

最後要說的是va_end巨集的意思,x86平台定義為ap=(char*)0;使ap不再  

指向堆疊,而是跟null一樣.有些直接定義為((void*)0),這樣編譯器不  

會為va_end產生**,例如gcc在linux的x86平台就是這樣定義的.  

在這裡大家要注意乙個問題:由於引數的位址用於va_start巨集,所  

以引數不能宣告為暫存器變數或作為函式或陣列型別.  

關於va_start,   va_arg,   va_end的描述就是這些了,我們要注意的  

是不同的作業系統和硬體平台的定義有些不同,但原理卻是相似的.  

(三)可變引數在程式設計中要注意的問題  

因為va_start,   va_arg,   va_end等定義成巨集,所以它顯得很愚蠢,  

可變引數的型別和個數完全在該函式中由程式**控制,它並不能智慧型  

地識別不同引數的個數和型別.  

有人會問:那麼printf中不是實現了智慧型識別引數嗎?那是因為函式  

printf是從固定引數format字串來分析出引數的型別,再呼叫va_arg  

的來獲取可變引數的.也就是說,你想實現智慧型識別可變引數的話是要通  

過在自己的程式裡作判斷來實現的.  

另外有乙個問題,因為編譯器對可變引數的函式的原型檢查不夠嚴  

格,對程式設計查錯不利.如果******_va_fun()改為:  

void   ******_va_fun(int   i,   ...)  

可變引數為char*型,當我們忘記用兩個引數來呼叫該函式時,就會出現  

core   dump(unix)   或者頁面非法的錯誤(window平台).但也有可能不出  

錯,但錯誤卻是難以發現,不利於我們寫出高質量的程式.  

以下提一下va系列巨集的相容性.  

system   v   unix把va_start定義為只有乙個引數的巨集:  

va_start(va_list   arg_ptr);  

而ansi   c則定義為:  

va_start(va_list   arg_ptr,   prev_param);  

如果我們要用system   v的定義,應該用vararg.h標頭檔案中所定義的  

巨集,ansi   c的巨集跟system   v的巨集是不相容的,我們一般都用ansi   c,所以  

用ansi   c的定義就夠了,也便於程式的移植.    

C語言可變引數研究

一 何謂可變引數 int printf const char format,這是使用過c語言的人所再熟悉不過的printf函式原型,它的引數中就有固定引數format和可變引數 用 表示 而我們又可以用各種方式來呼叫printf,如 printf d value printf s str print...

可變引數研究

可變引數的研究,可變引數底層是陣列,確定這個結論之後要實際驗證測試 package day9yue1 public class test1 如果這2個方法都叫test,編譯錯誤,說明引數是一模一樣,沒有過載 結論 string s 和 string.s 結果是一樣的 public void test...

C中的可變引數研究

一 何謂可變引數 int printf const char format,這是使用過c語言的人所再熟悉不過的printf函式原型,它的引數中就有固定引數format和可變引數 用 表示 而我們又可以用各種方式來呼叫printf,如 printf d value printf s str print...