可變參數函數

計算機程序設計,一個可變參數函數是指一個函數擁有不定引數,即是它接受一個可變數目的參數。不同的程式語言對可變參數函數的支持有很大差異。

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

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

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

例子

C/C++

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

要創建一個可變參數函數,必須把省略號(...)放到參數列表後面。函數內部必須定義一個va_list變數。然後使用巨集va_startva_argva_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++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 (){}]

參見

外部連結