存取變長參數(shù)列表va_start,va_end,va_arg


1.在C中,變長參數(shù)的函數(shù)即參數(shù)個數(shù)可變、參數(shù)類型不定 的函數(shù)。當我們無法列出傳遞函數(shù)的所有實參的類型和數(shù)目時,以三個點結束該函數(shù)的聲明,指定參數(shù)表.

int InitArray(Array &A,int dim,...);

// printf函數(shù)的聲明

int printf(const char *? _Format,?...);

//scanf函數(shù)聲明

int scanf(const char *? _Format,?...);

//自定義變長參數(shù)函數(shù)func的聲明

int func(int a,int b,...);

? ? ? ? 在C/C++中,任何使用變長參數(shù)聲明的函數(shù)都必須至少有一個指定的參數(shù)(又稱強制參數(shù)),即至少有一個參數(shù)的類型是已知的,而不能用三個點省略所有參數(shù)的指定,且已知的指定參數(shù)必須聲明在函數(shù)最左端。即int func(...);是非法的,int func(...,int a);是非法的。

2.含有變長參數(shù)的函數(shù)是怎么實現(xiàn)的呢?

?????? 變長參數(shù)函數(shù)的實現(xiàn)其實關鍵在于怎么使用參數(shù),指定了的參數(shù)好說,直接使用指定的參數(shù)名稱訪問,但未指定的參數(shù)呢?

#include <stdio.h>

void foo(int x, int y, int z)

{

printf("x = %d at [%X]/n", x, &x);

printf("y = %d at [%X]/n", y, &y);

printf("z = %d at [%X]/n", z, &z);

}

int main(int argc, char *argv[])

{

foo(100, 200, 300);

return 0;

}

運行結果:

x = 100 at [BFE28760]

y = 200 at [BFE28764]

z = 300 at [BFE28768]

? ? ? ? 我們知道入棧時,棧頂從高地址向低地址方向增長。而函數(shù)調用過程中參數(shù)傳遞是通過棧來實現(xiàn)的,一般調用都是從右至左的順序壓參數(shù)入棧。通過棧堆分析可知,自左向右的入棧方式,最前面的參數(shù)被壓在棧底。除非知道參數(shù)個數(shù),否則是無法通過棧指針的相對位移求得最左邊的參數(shù)。這樣就變成了左邊參數(shù)的個數(shù)不確定,正好和動態(tài)參數(shù)個數(shù)的方向相反。因此,C語言中函數(shù)參數(shù)的入棧順序是從右至左正好便利了動態(tài)變化參數(shù)個數(shù)。不管你有多少個參數(shù)反正將最右面的那個壓入棧底,最左面的參數(shù)出入棧頂。參數(shù)與參數(shù)之間是相鄰的,知道前一個參數(shù)的類型及地址,根據(jù)后一個參數(shù)的類型就可以獲取后一個參數(shù)的內容。對于變長參數(shù)函數(shù),結合一定的條件,我們可以根據(jù)最后一個指定參數(shù)獲取之后的省略參數(shù)內容。

??????? int func(int a,int b,...);

?????? 如,對于函數(shù)func,我們知道了參數(shù)b的地址及類型,就可知道第一個可變參數(shù)的棧地址(如果有的話),如果知道第一個可變參數(shù)的類型,就可知道第一個可變參數(shù)的內容和第二個可變參數(shù)的地址(如果有的話)。以此類推,可以實現(xiàn)對可變參數(shù)函數(shù)的所有參數(shù)的訪問。

??????? 那么,要怎么指定上訴的“一定的條件”呢?最簡單的方法就像printf等函數(shù)一樣,使用格式化占位符。分析格式化字符串參數(shù),通過事先定義好的格式化占位符可知可變參數(shù)的類型及個數(shù),從而獲取各個參數(shù)內容。一般對于可變參數(shù)類型相同的函數(shù)也可直接在強制參數(shù)中指定可變參數(shù)的個數(shù)和類型,這樣也能獲取各個參數(shù)的內容。

????? 無論哪種,都涉及對棧地址偏移的操作。結合棧存儲模式和系統(tǒng)數(shù)據(jù)類型的字長,我們可根據(jù)可變參數(shù)的類型很容易得到棧地址的偏移量。使用va_start、va_arg、va_end三個標準宏來實現(xiàn)棧地址的偏移及獲取可變參數(shù)內容。這三個宏定義在stdarg.h頭文件中,可根據(jù)預先定義的系統(tǒng)平臺自動獲取相應平臺上各個數(shù)據(jù)類型的偏移量。

類型 va_list

保有 va_start 、 va_arg 、 va_end 和 va_copy 所需的信息

va_start? 令對可變函數(shù)參數(shù)的訪問可行??????????? void? va_start(va_list ap,last);

va_arg 訪問下一個可變函數(shù)參數(shù)? ? ? ? ? ? ? ? ? ? ? ? type? va_arg(va_list ap,type);

va_copy (C++11)制造可變函數(shù)參數(shù)的副本? ? ?? void? va_copy(va_list dest,va_list src);

va_end 結束可變參數(shù)函數(shù)的遍歷??????????????????????? void? va_end(va_list ap);

? ? ? ? 概要:調用函數(shù)必須聲明一個va_list類型的變量,以供宏va_start(),va_arg()和va_end()使用。va_list 是一個字符指針,可以理解為指向當前參數(shù)的一個指針,取參必須通過這個指針進行。在調用參數(shù)表之前,定義一個 va_list 類型的變量,(假設va_list 類型變量被定義為ap);

? ? ? ? 對ap 進行初始化,讓它指向可變參數(shù)表里面的第一個參數(shù),這是通過 va_start 來實現(xiàn)的,第一個參數(shù)是 ap 本身,第二個參數(shù)是在變參表前面緊挨著的一個變量,即“...”之前的那個參數(shù);初始化va_list變量,參數(shù)last是變長參數(shù)表前的最后一個知道類型的變量名;

? ? ? ? 獲取參數(shù),調用va_arg,它的第一個參數(shù)是ap,第二個參數(shù)是要獲取的參數(shù)的指定類型,然后返回這個指定類型的值,并且把 ap 的位置指向變參表的下一個變量位置;獲取下一個變長參數(shù)表里的參數(shù),需要指定類型,以決定返回的對象類型和指針移動的步長;

???????? 獲取所有的參數(shù)之后,我們有必要將這個 ap 指針關掉,以免發(fā)生危險,方法是調用 va_end,他是輸入的參數(shù) ap 置為 NULL,應該養(yǎng)成獲取完參數(shù)表之后關閉指針的習慣。通常va_start和va_end是成對出現(xiàn)。


//訪問可變參數(shù)流程

va_listargs;//定義一個可變參數(shù)列表

va_start(args,arg);//初始化args指向強制參數(shù)arg的下一個參數(shù);

va_arg(args,type);//獲取當前參數(shù)內容并將args指向下一個參數(shù)

...//循環(huán)獲取所有可變參數(shù)內容

va_end(args);//釋放args

?????? 注意: 對于可變參數(shù)函數(shù)的調用有一點需要注意,實際的可變參數(shù)的個數(shù)必須比前面強制參數(shù)中指定的個數(shù)要多,或者不小于,也即后續(xù)參數(shù)多一點不要緊,但不能少,如果少了則會訪問到函數(shù)參數(shù)以外的堆棧區(qū)域,這樣程序可能崩掉。前面強制參數(shù)中指定的類型和后面實際參數(shù)的類型不匹配也有可能造成程序崩潰。

變長參數(shù)函數(shù)與默認參數(shù)函數(shù)

擁有變長參數(shù)的函數(shù)在聲明定義時其參數(shù)個數(shù)與類型是不定的,在運行調用時參數(shù)的狀態(tài)則是一定的。而默認參數(shù)函數(shù)在聲明定義時其參數(shù)類型與個數(shù)都是一定的,只是后面部分參數(shù)指定了默認值,可通過省略(不指定)部分參數(shù)調用這個默認參數(shù)函數(shù)。但是默認參數(shù)函數(shù)還是使用了聲明中指定的全部參數(shù),編譯器自動給后部分參數(shù)賦了默認值;而變長參數(shù)函數(shù)則僅僅使用了運行調用時提供的參數(shù)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 我們在C語言編程中會遇到一些參數(shù)個數(shù)可變的函數(shù),例如printf() 這個函數(shù),它的定義是這樣的: int pri...
    Mr_Me閱讀 846評論 0 2
  • Exercise 3 方法:打開終端運行make qemu-gdb,再打開另一個終端運行make gdb,通過b ...
    找不到工作閱讀 10,325評論 0 6
  • 題目: 某些電商網(wǎng)站會將篩選模塊放在搜索結果集的上面,某些網(wǎng)站會將篩選模塊放在結果集的左邊,而甚至有些網(wǎng)站,例如e...
    doctor_wang閱讀 3,454評論 3 38
  • 所有偏愛 都是一見鐘情 我偏愛冰淇淋。 偏愛魚。 偏愛芝士蛋糕。 偏愛童話故事勝過娛樂八卦。 偏愛粉色。 偏愛做白...
    游走星宿閱讀 568評論 1 2
  • 最早接觸到龕是小時候和外婆一起住的時候,外婆信佛,每天早晚念經(jīng)的時候我偶爾會在一旁看著,壁龕上供著幾尊菩薩,...
    渡邊志明閱讀 561評論 0 2

友情鏈接更多精彩內容