#define はプリプロセッサディレクティブで、コンパイル前に識別子のすべての出現を値に置き換えます。変数を作成せず、型を認識せず、境界をチェックせず、スコープを無視します。
const は修飾子で、実際の変数を作成しますが、初期化後は書き込みができません。const変数は型、スコープを持ち、デバッグに参加し、安全性が向上します。このような変数は.rodataセグメントまたはスタック/メモリに配置されます。
#define はコンパイル時に必要な単純なスカラー値のために、特にプリプロセッサ条件や配列のサイズに使用します(例: #define SIZE 16)。const は型安全に値を渡す必要がある場合、デバッグし、安全にエクスポート(ヘッダーファイルやモジュール間)する必要がある場合に使用します。constの使用が好ましいです。例:
#define PI 3.14159 const double G = 9.81; int arr[PI]; // エラー!PIは整数ではない int arr2[G]; // エラー!Gはコンパイル時の値ではない
質問: const int a = 10;は配列を作成するためのコンパイル時定数として機能しますか: int arr[a];?
回答: いいえ。C言語ではconstは修飾子ですが、変数は実行時に作成および初期化されるため、配列のサイズはリテラルまたはコンパイラに知られている式でなければなりません。#define、またはenum { SIZE = 10 };を使用してください。
エラーの例:
const int a = 5; int arr[a]; // C89/90では動作しない(VLAはC99で登場したが、すべての環境でサポートされているわけではない)
物語
メディアサーバーでは、バッファサイズを設定するために
const intを使用し、「コンパイル時定数」であると思い込んでいました。あるコンパイラ(GCC、C99)ではすべてが機能しましたが、大多数のコンパイラではコンパイルエラーが発生し(VLAがサポートされていない)、急いで# defineに書き換えました。
物語
プラットフォーム依存のコードでは、
#define NAME "MyApp"を使用して文字列を定義し、何か所かで使用して括弧を付け忘れました。明示的な括弧なしで文字列に文字を追加すると、間違った結果が生じ、ログに奇妙なバグが発生しました。
物語
複数のモジュールを持つプロジェクトでは、異なる値で# defineを2か所に定義しました(コピペ)。結果として、モジュールは異なる定数で動作し、データの非正規化が発生し、丁寧なデバッグでのみ修正されました。