C言語-5

C言語の学習、素晴らしいペースですね!基礎的な概念は一通り網羅できましたので、今回は「プリプロセッサ」と「共用体・列挙型」という、C言語特有の強力な機能について解説します。


8. プリプロセッサ(Preprocessor)

プリプロセッサは、コンパイラが実際のコンパイル作業を行うに、ソースコードを前処理する機能です。ファイル先頭の # で始まる行がプリプロセッサディレクティブです。

🏷️ マクロ定義 (#define)

マクロは、特定のテキストを別のテキストに置き換える(展開する)機能です。定数を定義したり、簡単な関数のような処理を定義するのに使われます。

定数の定義

C

#include <stdio.h>

// PIという文字がコード内に現れたら、3.14159に置き換えられる
#define PI 3.14159 
// 最大値を定義
#define MAX_VALUE 100 

int main() {
    double circumference = 2 * PI * 5.0; // PIが3.14159に置き換えられる
    printf("円周: %f\n", circumference);
    return 0;
}

関数マクロの定義(引数を持つマクロ)

引数を持つマクロは、簡単な計算処理などを高速に(関数呼び出しのオーバーヘッドなしに)行うために使われます。

C

// aとbのうち大きい方を返すマクロ
// 複雑な式を引数に取る場合は、バグを防ぐため引数を () で囲むのが鉄則
#define MAX(a, b) ((a) > (b) ? (a) : (b))

int main() {
    int x = MAX(10, 20); // プリプロセッサにより (10 > 20 ? 10 : 20) に展開
    printf("最大値: %d\n", x); // 出力: 20
    return 0;
}

💡 条件付きコンパイル (#ifdef, #ifndef)

特定の条件に応じて、コンパイルするコードを切り替えるために使われます。デバッグコードとリリースコードを分ける際などに非常に便利です。

C

#include <stdio.h>

// #define DEBUG // この行があればDEBUGが定義されている

void process_data() {
#ifdef DEBUG // DEBUGが定義されていたら以下のコードをコンパイルに含める
    printf("--- デバッグログ: process_data 開始 ---\n");
#endif

    printf("メインの処理を実行中...\n");

#ifdef DEBUG
    printf("--- デバッグログ: process_data 終了 ---\n");
#endif
}

int main() {
    process_data();
    return 0;
}
// DEBUGをコメントアウト(#defineを無効)にすると、デバッグログの行はコンパイル時に無視されます。

9. 共用体(Union)

共用体は、複数のメンバーが同じメモリ領域を共有する構造です。同時に使用できるメンバーは一つだけで、メモリの節約を目的として使用されます。

🧱 定義と利用

共用体のサイズは、最も大きいサイズのメンバーに合わせて決定されます。

C

#include <stdio.h>

// Numberという共用体を定義
union Number {
    int i;    // 4バイト
    float f;  // 4バイト
}; // 共用体全体のサイズは 4バイト

int main() {
    union Number n;

    // 1. i に値を代入 (メモリ4バイトを使用)
    n.i = 10;
    printf("整数 (i) : %d\n", n.i); 

    // 2. f に値を代入 (メモリ4バイトを使用)
    // この時点で、i に代入されていたデータはfのデータで上書きされる
    n.f = 9.9f;
    printf("浮動小数点数 (f) : %.1f\n", n.f); 

    // i の値は壊れている(上書きされている)ため、意味のない値が表示される
    printf("上書き後の整数 (i) : %d\n", n.i); 
    
    return 0;
}

🚨 注意: 共用体のメンバーは同時に使えません。使う際には、現在どのメンバーにデータが格納されているかをプログラマが把握している必要があります。


10. 列挙型(Enum)

列挙型は、プログラマが定義した意味のある名前整数の定数に対応づけるための仕組みです。コードの可読性を大幅に向上させます。

🚦 定義と利用

デフォルトでは、最初の要素が 0、それ以降が 12…と順に整数値が割り当てられます。

C

#include <stdio.h>

// Colorという列挙型を定義
enum Color {
    RED,    // 0
    GREEN,  // 1
    BLUE    // 2
};

// 途中の値は明示的に指定可能 (以降の要素は1ずつ増加)
enum StatusCode {
    SUCCESS = 0,
    ERROR_FILE_NOT_FOUND = 404, // 404
    ERROR_ACCESS_DENIED         // 405
};

int main() {
    enum Color current_color = GREEN;

    if (current_color == GREEN) {
        printf("現在の色はGREENです (値: %d)\n", current_color);
    }
    
    enum StatusCode status = ERROR_FILE_NOT_FOUND;
    printf("ステータスコード: %d\n", status); // 出力: 404

    return 0;
}

これらの機能は、C言語による低レベルなプログラミングや、システム設計において、コードの柔軟性、効率性、可読性を高めるために不可欠です。