可變參數函式

在電腦程式設計,一個可變參數函式是指一個函式擁有不定引數,即是它接受一個可變數目的參數。簡單來說,就是函式的參數個數可變,參數類型不定的函式。 不同的程式語言對可變參數函式的支持有很大差異。

簡介

在計算機編程時,可變參數函式中參數是一個可變數目,即這個函式擁有不定引數。

一般而言,在設計函式時會遇到許多數學和邏輯操作,是需要一些可變功能。例如,計算數字串的總和、字元串的聯接或其他操作過程,都可以存在任意數量的參數。

另一種許多語言都實現為可變參數函式的是格式輸出函式,在C語言的printf函式和Common Lisp的format函式就是例子。這些函式都需要一個參數,指定格式的輸出,再讀取可變參數的值進行格式化 。

另外,可變參數函式在某些語言存在安全問題。例如C語言在沒有長度檢查和類型檢查,在傳入過少的參數或不符的類型時可能會出現溢位的情況,更可能會被利用為攻擊目標。所以,在設計函式時可以先考慮其他替補方案,例如以類型安全的方式——重載。

C/C++中的舉例

在C語言中 ,C標準函式庫的stdarg.h標頭檔定義了提供可變參數函式使用的宏。在C++,應該使用標頭檔cstdarg。

要創建一個可變參數函式,必須把省略號(...)放到參數列表後面。函式內部必須定義一個va_list變數。然後使用宏va_start、va_arg和va_end來讀取。例如:

這個是一段計算平均數的程式碼,可以輸入任意數量的小數並計算平均數,注意計算以0停止計算。請注意,函式不知道參數的數量或它們的類型,這裡要求的類型是double,而且第一個參數傳遞可變參數的數量。在另外的情況下,例如printf,參數的數量和類型都設定在格式字元串中。在這兩種情況下,程式設計師實際上需要提供正確的參數,如果參數傳遞少了或參數的類型不正確,導致讀入記憶體的無效區(溢位),這樣會有安全漏洞如格式字元串攻擊。

C++中可變參數的函式是從C中繼承而來,可變參數的函式是指函式的參數個數可變,參數類型不定的函式。我們最常見的就是printf()。

可變參數函式實現原理

指定參數的函式實現很簡單,通過通過指定的參數名訪問就行了。但是如果不指定的呢?函式的調用的參數會進行壓棧處理。而對參數的壓棧是從右到左進行壓棧。而參數和參數之間存放是連續的,也就是說,只要知道第一個參數的地址和類型,以及其他參數的類型,就可以獲取各個參數的地址。

比如:

可變參數函式 可變參數函式

函式調用記憶體結構如右:

這裡的a是int型,b是char型。printf()有3個參數,一個const char*,一個是int,一個是char。所以參數壓棧的順序就是先將b入棧,再將a入棧,最後是format入棧,由於棧是向下(低地址)生長的,所以在知道了format的地址之後,所有的參數地址都可以計算出來。

聲明和定義

可變參數函式的聲明很簡單,對於不定參數部分用“...”表示即可。但是實現原理可以看到,第一個的參數的地址是必須提供的,也就是可變參數必須至少包含一個參數,這個參數用來定址,實現對所有參數的訪問。

當然通常也會在對第一個參數進行一些特殊處理以方便函式的實現,比如強制指定為參數個數,或者像printf一樣使用格式占位符來。

C++11/C++14中的舉例

C++11新增了initializer_list物件,當編譯器遇到大括弧陣列變成函式的參數時,會自動將大括弧陣列轉型成initializer_list物件,因此可以使用此原理來做出“相同型別”參數的可變參數函式。

JavaScript中的舉例

在JavaScript(簡稱js)中,函式的可變參數用法很巧妙 。

相關詞條

熱門詞條

聯絡我們