C言語の可変長引数関数:va_copyマクロで自由度の高いプログラミングを実現する


可変長引数関数を定義するには、関数宣言に ... を使用します。これは、関数が不定個数の引数を取ることができることを示します。

void my_function(int arg1, ...);

可変長引数関数の引数にアクセスするには、va_start(), va_arg(), va_end() マクロを使用します。

  • va_start() マクロ: 可変長引数リストの処理を開始します。引数には、処理対象となる va_list 型の変数と、最初の引数の型を渡します。
va_list ap;
va_start(ap, arg1);
  • va_arg() マクロ: 可変長引数リストから次の引数を取得します。引数には、va_list 型の変数と、取得する引数の型を渡します。
int arg2 = va_arg(ap, int);
  • va_end() マクロ: 可変長引数リストの処理を終了します。引数には、処理対象となる va_list 型の変数を渡します。
va_end(ap);

va_copy() マクロは、可変長引数リストのコピーを作成するために使用されます。引数には、コピー元の va_list 型の変数と、コピー先の va_list 型の変数を渡します。

va_list ap2;
va_copy(ap2, ap);

va_copy() マクロを使用すると、元の可変長引数リストを変更せずに、そのコピーに対して処理を行うことができます。これは、複数の可変長引数関数に同じ引数リストを渡す場合などに役立ちます。

以下の例は、va_copy() マクロを使用して、2 つの可変長引数関数に同じ引数リストを渡す方法を示しています。

#include <stdarg.h>

void print_all(int arg1, ...) {
  va_list ap;
  va_copy(ap, arg1);

  while ((arg2 = va_arg(ap, int)) != -1) {
    printf("%d ", arg2);
  }

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

void sum_all(int arg1, ...) {
  va_list ap;
  va_copy(ap, arg1);

  int sum = 0;
  while ((arg2 = va_arg(ap, int)) != -1) {
    sum += arg2;
  }

  va_end(ap);
  printf("Sum: %d\n", sum);
}

int main() {
  int a = 1;
  int b = 2;
  int c = 3;
  int d = -1;

  print_all(a, b, c, d);
  sum_all(a, b, c, d);

  return 0;
}

このプログラムは、以下の出力を生成します。

1 2 3 
Sum: 6

この例では、print_all() 関数は引数リスト全体をコピーし、そのコピーに対して処理を行います。一方、sum_all() 関数は va_copy() マクロを使用して引数リストのコピーを作成し、そのコピーに対して合計を計算します。元の引数リストは変更されません。

  • va_copy() マクロは、複数の可変長引数関数に同じ引数リストを渡す場合などに役立ちます。
  • va_copy() マクロを使用すると、元の可変長引数リストを変更せずに、そのコピーに対して処理を行うことができます。
  • va_copy() マクロは、可変長引数リストのコピーを作成するために使用されます。


例 1: 複数の可変長引数関数に同じ引数リストを渡す

この例では、print_all() 関数と sum_all() 関数に同じ引数リストを渡します。va_copy() マクロを使用して、元の引数リストを変更せずに、各関数で引数リストのコピーを使用します。

#include <stdarg.h>

void print_all(int arg1, ...) {
  va_list ap;
  va_copy(ap, arg1);

  while ((arg2 = va_arg(ap, int)) != -1) {
    printf("%d ", arg2);
  }

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

void sum_all(int arg1, ...) {
  va_list ap;
  va_copy(ap, arg1);

  int sum = 0;
  while ((arg2 = va_arg(ap, int)) != -1) {
    sum += arg2;
  }

  va_end(ap);
  printf("Sum: %d\n", sum);
}

int main() {
  int a = 1;
  int b = 2;
  int c = 3;
  int d = -1;

  print_all(a, b, c, d);
  sum_all(a, b, c, d);

  return 0;
}

例 2: 可変長引数リストの要素を逆順に処理する

この例では、va_copy() マクロを使用して可変長引数リストのコピーを作成し、そのコピーの要素を逆順に処理します。

#include <stdarg.h>

void reverse_print(int arg1, ...) {
  va_list ap, ap_copy;
  va_start(ap, arg1);
  va_copy(ap_copy, ap);

  while (va_arg(ap_copy, int) != -1) {}

  va_end(ap_copy);

  while ((arg2 = va_arg(ap, int)) != -1) {
    printf("%d ", arg2);
  }

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

int main() {
  int a = 1;
  int b = 2;
  int c = 3;
  int d = -1;

  reverse_print(a, b, c, d);

  return 0;
}

例 3: 可変長引数リストの要素の数を数える

この例では、va_copy() マクロを使用して可変長引数リストのコピーを作成し、そのコピーの要素数を数えます。

#include <stdarg.h>

int count_args(int arg1, ...) {
  va_list ap, ap_copy;
  va_start(ap, arg1);
  va_copy(ap_copy, ap);

  int count = 0;
  while (va_arg(ap_copy, int) != -1) {
    count++;
  }

  va_end(ap_copy);
  va_end(ap);

  return count;
}

int main() {
  int a = 1;
  int b = 2;
  int c = 3;
  int d = -1;

  int num_args = count_args(a, b, c, d);
  printf("Number of arguments: %d\n", num_args);

  return 0;
}


標準ライブラリを使用する

C++11 以降では、可変長引数テンプレートと std::forward_list クラスを使用して、va_copy マクロと同等の機能を実現できます。この方法は、より安全で型安全なコードを作成するのに役立ちます。

以下の例は、std::forward_list を使用して可変長引数リストをコピーする方法を示しています。

#include <forward_list>
#include <iostream>

template <typename T>
void print_all(std::forward_list<T> args) {
  for (const auto& arg : args) {
    std::cout << arg << " ";
  }
  std::cout << std::endl;
}

int main() {
  std::forward_list<int> args = {1, 2, 3, 4, 5};
  print_all(args);

  return 0;
}

再帰を使用する

再帰を使用して、可変長引数リストを要素ごとに処理することもできます。この方法は、より複雑で読みづらいコードになる可能性がありますが、va_copy マクロを使用しない方法として使用できます。

以下の例は、再帰を使用して可変長引数リストの要素をすべて印刷する方法を示しています。

void print_all(int arg1, ...) {
  printf("%d ", arg1);
  if (va_arg(ap, int) != -1) {
    print_all(va_arg(ap, int), ...);
  } else {
    printf("\n");
  }
}

int main() {
  print_all(1, 2, 3, 4, 5);

  return 0;
}

va_copy マクロは、可変長引数関数の引数リストをコピーするための一般的な方法ですが、状況によっては代替方法が有効となる場合があります。 標準ライブラリを使用したり、再帰を使用したりすることで、より安全で型安全なコードを作成したり、va_copy マクロを使用しない方法で可変長引数リストを処理したりすることができます。