名称
varargs ― 可変個の引き数リストの取り扱い
構文
#include <varargs.h>
va_alist
va_dcl
void va_start(pvar)
va_list pvar;
type va_arg(pvar, type)
va_list pvar;
void va_end(pvar)
va_list pvar;
説明
この一連のマクロはプログラマ に移植可能な可変個の引き数リストを受け付けるプロシージャを書くことを可能にします。
可変個の引き数を持つ定期的な (例えば printf())
で varargs を使わないものは、異なったマシンでは異なった
引き数渡し規約 を使っているため、本来移植不可能です printf(3S) を参照してください)。
va_alist は関数ヘッダでパラメータ リストとして使われます。
va_dcl は va_alist の宣言です。 va_dcl の後にはセミコロンを付けてはいけません。
va_list はリストを走査していくために使われる変数用に定義された型です。
va_start は pvar を初期化するために、リストの最初で呼び出されます。 argN の型は、関数の引き数リストの可変部分の直前の引き数と同じでなければなりません。
va_arg は pvar の指し示すリストの次の引き数を返します。 type は引き数に期待される型です。 異なった型を混ぜてもかまいませんが、どういう型の引き数が期待されているかを実行時に判別することはできませんので、それを知ることはルーチンの責任です。
va_end は終了処理を行います。
va_start ... va_end,
で囲って複数の操作を行うこともできます。
注記: <varargs.h>
ヘッダーファイルは、ANSI 以前のコンパイラおよび HP C/HP-UX の以前のリリースとの互換性を保つために提供されています。このファイルは、 varargs マクロをすべて含む <stdarg.h>
に置き換えられています。
例
次の 例は execl() の実現方法です exec(2) を参照してください)。
#include <varargs.h>
#define MAXARGS 100
/* execl is called by
execl(file, arg1, arg2, ..., (char *)0);
*/
execl(va_alist)
va_dcl
{
va_list ap;
char *file;
char *args[MAXARGS];
int argno = 0;
va_start(ap);
file = va_arg(ap, char *);
while ((args[argno++] = va_arg(ap, char *)) != (char *)0);
va_end(ap);
return execv(file, args);
}
次の例は可変個の引き数 を受け取る関数が、その引き数をどのように他の関数に渡すことができるかを示しています。
これを実現するために、可変個の引き数リストを受け取る最初のルーチン (この例の log_errors())
が、 va_start() の返り値であるアドレス ポインタを、同じ可変個の
引き数リストにアクセスする必要のある それ以降の呼び出しに渡してやることが必要です。
このアドレス ポインタを受け取るすべてのルーチン (この例では v_print_log())
は単に va_arg() を使えば、そのルーチンがあたかも可変個の引き数を渡された元のルーチンであるかのように元の可変個の引き数リストにアクセスできます。
次の例では、他に同様のルーチン (例えば log_warning() や log_message())
があり、それらもまた v_print_log() 関数を呼び出しているという状況を想像することができます。
#include <stdio.h>
#include <varargs.h>
#include <unistd.h>
int error_count;
/* VARARGS4 -- for lint */
int
log_errors(log_fp, func_name, err_num, msg_fmt, va_alist)
FILE *log_fp;
char *func_name;
int err_num;
char *msg_fmt;
va_dcl
{
va_list ap;
/* Print error header information */
(void) fprintf(log_fp, "\nERROR in process %d\n", getpid());
(void) fprintf(log_fp, " function \"%s\": ", func_name);
switch(err_num)
{
case ILLEGAL_OPTION:
(void) fprintf(log_fp, "illegal option\n");
break;
case CANNOT_PARSE:
(void) fprintf(log_fp, "cannot parse input file\n");
break;
...
}
/*
* Get pointer to first variable argument so that we can
* pass it on to v_print_log(). We do this so that
* v_print_log() can access the variable arguments passed
* to this routine.
*/
va_start(ap);
v_print_log(log_fp, msg_fmt, ap);
va_end(ap);
}
/* VARARGS2 -- for lint */
int
v_print_log(log_fp, fmt, ap)
FILE *log_fp;
char *fmt;
va_list ap;
{
/*
* If "%Y" is the first two characters in the format string,
* a second file pointer has been passed in to print general
* message information to. The rest of the format string is
* a standard printf(3S) format string.
*/
if ((*fmt == '%') && (*(fmt + 1) == 'Y'))
{
FILE *other_fp;
fmt += 2;
other_fp = (FILE *) va_arg(ap, char *);
if (other_fp != (FILE *) NULL)
{
/*
* Print general message information to additional stream.
*/
(void) vfprintf(other_fp, fmt, ap);
(void) fflush(other_fp);
}
}
/*
* Now print it to the log file.
*/
(void) vfprintf(log_fp, fmt, ap);
}
警告
スタックフレームから引き数の個数を知ることは必ずしもできないため、それを指定するのは呼び出す側のルーチンの責任です。
例えば、 execl() ではリストの最後を知らせるためにゼロポインタが渡されています。 printf() はそのフォーマットからいくつの引き数があるかを判定することができます。
va_arg の引き数として char, short,
や float を指定するのは移植性がよくありません。
これは呼び出された関数には引き数が char、 short、
や float としては見えないからです。 C は関数に渡す前に char と short の引き数を int に変換し、 float の引き数を double,
に変換してしまいます。
参照
exec(2)、 vprintf(3S)、 stdarg(5)
標準準拠
va_alist: AES, SVID2, SVID3, XPG2, XPG3,
XPG4
va_arg: SVID2, SVID3, XPG2, XPG3, XPG4
va_dcl: SVID2, SVID3, XPG2, XPG3, XPG4
va_end: SVID2, SVID3, XPG2, XPG3, XPG4
va_list: SVID2, SVID3, XPG2, XPG3, XPG4
va_start: SVID2, SVID3, XPG2, XPG3, XPG4
<varargs.h>: AES, SVID3, XPG2,
XPG3, XPG4