C言語の「stdarg.h」を使ったサンプルプログラムです。
- va_start() 引数リストを初期化する
- C89
- #include <stdarg.h>
- void va_start( va_list ap, last );
- va_arg() 次の位置にある引数を取得する
- C89
- #include <stdarg.h>
- type va_arg( va_list ap, type );
- va_arg() を呼び出すごとに ap は変更されることに注意
- va_end() va_listの後始末をする
- C89
- #include <stdarg.h>
- void va_end( va_list ap );
- va_copy() va_listのコピーを作る
- C99
- #include <stdarg.h>
- void va_copy( va_list dest, va_list src );
- vfprintf() va_list型を引数にとれるprintf()関数
- C99
- #include <stdarg.h>
#include <stdio.h> - int vfprintf( FILE *stream, const char *format, va_list ap );
目次
サンプルコード
[stdarg.c]
#include <stdio.h> /* fprintf(), vfprintf() */
#include <stdarg.h> /* va_start(), va_arg(), va_end(), vfprintf() */
#include <string.h> /* strnlen(), strncpy() */
static int adder( int qty, ... );
static char *concatenate( char *buffAddr, int buffSize, int qty, ... );
static int printf_ex( char *prefix, char *fmt, ... );
/**************************************************
* プログラムのエントリポイント
**************************************************/int main( void )
{
static char buff[ 21 ];printf_ex( "extend: ", "\"%s\", %d, '%c'\n", "hello", 200, 'Z' );
printf( "adder(): int : %d\n", adder( 5, 1, 2, 3, 4, 5 ) );
concatenate( buff, ( ( sizeof buff ) / ( sizeof buff[ 0 ] ) ), 4, "abcde", "FGHIJ", "klmno", "PQRST" );
printf( "concatenate(): \"%s\"\n", buff );return 0;
}
/**************************************************
* va_start(), va_arg(), va_end() の動作を確認する
**************************************************/static int adder( int qty, ... )
{
va_list vaList;
int iRet, iArg;
int idx;/*--- qty を目印にして vaList をセットアップする ---*/
va_start( vaList, qty );iRet = 0;
for( idx=0; idx<qty; idx++ ){
/*--- 引数を取り出す ---*/
iArg = va_arg( vaList, int );
iRet += iArg;
}/*--- vaList の後始末 ---*/
va_end( vaList );return iRet;
}
static char *concatenate( char *buffAddr, int buffSize, int qty, ... )
{
va_list vaList;
char *pStrArg, *pStr;
size_t iRemainBuff, iCopySize;
int idx;for( idx=0; idx<buffSize; idx++ ){
*( buffAddr + idx ) = '\0';
}/*--- qty を目印にして vaList をセットアップする ---*/
va_start( vaList, qty );pStr = buffAddr;
iRemainBuff = buffSize - 1;
for( idx=0; idx<qty; idx++ ){
if( iRemainBuff == 0 ){
fprintf( stderr, "%s(): warn: buffer full: idx=%d\n", __func__, idx );
break;
}/*--- 引数を取り出す ---*/
pStrArg = va_arg( vaList, char * );
iCopySize = strnlen( pStrArg, 100 );
if( iRemainBuff < iCopySize ){
iCopySize = iRemainBuff;
fprintf( stderr, "%s(): warn: missing string\n", __func__ );
}
strncpy( pStr, pStrArg, iCopySize );iRemainBuff -= iCopySize;
pStr += iCopySize;
}/*--- vaList の後始末 ---*/
va_end( vaList );return buffAddr;
}
/**************************************************
* printf()の拡張バージョンを実装してみる
*
* \param[in] fmt 対応している出力変換は、「%d」,「%u」,「%s」,「%c」のみ
**************************************************/static int printf_ex( char *prefix, char *fmt, ... )
{
va_list vaList1, vaList2, vaList3;
int iRet;
char *pFmt;
int *pInt;
unsigned *pUInt;/*--- fmt を目印にして vaList1 をセットアップする ---*/
va_start( vaList1, fmt );/*--- vaList1 を更新する前にコピーしておく ---*/
va_copy( vaList2, vaList1 );
va_copy( vaList3, vaList1 );/* vaList1 を使う */
fputs( prefix, stdout );
fputs( "before: ", stdout );
/*--- vfprintf()関数は va_list型 を引数にとれる ---*/
iRet = vfprintf( stdout, fmt, vaList1 );/* vaList2 を使う */
/* 指定された引数のうち数値は10%増しにしておく */
for( pFmt = fmt; *pFmt != '\0'; pFmt++ ){
//printf( "fmt: %c\n", *pFmt );
if( *pFmt == '%' ){
pFmt++;
//printf( "fmt: %c\n", *pFmt );
switch( *pFmt ){
case 'd':
/* va_list型の構造は未定義なので処理系により実装が異なる。 */
/* ライブラリの実装によっては動くが保証はできない。 */
/* clangだとそもそもエラーとなりコンパイルできない。 */
/* 実際の実装では絶対に使わないこと。 */
pInt = ( int *)vaList2;
/* アドレスを取得したので vaList2 を更新しておく */
va_arg( vaList2, int );
*pInt = ( int )( *pInt * 1.1 );
break;
case 'u':
/* va_list型の構造は未定義なので処理系により実装が異なる。 */
/* ライブラリの実装によっては動くが保証はできない。 */
/* clangだとそもそもエラーとなりコンパイルできない。 */
/* 実際の実装では絶対に使わないこと。 */
pUInt = ( unsigned *)vaList2;
/* アドレスを取得したので vaList2 を更新しておく */
va_arg( vaList2, unsigned );
*pUInt = ( unsigned )( *pUInt * 1.1 );
break;
case 'c':
//#if __GNUC__ == 11 && __GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ == 0
//#if __GNUC__ == 11 && __GNUC_MINOR__ == 3
#ifdef __GNUC__
va_arg( vaList2, int ); /* pass "int" not "char" to va_arg() */
#else
va_arg( vaList2, char );
#endif
break;
case 's':
va_arg( vaList2, char* );
break;
}
}
}/* vaList3 を使う */
fputs( prefix, stdout );
fputs( "after : ", stdout );
/*--- vfprintf()関数は va_list型 を引数にとれる ---*/
iRet = vfprintf( stdout, fmt, vaList3 );/*--- vaList1, vaList2, vaList3 の後始末 ---*/
va_end( vaList1 );
va_end( vaList2 );
va_end( vaList3 );return iRet;
}
実行例
Windows 10, Visual Studio 2022, x86 Native Tools Command Prompt
>cl /nologo /utf-8 /Fe:stdarg_windows stdarg.c
stdarg.c
>
>stdarg_windows.exe
extend: before: "hello", 200, 'Z'
extend: after : "hello", 220, 'Z'
adder(): int : 15
concatenate(): "abcdeFGHIJklmnoPQRST"
>
CentOS Stream 9, gcc (GCC) 11.3.1
$ gcc -Wall -O2 -o stdarg_centos9 stdarg.c
$
$ ./stdarg_centos9
extend: before: "hello", 200, 'Z'
extend: after : "hello", 200, 'Z'
adder(): int : 15
concatenate(): "abcdeFGHIJklmnoPQRST"
$
CentOS Linux 8, gcc (GCC) 8.3.1
$ gcc -Wall -O2 -o stdarg_centos8 stdarg.c
$
$ ./stdarg_centos8
extend: before: "hello", 200, 'Z'
extend: after : "hello", 200, 'Z'
adder(): int : 15
concatenate(): "abcdeFGHIJklmnoPQRST"
$
Cygwin 3.3.6-1, gcc (GCC) 11.3.0
$ gcc -Wall -O2 -o stdarg_cygwin stdarg.c
$
$ ./stdarg_cygwin.exe
extend: before: "hello", 200, 'Z'
extend: after : "hello", 220, 'Z'
adder(): int : 15
concatenate(): "abcdeFGHIJklmnoPQRST"
$
Knoppix 8.6.1, gcc (Debian 8.3.0-6) 8.3.0
$ gcc -Wall -O2 -o stdarg_knoppix stdarg.c
$
$ ./stdarg_knoppix.exe
extend: before: "hello", 200, 'Z'
extend: after : "hello", 220, 'Z'
adder(): int : 15
concatenate(): "abcdeFGHIJklmnoPQRST"
$
Android, Termux 0.118.0, clang 14.0.6
$ gcc -Wall -O2 -o stdarg_termux stdarg.c
stdarg.c:137:32: error: operand of type 'va_list' (aka '__builtin_va_list') where arithmetic or pointer type is required
pInt = ( int *)vaList2;
^~~~~~~
stdarg.c:145:38: error: operand of type 'va_list' (aka '__builtin_va_list') where arithmetic or pointer type is required
pUInt = ( unsigned *)vaList2;
^~~~~~~
2 errors generated.
$