tomyamaのブログ

日記・雑記。

C言語 <stdarg.h> 可変引き数 サンプルソースコード

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.
$