可變參數函數
![本頁使用了標題或全文手工轉換](http://images.weserv.nl/?url=//upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Zh_conversion_icon_m.svg/35px-Zh_conversion_icon_m.svg.png)
在計算機程式設計,一個可變參數函數是指一個函數擁有不定引數,即是它接受一個可變數目的參數。不同的程式語言對可變參數函數的支援有很大差異。
一般而言,在設計函數時會遇到許多數學和邏輯操作,是需要一些可變功能。例如,計算數字串的總和、字串的聯接或其他操作過程,都可以存在任意數量的參數。
另一種許多語言都實現為可變參數函數的是格式輸出函數,在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++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 (){}]
參見
外部連結
- Variadic function (頁面存檔備份,存於互聯網檔案館). Rosetta Code task showing the implementation of variadic functions in over fifty programming languages.
- Variable Argument Functions — A tutorial on Variable Argument Functions for C++
- GNU libc manual (頁面存檔備份,存於互聯網檔案館)