定数式

編集

プログラミングにおいて、定数式は非常に重要な役割を担っています。定数式とは、その名の通り、コンパイル時に値が確定する式のことを指します。C言語では、定数式は様々な場面で用いられます。例えば、配列のサイズを指定する際や、enum型の値を初期化する際などがその具体例です。

定数式は以下の構文規則に従って記述されます。

constant-expression: 
    conditional-expression

つまり、条件式が定数式として使用可能であることを意味しています。しかしながら、条件式の形を取っている式が必ずしも定数式とは限りません。なぜなら、定数式には幾つかの制約が課されているからです。

第一に、定数式中には代入演算子(=)、インクリメント演算子(++)、デクリメント演算子(--)、関数呼び出し、コンマ演算子(,)を含めてはいけません。ただし、評価されない副式内に含まれる場合は例外とされます。

第二に、定数式が評価された結果は、その式の型の値の範囲内に収まる必要があります。例えば、unsigned int型の定数式なら、0から4294967295の範囲内の値を取らなければいけません。

定数式は主に次の3種類に大別することができます。

整数定数式

編集

整数定数式とは、整数型の定数式のことを指します。具体的には、整数定数、整数型の列挙子定数、整数型のconstexprオブジェクト、サイズオフ演算子の整数結果、アライメント演算子などを含む式が該当します。整数定数式のキャスト演算子は、算術型から整数型への変換のみ許可されます。

例:

enum Color {
    RED = 1,
    GREEN = RED << 1,  // 2
    BLUE = GREEN << 1  // 4
};

const int ARRAY_SIZE = 10;
int array[ARRAY_SIZE];  // 配列サイズとして使用

#define BUFFER_SIZE (1024 * 2)  // マクロでの使用
static_assert(BUFFER_SIZE <= 4096, "バッファサイズが大きすぎます");

算術定数式

編集

算術定数式とは、整数型や浮動小数点型の定数式を指します。具体的には、整数定数、浮動小数点数定数、算術型のconstexprオブジェクト、文字定数、サイズオフ演算子の整数結果、アライメント演算子などを含む式が該当します。算術定数式のキャスト演算子は、算術型から算術型への変換のみ許可されます。

例:

const double PI = 3.14159265359;
const float GRAVITY = 9.81f;
const char NEWLINE = '\n';

// コンパイル時計算
const double CIRCLE_AREA = PI * (2.5 * 2.5);
const float FALL_DISTANCE = 0.5f * GRAVITY * (3.0f * 3.0f);

アドレス定数

編集

アドレス定数とは、NULLポインタ、静的記憶期間オブジェクトを指す左辺値式、関数指標を指すポインタなどを指します。これらはアドレス演算子(&)、整数からポインタへのキャスト、配列型や関数型の式から暗に生成されます。アドレス定数の生成には[]演算子、->演算子、&演算子、*演算子、ポインタキャストが使用できますが、オブジェクトの値自体を参照してはいけません。

例:

// 静的配列とそのポインタ
static const int data[] = {1, 2, 3, 4, 5};
static const int *const ptr = &data[0];

// 関数ポインタ
typedef void (*FuncPtr)(void);
static void dummy(void) {}
static const FuncPtr func_ptr = &dummy;

// NULLポインタ定数
const char *const null_ptr = NULL;
#define MY_NULL ((void*)0)

コンパイル時の制約チェック例

編集

以下は定数式を使用してコンパイル時に制約をチェックする例です:

#include <stddef.h>
#include <limits.h>

// サイズの制約
#define MAX_BUFFER_SIZE 1024
static const size_t buffer_size = 512;
static_assert(buffer_size <= MAX_BUFFER_SIZE, "バッファサイズが上限を超えています");

// アライメントの制約
struct aligned_struct {
    char data[buffer_size];
} __attribute__((aligned(8)));
static_assert(alignof(struct aligned_struct) >= 8, "アライメントが不適切です");

// 型のサイズの制約
static_assert(sizeof(int) >= 4, "int型は少なくとも32ビット必要です");
static_assert(CHAR_BIT == 8, "char型は8ビットである必要があります");