C言語の基礎 - 可変長引数

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
2021年11月22日 (月) 11:50時点におけるWiki (トーク | 投稿記録)による版 (文字列「<source lang」を「<syntaxhighlight lang」に置換)
ナビゲーションに移動 検索に移動

概要

stdarg.hに可変長引数を扱うための機能が用意されている。
C言語は関数の仮引数に、"..."を指定することで可変長引数を受け取ることができる。
受け取った可変長引数は、stdarg.hのva_startマクロ、va_argマクロ、va_endマクロのそれぞれを使用することで利用することができる。


可変個数の実引数

引数の取得は以下のマクロを用いて手動で行う必要がある。
受け取った可変長引数は、まず、va_startマクロを使用して初期化し、va_argマクロを使用して取得する。使用後は、va_endマクロを使用することで、関数から正常に復帰する。

  • va_list ap
    イテレート用の情報を保持する型
  • va_start(ap, param)
    イテレートを開始
  • va_arg(ap, type)
    次の引数を取り出す
  • va_end(ap)
    イテレートを終了


可変長引数の個数を取得することはできない。
そのため、関数側には何らかの形で引数の個数を知らせる必要がある。
以下のサンプルコードでは、引数countの値で可変長引数のサイズを明示する。


サンプルコード

<syntaxhighlight lang="c">
#include <stdarg.h>

int sum(int count, ...)
{
   va_list ap;
   va_start(ap, count);

   int sum = 0;
   for (int i = 0; i < count; i++ )
   {
      sum += va_arg(ap, int);
   }

   va_end(ap);

   return sum;
}

int main()
{
   int s = sum(3, 1, 2, 3);
   printf("%d", s);                    // 6
   printf("%d", sum((3), 1, 2, 3, 4)); // 6
   printf("%d", sum((4), 1, 2, 3, 4)); // 10
   printf("%d", sum((2), 1/* , 2 */)); // 無効なバッファを参照するため危険
}
</source>



その他のテクニック

NULL終端の活用

NULL終端や番兵により、可変長引数の終わりを知らせるテクニックがある。
NULLが出現するまで実引数の取り出し操作と比較処理が繰り返される。

<syntaxhighlight lang="c">
bool strin(const char *target, const char *args, ...)
{
   va_list ap;
   va_start(ap, args);

   for (const char *arg = args; arg != NULL; arg = va_arg(ap, const char *))
   {
      if (!strcmp(target, arg))
      {
         va_end(ap);
         return true;
      }
   }

   va_end(ap);

   return false;
}
</source>
<syntaxhighlight lang="c">
/* 使用例 */
if (strin("a", "A", "a", NULL)) puts("have");
</source>


可変長引数をprintf関数に渡す方法

先頭引数をprintf関数に渡すだけでは不十分である。
問題なく動く場合もあるが、最悪の場合、プログラムがクラッシュする。
printf関数をラップする場合は、vprintf関数を使用する。

<syntaxhighlight lang="c">
/* 間違った使用例 */
void pf(const char *format, ...)
{
   // 危険
   // printf(format); // 警告:Format string is not a string literal (potentially insecure)
}
</source>


<syntaxhighlight lang="c">
/* 良い使用例 */
void pf(const char *format, ...)
{
   va_list ap;
   va_start(ap, format);
   vprintf(format, ap);
   va_end(ap);
}
</source>


<syntaxhighlight lang="c">
/* 呼び出し側 */
pf("%s %d", "Shop", 99); // "Shop 99"
</source>