c語言不定參數宏,va

關於INTSIZEOF宏是怎麼來的已經解釋過了, 這裡簡要概述一下,宏定義如下:

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

當sizeof(n)<4時,宏的值就是4,當sizeof(n)>4時,宏的值就是4的倍數。

這裡主要介紹兩個宏va_start和va_arg兩個宏,先說這個va_start。

宏定義如下:

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ),這裡的va_list的類型是

char *,v是輸入的第一個參數。首先在理解這個宏的前提要理解函數的形參是如何存儲,這裡我們做個試驗如下圖所示:

c語言不定參數宏,va_start,va_arg的來歷解釋

關於形參的地址值

形參是存儲在棧裡面的,棧的高地址在上,低地址在下,那麼由上圖可以得到的是,x,y,z在棧中存儲的結構從上到下依次為z,y,x。那麼(va_list)&v的意思就是取第一個參數的地址並強制轉化為char*類型。比如這裡的x的地址為1638116(十進制表示的)被強制轉化為char*,對於_INTSIZEOF(v),若是這裡的v也為x的話,那麼這個宏的值為4。進而兩者相加

的值就是1638120。這裡要說明的是地址的類型為char*的話,地址加1就相當於只是加1,比如這裡的1638116 + 4 = 1638120;如果地址的類型是int *那麼加1的意思就是地址+1*4。為什麼???是這樣的:比如一個指針指向一個int的輸入比如int a[10],那麼每一個元素就佔4個字節所以地址+1中的1表示1*4,所謂的指針加幾就是為了向前或者向後移動,那麼移動多少字節就要取決於指針指向的變量的類型了,這樣解釋明白了吧!!!很基礎是吧,比如數組是char型的,那麼加1就是地址純粹的加1了,因為sizeof(char) = 1的嘛。

好了,繼續上面說的得到了地址1638116 + 4 = 1638120,那麼這個1638120不正好就是y的地址值嗎。所以這個宏的功能就是:輸入第n個參數,輸出的就是第n + 1個參數的地址值

再來介紹va_arg宏,它的定義如下:

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) ),這裡的t輸入的是變量的類型,比如int,char等等之類的。ap += _INTSIZEOF(t)的意思讓指針ap指向下一個參數的地址,在強調一遍高地址存的是函數越右邊的形參值(因為形參是存在棧中的),比如假設ap指向的是x的地址,要使得ap指向y,那麼由於x是在低地址那麼就要加上x的類型,比如這裡的x類型為int,所以這個時候ap是指向y的了。再減去_INTSIZEOF(t)(注意此時ap還是指向的y)那麼((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))整個地址值就還原到原來的地方,也就是再次指向了x,為什麼又要回來,其實就是為了使得ap指向下一個地址,然後再次回到原來的地址,在經過強制轉化(t *),然後再取值*(t *)的符號“*”。

我們來捋一捋這個過程,假設函數只有x,y,z三個形參,一般先調用va_start使得ap指向了y,然後再調用va_arg,使得ap指向z,由於y的值沒取出來進行計算,那麼,就要把地址還原也就是上面再減去_INTSIZEOF(t),在取出y的值。那麼這裡就有一個問題是x的值並沒用到,別急看完下面的例子就明白怎麼用x的值了。

我們來舉一個例子來應用一下,我們要實現,不管輸入多少個參數,使得函數形參從第二個參數開始起進行累加,如下圖所示:

c語言不定參數宏,va_start,va_arg的來歷解釋

function函數形參x表示的是有多少個參數,因為在執行va_start的時候就直接使得ap的指針指向了下一個形參的位置,所以可以利用x的值來表示有多少個參數,比如這裡有4個參數,分別為0,1,2,3,4,那麼function的功能就是把這幾個值相加也就是6了。

所以若是有興趣可以自己寫一個printf的函數了!!!


分享到:


相關文章: