「C言語の基礎 - 可変長引数」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
(ページの作成:「== 概要 == stdarg.hに可変長引数を扱うための機能が用意されている。<br> <br><br> == 可変個数の実引数 == 引数の取得は以下のマク…」)
 
(文字列「</source>」を「</syntaxhighlight>」に置換)
 
(同じ利用者による、間の4版が非表示)
1行目: 1行目:
== 概要 ==
== 概要 ==
stdarg.hに可変長引数を扱うための機能が用意されている。<br>
stdarg.hに可変長引数を扱うための機能が用意されている。<br>
C言語は関数の仮引数に、"..."を指定することで可変長引数を受け取ることができる。<br>
受け取った可変長引数は、stdarg.hのva_startマクロ、va_argマクロ、va_endマクロのそれぞれを使用することで利用することができる。<br>
<br><br>
<br><br>


== 可変個数の実引数 ==
== 可変個数の実引数 ==
引数の取得は以下のマクロを用いて手動で行う必要がある。<br>
引数の取得は以下のマクロを用いて手動で行う必要がある。<br>
受け取った可変長引数は、まず、va_startマクロを使用して初期化し、va_argマクロを使用して取得する。使用後は、va_endマクロを使用することで、関数から正常に復帰する。<br>
<br>
<br>
* va_list
* va_list ap
*: イテレート用の情報を保持する型
*: イテレート用の情報を保持する型
* va_start(ap, param)
* va_start(ap, param)
21行目: 24行目:


== サンプルコード ==
== サンプルコード ==
  <source lang="c">
  <syntaxhighlight lang="c">
  #include <stdarg.h>
  #include <stdarg.h>
   
   
48行目: 51行目:
     printf("%d", sum((2), 1/* , 2 */)); // 無効なバッファを参照するため危険
     printf("%d", sum((2), 1/* , 2 */)); // 無効なバッファを参照するため危険
  }
  }
  </source>
  </syntaxhighlight>
<br><br>
<br><br>


55行目: 58行目:
NULL終端や番兵により、可変長引数の終わりを知らせるテクニックがある。<br>
NULL終端や番兵により、可変長引数の終わりを知らせるテクニックがある。<br>
NULLが出現するまで実引数の取り出し操作と比較処理が繰り返される。<br>
NULLが出現するまで実引数の取り出し操作と比較処理が繰り返される。<br>
  <source lang="c">
  <syntaxhighlight lang="c">
  bool strin(const char *target, const char *args, ...)
  bool strin(const char *target, const char *args, ...)
  {
  {
74行目: 77行目:
     return false;
     return false;
  }
  }
  </source>
  </syntaxhighlight>
  <source lang="c">
  <syntaxhighlight lang="c">
  /* 使用例 */
  /* 使用例 */
  if (strin("a", "A", "a", NULL)) puts("have");
  if (strin("a", "A", "a", NULL)) puts("have");
  </source>
  </syntaxhighlight>
<br>
<br>
===== 可変長引数をprintf関数に渡す方法 =====
===== 可変長引数をprintf関数に渡す方法 =====
84行目: 87行目:
問題なく動く場合もあるが、最悪の場合、プログラムがクラッシュする。<br>
問題なく動く場合もあるが、最悪の場合、プログラムがクラッシュする。<br>
printf関数をラップする場合は、vprintf関数を使用する。<br>
printf関数をラップする場合は、vprintf関数を使用する。<br>
  <source lang="c">
  <syntaxhighlight lang="c">
  /* 間違った使用例 */
  /* 間違った使用例 */
  void pf(const char *format, ...)
  void pf(const char *format, ...)
91行目: 94行目:
     // printf(format); // 警告:Format string is not a string literal (potentially insecure)
     // printf(format); // 警告:Format string is not a string literal (potentially insecure)
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c">
  <syntaxhighlight lang="c">
  /* 良い使用例 */
  /* 良い使用例 */
  void pf(const char *format, ...)
  void pf(const char *format, ...)
102行目: 105行目:
     va_end(ap);
     va_end(ap);
  }
  }
  </source>
  </syntaxhighlight>
<br>
<br>
  <source lang="c">
  <syntaxhighlight lang="c">
  /* 呼び出し側 */
  /* 呼び出し側 */
  pf("%s %d", "Shop", 99); // "Shop 99"
  pf("%s %d", "Shop", 99); // "Shop 99"
  </source>
  </syntaxhighlight>
<br><br>
<br><br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:C]]
[[カテゴリ:C]]

2021年11月24日 (水) 18:07時点における最新版

概要

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の値で可変長引数のサイズを明示する。


サンプルコード

 #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 */)); // 無効なバッファを参照するため危険
 }



その他のテクニック

NULL終端の活用

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

 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;
 }
 /* 使用例 */
 if (strin("a", "A", "a", NULL)) puts("have");


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

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

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


 /* 良い使用例 */
 void pf(const char *format, ...)
 {
    va_list ap;
    va_start(ap, format);
    vprintf(format, ap);
    va_end(ap);
 }


 /* 呼び出し側 */
 pf("%s %d", "Shop", 99); // "Shop 99"