C言語で関数を作成する場合、printf関数のような引数が可変になる関数の作り方をサンプルプログラムとともに説明します
目次
可変引数を取る関数の宣言方法
関数の引数を可変にする場合の関数宣言は、以下のように宣言します
戻り値の型 関数名(引数の型 引数名, …)
例)
int sum(int n, ...)
- 関数の引数すべてを可変引数にすることはできない(最低1個の引数は必要)
- 可変引数は、関数の引数の最後でなければならない
- 可変引数は、複数個使用できない
宣言できない例
int badCase1(…)
int badCase2(int p1, … , int pend)
int badCase3(int p1, … , int p2, …)
可変引数部分の値を取得するには
可変引数の型は、va_list型で宣言し、値を取得するには、以下のマクロを使用する必要があります
- va_start
- va_arg
- va_end
va_startマクロは、va_argマクロで引数の値を取得する前に使用します
形式
#include <stdarg.h>
void va_start(va_list ap, 最終仮引数);
最終仮引数は、仮引数(…)の前の引数のこと
va_argマクロは、可変引数の値を順番に取得するために使用します
形式
#include <stdarg.h>
型 va_arg(va_list ap, 型);
va_endマクロは、va_startマクロを使用した後、va_argマクロの使用を終えた時に状態を復帰するため使用します
形式
#include <stdarg.h>
void va_end(va_list ap);
可変引数を取る関数のサンプル
可変引数を使用するには、最低1個の引数が必要になるので、その引数は可変引数の個数(va_argマクロの使用回数)を指定するために使用します
#include <stdio.h>
#include <stdarg.h>
// 可変引数の関数のプロトタイプ宣言
int add(int n, ...);
/*
* 可変引数のサンプルプログラム
*/
int main(int argc, char *argv[]) {
//可変引数部分の個数0個のケース
printf("test 1:%d\n", add(0));
//可変引数部分の個数1個のケース
printf("test 2:%d\n", add(1, 10));
//可変引数部分の個数2個のケース
printf("test 3:%d\n", add(2, 10, 20));
//可変引数部分の個数3個のケース
printf("test 4:%d\n", add(3, 10, 20, 30));
//可変引数部分の個数4個のケース
printf("test 5:%d\n", add(4, 10, 20, 30, 40));
}
/*
* 可変引数を受け取る関数の定義
* 可変引数で指定された値の合計を計算して戻り値として返す
*/
#define MAX_ARGS 3 /* 可変引数の最大個数 */
int add(int n, ...)
{
int total = 0;
va_list ap;
va_start(ap, n);
// MAX_ARGSを超えない可変引数の値を取得
for(int i = 0; (i < n) && (i < MAX_ARGS); i++) {
total += va_arg(ap, int);
}
va_end(ap);
return total;
}

test 4とtest 5の結果が一緒になっているのは、可変引数を受け取る側の関数で可変引数の最大数を決めているので、最大数を超えた可変引数が渡されても超えた分の処理を行わないようにしているからです
今回のサンプルプログラムでは、可変引数の値を配列に入れたりしていないので特に気にする必要もないのですが、可変引数の値を配列等の変数に格納する場合などは、プログラミングを簡単にするためにも最大数を決めておいた方が良いです
printf関数は、可変引数の個数を指定するパラメータは無いですがそれは、format文字列の中の書式を調べて可変引数の個数と型を判断しています
関数の引数を可変する方法のまとめ
関数の引数を可変引数にする場合の定義は、以下のように定義する
戻り値の型 関数名(引数の型 引数名, …)
可変引数(…)部分は、最後でなければならず、複数個使用する事もできない
関数の引数をすべて可変引数にすることはできない(最低1個の引数は必要)
可変引数の型は、va_list型
関数の引数を可変にするには、stdarg.hで定義されている以下のマクロを使用する必要がある
- va_start
- va_arg
- va_end
va_startマクロは、va_argマクロを使う前に使用して初期化しなければならない
va_endマクロは、va_startマクロで初期化した後、va_argマクロの使用を終えた後に使用して、va_startマクロを使用する前の状態に戻す