Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

C言語のマクロで可変長引数を使う方法(__VA_ARGS__の基本と実践例)

C言語では、printf()やscanf()のように関数に対して可変長引数(可変個引数)を使用できます。その際には省略記号「...」を記述します。実はマクロでも同様に可変長引数を扱うことが可能です。マクロの場合も引数リストの末尾に「...」を書き、渡された可変引数は特殊な識別子「__VA_ARGS__」で受け取ります。さらに、連結演算子「##」を使うことで、可変引数と他の要素を連結したり、引数が空の場合のカンマを除去したりできます。

以下の例では、printf()のように可変長引数を受け取るログ出力マクロを定義しています。このマクロは、ファイル名、行番号、メッセージを整形して出力します。第1引数の「pr」はメッセージの種類(優先度)を指定するためのもので、通常の情報メッセージ(INFO)なのか、エラーメッセージ(ERR)なのかを判別します。

サンプルコード

#include <stdio.h>
#define INFO 1
#define ERR 2
#define STD_OUT stdout
#define STD_ERR stderr
#define LOG_MESSAGE(pr, strm, msg, ...) do {\
    char *str;\
    if (pr == INFO)\
        str = "INFORMATION";\
    else if (pr == ERR)\
        str = "ERROR";\
        fprintf(strm, "[%s] : %s : %d : "msg" \n", \
        str, __FILE__, __LINE__, ##__VA_ARGS__);\
} while (0)
int main(void) {
    char *s = "Test String";
    LOG_MESSAGE(ERR, STD_ERR, "Unable to open the file"); //通常のメッセージを出力
    LOG_MESSAGE(INFO, STD_OUT, "%s is passed as argument", s); //文字列引数を渡す
    LOG_MESSAGE(INFO, STD_OUT, "%d + %d = %d", 14, 16, (14 + 16)); //整数を渡す
}

実行結果

[ERROR] : D:\text.c : 21 : Unable to open the file
[INFORMATION] : D:\text.c : 23 : Test String is passed as argument
[INFORMATION] : D:\text.c : 25 : 14 + 16 = 30

ポイント解説

__VA_ARGS__とは

マクロ定義内で「...」として受け取った可変長引数は、マクロ展開時に「__VA_ARGS__」へ置き換えられます。これはC99規格で導入された機能で、関数の可変長引数(stdarg.h)とは仕組みが異なります。

連結演算子「##」の役割

「##__VA_ARGS__」のように記述すると、可変引数が渡されなかった場合に、直前のカンマが自動的に削除されます。これにより、LOG_MESSAGE(ERR, STD_ERR, "...") のように引数なしでマクロを呼び出しても構文エラーを防げます。なお、この挙動はGCCやClangなどで広くサポートされている拡張機能です。

do { } while(0)で囲む理由

複数の文を含むマクロは、do { } while(0) で囲むのが定石です。こうすることで、if文などの制御構造内でマクロを使用しても意図どおりに動作し、呼び出し側でセミコロンを付けても安全に展開されます。

__FILE__と__LINE__

「__FILE__」と「__LINE__」はコンパイラが自動的に定義する定義済みマクロで、それぞれソースファイル名と現在の行番号に展開されます。ログ出力に組み込むことで、エラー発生箇所を素早く特定できるようになります。

  1. 対角線の長さから正六角形の面積を求めるC++プログラム

    この記事では、対角線の長さがわかっている場合に、正六角形の面積を計算する方法を解説します。ここでは対角線の長さを d とします。正六角形の内角はそれぞれ 120° であり、すべての内角の合計は 720° になります。対角線の長さが d のとき、面積は以下の公式で求められます。面積の公式正六角形の対角線 d を使った面積の式は次のとおりです。面積 = (3√3 × d²) / 8この式では、対角線の2乗に 3√3 を掛け、8 で割ることで面積が得られます。入力値が負の場合は無効な値として扱うため、プログラム側でチェックを行っています。サンプルコード(C++)#include <iostre

  2. Matplotlibで長さの異なるデータから箱ひげ図を作成する方法

    Matplotlibで可変長データを使った箱ひげ図を作成する方法 Matplotlibでは、要素数(データ長)が異なる複数のデータセットでも、そのまま箱ひげ図(ボックスプロット)として描画できます。リストのリスト形式でデータを渡すだけで、各データセットごとに独立した箱ひげ図が表示されます。 作成手順 図のサイズを設定し、サブプロット間および周囲の余白(パディング)を調整します。 データポイントのリストを作成します。各リストの長さは異なっていても問題ありません。 boxplot() メソッドを使用して箱ひげ図を作成します。 図を表示するには show() メソッドを使用します。 コード例