27-1 3級対策:プリプロセッサ・#define

コンパイル前に実行される「前処理」の仕組みを学ぶ。
試験では #define のマクロ展開と演算子の優先順位を絡めた問題が頻出。


27-1-1 プリプロセッサとは

C言語のソースファイルは コンパイルの前 に「プリプロセッサ」という処理が実行される。
#(シャープ)で始まる命令がその対象。

命令役割
#include外部ファイルを読み込む(stdio.h など)
#defineマクロ定義(文字・式の置換)
#ifdef / #endif条件によってコンパイルを分岐(3級範囲外)
✅ プリプロセッサはコンパイル前に動く。#define による置換は実行時ではなくソースコードの文字列置換として行われる。

27-1-2 #define:定数マクロ

#define 識別名 置換内容

識別名をソースコード中で使うと、コンパイル前に置換内容に書き換えられる。

27_01_define_const.c
#include <stdio.h>
#define PI 3.14
#define SIZE 5

int main(void)
{
    printf("PI = %f\n", PI);         /* → printf("PI = %f\n", 3.14); */
    printf("SIZE = %d\n", SIZE);     /* → printf("SIZE = %d\n", 5); */
    return 0;
}
PI = 3.140000
SIZE = 5
#define の末尾にセミコロンは付けない。
#define PI 3.14; と書くと PI3.14; に置換されてコンパイルエラーになる。

27-1-3 文字列リテラルの中は置換されない

#include <stdio.h>
#define NAME "Taro"

int main(void)
{
    printf("%s\n", NAME);    /* → "Taro" が表示される(置換される)*/
    printf("%s\n", "NAME");  /* → "NAME" と表示される(置換されない)*/
    return 0;
}
Taro
NAME
✅ ダブルクォートで囲まれた文字列リテラルの中にある識別名は置換されない。

27-1-4 引数付きマクロ(関数マクロ)

#define マクロ名(引数) 置換内容
27_02_define_macro.c
#include <stdio.h>
#define SQUARE(x) ((x) * (x))

int main(void)
{
    printf("%d\n", SQUARE(3));       /* → ((3) * (3)) = 9 */
    printf("%d\n", SQUARE(1 + 2));   /* → ((1+2) * (1+2)) = 9 */
    return 0;
}
9
9

カッコなしは危険

#define BAD_SQUARE(x)  x * x
#define GOOD_SQUARE(x) ((x) * (x))

int b = BAD_SQUARE(1 + 2);    /* → 1 + 2 * 1 + 2 = 5(誤動作)*/
int c = GOOD_SQUARE(1 + 2);   /* → ((1+2) * (1+2)) = 9(正動作)*/
⚠ 引数と全体を必ず () で囲む。演算子の優先順位による意図しない展開を防ぐため。

27-1-5 演算マクロと優先順位の罠

#define ADD x + y   /* カッコなし */

int x = 2, y = 3;
int result = ADD * 2;   /* → x + y * 2 = 2 + 6 = 8(意図は 5*2=10 だったかも)*/
#define ADD (x + y)  /* カッコあり */

int result = ADD * 2;   /* → (x + y) * 2 = 5 * 2 = 10 */
✅ マクロ内の式全体もカッコで囲む習慣をつける。

27-1-6 理解度チェック

Q1. 次のコードで変数 a の値はどれか。

#define PI 3.14
float a = PI * 2;
解説を表示

正解:ア(6.28)

PI が 3.14 に置換され 3.14 * 2 = 6.28

Q2. 次のコードで result の値はどれか。

#define ADD x + y
int x = 2, y = 3;
int result = ADD * 2;
解説を表示

正解:イ(8)

ADD * 2x + y * 22 + 3 * 2 = 2 + 6 = 8*+ より優先される。

Q3. 次のコードで msg に格納される文字列はどれか。

#define NAME "Taro"
char msg[] = "NAME";
解説を表示

正解:ウ(NAME)

"NAME" は文字列リテラルなのでマクロ展開されない。

Q4. 次のコードで a, b の値の組み合わせとして正しいものはどれか。

#define MUL1  x + y * z
#define MUL2 (x + y) * z
int x = 1, y = 2, z = 3;
int a = MUL1;
int b = MUL2;
解説を表示

正解:ア(a=7, b=9)

MUL1 → 1 + 2 * 3 = 1 + 6 = 7
MUL2 → (1 + 2) * 3 = 3 * 3 = 9

Q5. 次のコードで TEXT を使って正しく展開されるのはどれか。

#define TEXT "Hello"
char a[] = TEXT;
char b[] = "TEXT";
解説を表示

正解:ア

TEXT"Hello" に置換される。"TEXT" は文字列リテラルなので置換されず "TEXT" のまま。