C語言中的可變參數va(va_arg) 大家應該比較熟悉了,主要是用來解決函數參數類型與個數不確定的問題,基本用法與詳細請移步這裡 http://www.BkJia.com/kf/201202/119885.html
通常的用法是把va_arg放在等號右邊,提取其值來使用:
[cpp]
func( Type para1, Type para2, Type para3, ... )
{
/****** Step 1 ******/
va_list ap;
va_start( ap, para3 ); //一定要“...”之前的那個參數
/****** Step 2 ******/
//此時ap指向第一個可變參數
//調用va_arg取得裡面的值
Type xx = va_arg( ap, Type );
//Type一定要相同,如:
//char *p = va_arg( ap, char *);
//int i = va_arg( ap, int );
//如果有多個參數繼續調用va_arg
/****** Step 3 ******/
va_end(ap); //For robust!
}
這裡介紹va的另一種用法:把va_arg()當左值使用:
[cpp]
<pre class="cpp" name="code">//函數定義:
PassInto(ID, ...)
{
va_list valist;
va_start(valist, ID);
*(uint32_t*)va_arg(valist, uint32_t*) = 10;
va_end(valist);
}
//調用:
uint32_t value = 0;
PassInto(id, &value);
printf("%d", value);
得到結果是value被賦值為10。
可以看到,這裡va_arg被放到了等號左邊,好像很奇怪的用法,實際上分析一下,同放在右邊原理都是一樣的。因為不論va的原理如何(見上面的鏈接),
C語言的函數歸根到底還是值傳遞的,在第一種通常的用法中,va值被取出來後賦值給其它變量,在第二種用法中,&value被當作va傳了進去,
它的值其實就是一個指向value的指針,所以 va_arg(valist, uint32_t*) 取出的是一個uint32_t的指針,*(uint32_t*)va_arg(valist, uint32_t*) =10 即把這個指針強制類型轉換為(uint32_t*)並加*號間接引用,
這樣實際得到了變量value,現在把它賦值為10,就等價於
[cpp]
value = 10;
這樣就完成了我們的全部操作,實現了對任意類型和個數的參數進行賦值。
這個方式超級靈活,但是注意不要進行下面的危險操作:
[cpp]
uint16_t value = 0; //這裡有變化
PassInto(id, &value);
後果可能很嚴重。為什麼呢?因為參數類型不匹配,16位的參數被強迫當作32位來使用。我們的value同志此時只從系統那裡申請到了16bit的空間,而
[cpp]
*(uint32_t*)va_arg(valist, uint32_t*) =10
把10塞到了從value開始的32位空間,那麼value之後的那16bit是哪裡呢?由誰在用?不得而知。
如果運氣好,沒有人在用,程序無事,如果數據在用,那麼數據會變化,系統在用,系統也跑偏。
反正我用的時候它沒和我客氣,系統直接崩潰了。
摘自 herbert的知識庫