多くのプログラムで#define
は、定数と同じ目的を果たします。例えば。
#define FIELD_WIDTH 10
const int fieldWidth = 10;
基本的にアプリケーションの決定を処理するためにプリプロセッサに依存して、私は一般的に他の形式よりも優先される最初の形式を見ます。この伝統には理由がありますか?
これには非常に堅実な理由があります。Cのconst
は、何かが一定であることを意味しません。変数が読み取り専用であることを意味します。
コンパイラが真の定数を必要とする場所(非VLA配列の配列サイズなど)では、const
などのfieldWidth
変数を使用することはできません。
彼らは違います。
const
は修飾子であり、実行時に変数を変更できないことを示します。ただし、変数の他のすべての機能は保持されます。ストレージが割り当てられており、このストレージがアドレス指定される場合があります。したがって、コードはそれをリテラルとして扱うだけでなく、指定されたメモリ位置にアクセスして変数を参照し(static const
である場合を除き、実行時に最適化できる)、その値をロードします。また、const
変数はストレージを割り当てているため、ヘッダーに追加して複数のCソースに含めると、extern
としてマークしない限り、「複数シンボル定義」リンケージエラーが発生します。そしてこの場合、コンパイラーは実際の値に対してコードを最適化できません(グローバル最適化がオンになっていない限り)。
#define
は、名前をその値に単純に置き換えます。さらに、プリプロセッサで#define
'd定数を使用できます。その値に基づいて条件付きコンパイルを実行するために#ifdef
と共に使用するか、文字列化演算子#
を使用して文字列とその値。コンパイラーはコンパイル時にその値を知っているため、その値に基づいてコードを最適化できます。
例えば:
#define SCALE 1
...
scaled_x = x * SCALE;
SCALE
が1
として定義されている場合、コンパイラはx * 1 == x
を知っているため乗算を削除できますが、SCALE
が(extern
)const
である場合、値を取得して乗算を実行するコードを生成する必要がありますなぜなら、値はリンク段階までわからないからです。 (extern
は、複数のソースファイルの定数を使用するために必要です。)
#define
を使用するのとほぼ同等の方法は、列挙を使用することです。
enum dummy_enum {
constant_value = 10010
};
ただし、これは整数値に制限されており、#define
の利点がないため、広く使用されていません。
const
は、コンパイルされたライブラリから定数値をインポートする必要がある場合、またはポインターで使用される場合に便利です。または、変数インデックス値を介してアクセスされる定数値の配列の場合。それ以外の場合、const
には#define
に対する利点はありません。
その理由は、ほとんどの場合、const
修飾変数ではなく定数が必要だからです。この2つは、C言語ではリモートでは同じではありません。たとえば、変数はstatic
- storage-durationオブジェクトの初期化子の一部として、非vla配列次元として(たとえば、構造内の配列のサイズ、C99以前の配列のサイズとして)有効ではありません。
Rの答えを少し拡張します。fieldWidth
は定数式ではありません。これはconst
で修飾された変数です。そのvalueは実行時まで確立されないため、コンパイル時の定数式が必要な場所(配列宣言やswitch
ステートメントのcaseラベルなど)で使用できません。 )。
前処理後に定数式_FIELD_WIDTH
_に展開されるマクロ_10
_と比較してください。この値isはコンパイル時に既知であるため、配列の次元、ケースラベルなどに使用できます。
RとBartの答えに追加するには、Cでシンボリックコンパイル時定数を定義する方法は1つしかありません:列挙型定数です。標準では、これらはint
型であると規定されています。私は個人的にあなたの例を書くでしょう
enum { fieldWidth = 10 };
しかし、それについては、Cプログラマーの間で味が大きく異なると思います。
Const intは常に適切とは限りませんが、何かを整数値として定義している場合、enumは通常#defineの代わりとして機能します。これは実際にそのような場合の私の好みです。
enum { FIELD_WIDTH = 16384 };
char buf[FIELD_WIDTH];
C++では、#defineをスコープできないのに対して、クラスまたは名前空間で列挙型をスコープできるため、これは大きな利点です。
Cでは、名前空間がなく、構造体内に列挙型をスコープできず、タイプセーフを取得できるかどうかもわからないため、実際に大きな利点はありませんが、Cプログラマーが私に指摘するかもしれません。
K&R(第2版、211ページ)によれば、「constおよびvolatileプロパティはANSI標準で新しく追加されました」。これは、本当に古いANSIコードにはこれらのキーワードがまったくなかったことを意味する場合があり、実際には単なる伝統の問題です。さらに、コンパイラーはconst変数を変更しようとする試みを検出する必要があるが、それ以外ではこれらの修飾子を無視する可能性があると述べています。一部のコンパイラは、const変数を含むコードを最適化せず、マシンコードの中間値として表すことができない場合があり(#defineのように)、これにより、ファーメモリへのアクセスに時間がかかり、パフォーマンスに影響する可能性があると思います。
一部のCコンパイラは、すべてのconst
変数をバイナリに格納します。これにより、 係数の大きなリスト を準備すると、埋め込みの世界で膨大な量のスペースが使用されます。
逆に、const
を使用すると、既存のプログラムをフラッシュして特定のパラメーターを変更できます。
Cで数値定数を定義する最良の方法は、enumを使用することです。 K&RのCプログラミング言語、39ページの対応する章をお読みください。