#演算子と##演算子

C のプリプロセッサには2つの演算子がある。# と ## だ。

#演算子

#演算子はマクロ関数の引数を、引用符付きの文字列に変換する。

#include

#define MKSTRING(str) # str

int main(void)
{
    int value;

    value = 10;

    printf("%s: %d\n", MKSTRING(value), value);

    return 0;
}
takatoh@nightschool $ ./sample_12_5a
value: 10

13行目の MKSTRING(value) が “value” に置き換えられている。

##演算子

##演算子は2つの識別子を連結する。

#include

#define output(i) printf("%d %d\n", i ## 1, i ## 2)

int main(void)
{
    int count1, count2;

    count1 = 10;
    count2 = 20;

    output(count);

    return 0;
}
takatoh@nightschool $ ./sample_12_5b
10 20

outputマクロの引数 count が、##演算子によって 1 と 2 が連結され、count1 と count2 になっている。

組み込みマクロ

ANSI C 標準に準拠したコンパイラなら、次の5つの定義済みマクロがある。

__FILE__現在コンパイル中のファイル名
__LINE__現在コンパイル中の行番号(整数値)
__DATE__現在の日付(文字列)
__TIME__コンパイルを開始した時刻(文字列)
__STDC__コンパイラが ANSI C 標準に準拠しれいれば 1

次のプログラムは、__FILE__, __LINE__, __DATE__, __TIME__ を使った例。

#include

int main(void)
{
    printf("Compiling: file: %s, line: %d at %s %s\n",
    __FILE__, __LINE__, __DATE__, __TIME__);

    return 0;
}
takatoh@nightschool $ ./sample_12_4a
Compiling: file: sample_12_4a.c, line: 7 at Jun 13 2015 15:57:24

__DATE__, __TIME__ はコンパイルしたときに値が決まるので、プログラムをいつ実行しても同じ日付、時刻を出力する。

__STDC__ についても試してみよう。

#include

int main(void)
{
    printf("%d\n", __STDC__);

    return 0;
}

結果は:

takatoh@nightschool $ ./sample_12_4b
1

#error、#undef、#line、#pragma

#error

#error エラーメッセージ

プリプロセッサは #error ディレクティブを見つけるとコンパイルを中断する。

#include

int main(void)
{
    int i;

    i = 10;

    #error This is an error message.

    printf("%d\n", i); /* This line is NOT compiled */

    return 0;
}
takatoh@nightschool $ gcc sample_12_3a.c -o sample_12_3a
sample_12_3a.c: In function ‘main’:
sample_12_3a.c:10:2: error: #error This is an error message.
 #error This is an error message.
  ^

#error ディレクティブは主にデバッグ目的で使われる。

#undef

#undef マクロ名

#undef ディレクティブはマクロ名の定義を解除する。マクロ名が定義されていなければなんの効力もない。#undef は主にマクロ名の効果を局所的に限定するためにある。

#include

#define DOG

int main(void)
{
    #ifdef DOG
    printf("DOG is defined.\n");
    #endif

    #undef DOG

    #ifdef DOG
    printf("This line is NOT compiled.\n");
    #endif

    return 0;
}
takatoh@nightschool $ ./sample_12_3b
DOG is defined.

#line

C のコンパイラは、コンパイルの最中、現在コンパイルしているファイル名と行番号を保持している。#line ディレクティブを使うと、これらの値を変更することができる。一般的な形式は次のとおり。

#line 行番号 "ファイル名"

主にデバッグや大規模プロジェクトの管理に使用する、と本には書いてあるけど、ユースケースが思い浮かばない。

#include

int main(void)
{
    int i;

    #line 1000 "myprog.c"
    #error Check the line number and file name.

    return 0;
}
takatoh@nightschool $ gcc sample_12_3c.c -o sample_12_3c
myprog.c: In function ‘main’:
myprog.c:1000:2: error: #error Check the line number and file name.

確かに行番号やファイル名が #line ディレクティブで指定した値になっている。

#pragma

#pragma 命令

本には「#pragma ディレクティブを使うと、コンパイラに渡すプリプロセッサディレクティブを、コンパイラの実装者が定義することができます。」と書いてある。これって、コンパイラを使うプログラマには使い道がないんじゃ……。