可變參數函式

在電腦程式設計,一個可變參數函式是指一個函式擁有不定引數,即是它接受一個可變數目的參數。簡單來說,就是函式的參數個數可變,參數類型不定的函式。

不同的程式語言對可變參數函式的支持有很大差異。

基本介紹

  • 中文名:可變參數函式
  • 外文名:Variable parameter function
  • 定義:一個擁有不定引數的函式
  • 學科:計算機科學
簡介,C/C++中的舉例,可變參數函式實現原理,聲明和定義,C++11/C++14中的舉例,JavaScript中的舉例,

簡介

在計算機編程時,可變參數函式中參數是一個可變數目,即這個函式擁有不定引數。
一般而言,在設計函式時會遇到許多數學邏輯操作,是需要一些可變功能。例如,計算數字串的總和、字元串的聯接或其他操作過程,都可以存在任意數量的參數。
另一種許多語言都實現為可變參數函式的是格式輸出函式,在C語言的printf函式和Common Lisp的format函式就是例子。這些函式都需要一個參數,指定格式的輸出,再讀取可變參數的值進行格式化。
另外,可變參數函式在某些語言存在安全問題。例如C語言在沒有長度檢查和類型檢查,在傳入過少的參數或不符的類型時可能會出現溢位的情況,更可能會被利用為攻擊目標。所以,在設計函式時可以先考慮其他替補方案,例如以類型安全的方式——重載

C/C++中的舉例

在C語言中,C標準函式庫的stdarg.h標頭檔定義了提供可變參數函式使用的。在C++,應該使用標頭檔cstdarg。
要創建一個可變參數函式,必須把省略號(...)放到參數列表後面。函式內部必須定義一個va_list變數。然後使用va_start、va_arg和va_end來讀取。例如:
#include <stdio.h>
#include <stdarg.h>
double average(int count, ...); /* 函式聲明,計算參數的平均值。直到參數為0時停止計算 */
int main(void) /* 測試代碼 */
{        double avg;        avg = average(3, 2, 1, 5, 0);        printf("%f\n", avg);        return 0;
}
double average(int count, ...)
{        va_list ap;        int i, cnt = 0;  /* cnt 表示參數個數 */        double tot = 0;  /* 參數的和 */        va_start(ap, count);        for (i = count; i; i = va_arg(ap, int), cnt++) /* i為當前獲取參數的值 */                tot += i;        va_end(ap);  /* 將參數列表清空 */        return tot / cnt;
}
這個是一段計算平均數的程式碼,可以輸入任意數量的小數並計算平均數,注意計算以0停止計算。請注意,函式不知道參數的數量或它們的類型,這裡要求的類型是double,而且第一個參數傳遞可變參數的數量。在另外的情況下,例如printf,參數的數量和類型都設定在格式字元串中。在這兩種情況下,程式設計師實際上需要提供正確的參數,如果參數傳遞少了或參數的類型不正確,導致讀入記憶體的無效區(溢位),這樣會有安全漏洞如格式字元串攻擊。
C++中可變參數的函式是從C中繼承而來,可變參數的函式是指函式的參數個數可變,參數類型不定的函式。我們最常見的就是printf()。

可變參數函式實現原理

指定參數的函式實現很簡單,通過通過指定的參數名訪問就行了。但是如果不指定的呢?函式的調用的參數會進行壓棧處理。而對參數的壓棧是從右到左進行壓棧。而參數和參數之間存放是連續的,也就是說,只要知道第一個參數的地址和類型,以及其他參數的類型,就可以獲取各個參數的地址。
比如:
int printf(const char* format,...)
printf("%d and %c,a,b");
函式調用記憶體結構如右:
可變參數函式
這裡的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物件,因此可以使用此原理來做出“相同型別”參數的可變參數函式。
#include <iostream>
using namespace std;
template<typename T>
T sum(initializer_list<T> il) {    T data(0);    for (T i : il)        data += i;    return data;
}
int main() {    cout << sum( { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }) << endl;    return 0;
}

JavaScript中的舉例

在JavaScript(簡稱js)中,函式的可變參數用法很巧妙。
function test(){ // 不管函式是否聲明參數,所有的參數都會添加到arguments中,arguments以數組的形式將參數保存起來 var params = arguments; // 以數組的形式輸出 console.log(params);
};
test("a");// 結果:["a"]
test("a", 1);// 結果:["a", 1]
test("a", 1, function(){});// 結果:["a", 1, function (){}]

相關詞條

熱門詞條

聯絡我們