ユーニックス総合研究所

  • home
  • archives
  • c-stdarg

C言語の関数の可変長引数の使い方

  • 作成日: 2024-03-28
  • 更新日: 2024-03-28
  • カテゴリ: C言語

C言語で関数の可変長引数を扱う方法を解説します。

C言語や他の言語を扱うYoutubeも公開しています。
興味がある方は以下のリンクからご覧ください。

Youtubeの当チャンネル

関連動画
C言語で関数の可変長引数を扱う【stdarg.h, va_list, va_start, va_end, va_arg】 - YouTube

stdarg.h

C言語で可変長引数を使う場合はstdarg.hをインクルードします。

#include <stdarg.h>  

print関数を実装する

printf関数のクローンであるprint関数を実装します。

void print(const char *fmt, ...) {  
    va_list ap;  // apを作成  
    va_start(ap, fmt);  // apを初期化  
    vfprintf(stdout, fmt, ap);  // apを出力  
    va_end(ap); // apを破棄  
}  

上記の関数は以下のように使うことができます。

print("Hello %d %s\n", 123, "Bye");  // Hello 123 Bye  

まず可変長引数ですが、これは関数では...というキーワードがこれに当たります。

void print(const char *fmt, ...);  

上記の引数fmtの後の...が可変長引数です。
この引数には複数の引数を渡すことができます。

関数内ではまず最初にva_listapを作成します。

    va_list ap;  // apを作成  

このapは可変長引数が入っているオブジェクトです。
可変長引数を処理したい時はこのapを関数やマクロに渡します。

次にapを初期化します。

    va_start(ap, fmt);  // apを初期化  

上記ではva_startマクロ関数でapを初期化しています。
va_startマクロ関数の第2引数には、...の1つ前の引数を渡します。
print関数の...の1つ前の引数はfmtですから、これを渡すことになります。

    vfprintf(stdout, fmt, ap);  // apを出力  

上記ではvfprintf関数でapを出力しています。
この関数は書式に従ってapを出力してくれます。
第1引数には出力先のストリームを指定します。

    va_end(ap); // apを破棄  

上記では使い終わったapを破棄しています。

sum関数を実装する

int sum(int n, ...) {  
    va_list ap;  
    va_start(ap, n);  
    int ret = 0;  

    for (int i = 0; i < n; i++) {  
        int k = va_arg(ap, int);  
        ret += k;  
    }  

    va_end(ap);  
    return ret;  
}  

上記のsum関数は可変長引数のint型の値を合計してその合計値を返すというものです。
第1引数には可変長引数の数を渡します。

printf("%d\n", sum(3, 10, 20, 30));  // 60  

上記はsum関数を使っているところです。
上記の場合、sum関数は60を返します。

    for (int i = 0; i < n; i++) {  
        int k = va_arg(ap, int);  
        ret += k;  
    }  

上記はapからva_argマクロ関数で引数を取り出しているところです。
va_argには第1引数にapを渡し、第2引数に引数の型を渡します。
引数がdoubleの場合はva_argの第2引数にはdoubleを渡します。

retには引数の合計値を加算しています。
関数の最後でretを返しています。

puts_args関数を実装する

void puts_args(const char *fmt, ...) {  
    va_list ap;  
    va_start(ap, fmt);  

    for (const char *p = fmt; *p; p++) {  
        switch (*p) {  
        case 'i': {  
            int n = va_arg(ap, int);  
            printf("%d ", n);  
        } break;  
        case 'f': {  
            double n = va_arg(ap, double);  
            printf("%f ", n);  
        } break;  
        case 's': {  
            const char *s = va_arg(ap, const char*);  
            printf("%s ", s);  
        } break;  
        }  
    }  

    printf("\n");  
    va_end(ap);  
}  

上記の関数は可変長引数をfmtに従って出力する関数です。
第1引数にはfmtの文字列を指定し、第2引数以降に可変長引数を渡します。

puts_args("ifs", 123, 3.14, "Hello");  
// 123 3.14 Hello  

上記はputs_args関数を使っているところです。

    for (const char *p = fmt; *p; p++) {  
        ...  
    }  

上記ではfmtをポインタ変数pに入れてループを回してpをインクリメントしています。

        switch (*p) {  
        case 'i': {  
            int n = va_arg(ap, int);  
            printf("%d ", n);  
        } break;  
        case 'f': {  
            double n = va_arg(ap, double);  
            printf("%f ", n);  
        } break;  
        case 's': {  
            const char *s = va_arg(ap, const char*);  
            printf("%s ", s);  
        } break;  
        }  

上記では*pの値に応じて、va_argapから引き出す引数の型を変えています。
*piだったらintで引き出し、fだったらdoubleで引き出し、sだったらconst char *で引き出します。

おわりに

今回はC言語の可変長引数を解説しました。
何か参考になれば幸いです。