以下のCのステートメントのうち、どれを使用するのが良いでしょうか。
static const int var = 5;
または
#define var 5
または
enum { var = 5 };
一般的に言えば:
static const
それはスコープを尊重し、タイプセーフだからです。
私が見ることができた唯一の警告:あなたが変数をコマンドラインで定義される可能性がある場合。まだ選択肢があります。
#ifdef VAR // Very bad name, not long enough, too general, etc..
static int const var = VAR;
#else
static int const var = 5; // default value
#endif
可能な場合はいつでも、マクロ/省略記号の代わりに、タイプセーフな代替手段を使用してください。
もしあなたが本当にマクロを使う必要があるなら(例えば__FILE__
や__LINE__
が欲しいのなら)、そのマクロにはもっと慎重に名前をつけたほうがいいでしょう: 命名規則ではBoost プロジェクトの名前(ここではBOOST_)から始めますが、ライブラリを熟読している間、(一般的に)特定の領域(ライブラリ)の名前が続き、次に意味のある名前が続くことに気付くでしょう。
それは一般的に長い名前を作ります:)
それはあなたが価値を必要としているものによります。あなた(そして今までの他のみんな)は3番目の選択肢を省略しました:
static const int var = 5;
#define var 5
enum { var = 5 };
名前の選択に関する問題を無視すると、
そのため、ほとんどの場合、選択肢よりも「列挙型」を選択してください。そうでなければ、最初と最後の箇条書きの点が支配要因になる可能性があります - そしてあなたが両方を同時に満たす必要があるなら、あなたはもっと慎重に考えなければなりません。
C++について質問しているのであれば、毎回option(1) - 静的定数 - を使用してください。
Cでは、具体的には? Cでは、正しい答えは次のとおりです。#define
(または、適切な場合はenum
)を使用します。
const
オブジェクトのスコープと型付けのプロパティを持つことは有益ですが、実際には(C++とは対照的に)Cのconst
オブジェクトは真の定数ではないため、ほとんどの場合実用的ではありません。
ですから、C言語では、定数をどのように使用する予定であるかによって選択を決定する必要があります。たとえば、const int
オブジェクトをcase
ラベルとして使用することはできません(マクロは機能します)。 const int
オブジェクトをビットフィールド幅として使用することはできません(マクロは機能します)。 C89/90では、配列サイズを指定するためにconst
オブジェクトを使用することはできません(マクロは動作します)。 C99でも、 - _ vla _ /以外の配列が必要なときは、const
オブジェクトを使って配列サイズを指定することはできません。
これがあなたにとって重要であるならば、それはあなたの選択を決定します。ほとんどの場合、Cで#define
を使用する以外に選択肢はありません。そして、Cで正しい定数を生成する別の方法、enum
を忘れないでください。
C++ではconst
オブジェクトは真の定数なので、C++ではほとんどの場合const
バリアントを使用することをお勧めします(ただし、C++では明示的なstatic
は不要です)。
static const
と#define
の違いは、前者はメモリを使用し、後者はメモリをストレージとして使用しないことです。次に、#define
のアドレスを渡すことはできませんが、static const
のアドレスを渡すことはできます。実際には、私たちがどのような状況下にあるかに応じて、これら2つの中から1つを選択する必要があります。どちらもさまざまな状況下で最高に機能します。一方が他方より優れているとは思わないでください... :-)
もしそうなら、 Dennis Ritchie だけで最高の1つを守っていただろう……笑…:-)
Cでは#define
はもっと人気があります。配列サイズを宣言するためにこれらの値を使うことができます。例えば:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
私の知る限り、ANSI Cではこの文脈でstatic const
sを使用することはできません。 C++では、このような場合はマクロを避けるべきです。あなたは書ける
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
内部リンケージは既にstatic
によって暗黙のうちに暗示されているので(C++のみ)、const
も除外します。
Cのconst
のもう1つの欠点は、別のconst
を初期化するときにその値を使用できないことです。
static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;
// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND
* NUMBER_OF_HANDS;
コンパイラでは定数と見なされないため、これでもconstでは機能しません。
static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!
そうでない場合は、const
と入力してください。
うまくいけば、static const
には多くの利点があります。通常のスコープの原則に従い、デバッガーで表示され、一般的に変数が従うルールに従います。
ただし、少なくとも元のC標準では、実際には定数ではありません。 #define var 5
を使用する場合、int foo[var];
を宣言として記述することはできますが、static const int var = 5;
を使用して(コンパイラー拡張機能を除く)行うことはできません。C++ではそうではありません。 static const
バージョンは#define
バージョンが使用できる場所であればどこでも使用できますが、これはC99にも当てはまると思います。
ただし、#define
定数に小文字の名前を付けないでください。翻訳単位の終わりまで、その名前の使用可能性をオーバーライドします。マクロ定数は、事実上独自の名前空間に存在する必要があります。名前空間は、従来はすべて大文字で、おそらく接頭辞が付いています。
1つの違いを示すためにクイックテストプログラムを書きました。
#include <stdio.h>
enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};
#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32
int main(int argc, char *argv[]) {
printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);
return(0);
}
これはこれらのエラーおよび警告に準拠しています。
main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
^
Defineが警告を出すと、enumはエラーを出すことに注意してください。
#define var 5
のようなものがある場合、mystruct.var
はあなたに問題を引き起こすでしょう。
例えば、
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
プリプロセッサがそれを置き換えて、コードはコンパイルされません。このため、従来のコーディングスタイルでは、競合を避けるためにすべての定数#define
sに大文字を使用することが推奨されています。
#defineの代わりにconstを使用することが常に好ましいです。これは、constはコンパイラによって、#defineはプリプロセッサによって処理されるためです。 #define自体がコードの一部ではないようです(大まかに言って)。
例:
#define PI 3.1416
シンボル名PIは、コンパイラからは見られないかもしれません。ソースコードがコンパイラに到達する前に、プリプロセッサによって削除される可能性があります。その結果、名前PIがシンボルテーブルに入力されない場合があります。エラーメッセージはPIではなく3.1416を参照する可能性があるため、コンパイル中に定数の使用を含むエラーが発生した場合、これは混乱を招く可能性があります。あなたが書いていないヘッダファイルでPIが定義されていたら、その3.1416がどこから来たのか分からないでしょう。
この問題は、シンボリックデバッガでも発生する可能性があります。これも、プログラミングしている名前がシンボルテーブルに表示されていない可能性があるためです。
溶液:
const double PI = 3.1416; //or static const...
定義
const int const_value = 5;
常に定数値を定義するわけではありません。いくつかのコンパイラ(例えば tcc 0.9.26 )は単に "const_value"という名前で識別されるメモリを割り当てます。識別子 "const_value"を使用して、このメモリを変更することはできません。しかし、それでも別の識別子を使ってメモリを変更することができます。
const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.
これは定義を意味します
#define CONST_VALUE 5
決して変更できない定数値を定義する唯一の方法です。
ちなみに、適切なスコープを提供しますが、 "本当の"定数のように振る舞う#define
の代替は "enum"です。例えば:
enum {number_ten = 10;}
多くの場合、列挙型を定義してそれらの型の変数を作成すると便利です。それが行われると、デバッガはその列挙名に従って変数を表示できる可能性があります。
ただし、それを行う際の重要な注意点の1つは、C++では、列挙型は整数との互換性が限られていることです。たとえば、デフォルトでは、それらに対して算術演算を実行することはできません。私はそれがenumにとっては奇妙なデフォルトの振る舞いだと思います。 C++とCの互換性を一般的に望んでいることを考えると、 "厳密なenum"型を持つのはいいことでしたが、 "enum"型のデフォルトの振る舞いは整数と交換可能であるべきです。
「常に最高」の答えがあるとは思わないでください、しかし、マティウが言ったように
static const
タイプセーフです。 #define
の最大の利点は、 Visual Studioでのデバッグ時です あなたは変数を見ることができません。シンボルが見つからないというエラーが発生します。
問題は整数に関するものでしたが、定数構造や文字列が必要な場合は#defineとenumは役に立ちません。これらは通常、ポインタとして関数に渡されます。 (文字列の場合は必須、構造体の場合ははるかに効率的です)
整数に関して言えば、メモリが非常に限られた組み込み環境では、定数がどこに格納されているのか、そしてそれへのアクセスがどのようにコンパイルされるのかについて心配する必要があるかもしれません。コンパイラは実行時に2つのconstを追加しますが、コンパイル時に2つの#defineを追加します。 #define定数は、1つ以上のMOV [即値]命令に変換できます。これは、定数がプログラムメモリに効果的に格納されることを意味します。定数定数はデータメモリの.constセクションに格納されます。ハーバードアーキテクチャを採用したシステムでは、パフォーマンスとメモリ使用量に差がある可能性がありますが、それらは小さい可能性があります。それらは内部ループのハードコア最適化にとって重要かもしれません。
単純な違い
前処理時に、定数はその値に置き換えられます。したがって、間接参照演算子を定義に適用することはできませんが、間接参照演算子を変数に適用することはできます。
ご想像のとおり、defineはstatic constよりも高速です。
たとえば、
#define mymax 100
printf("address of constant is %p",&mymax);
はできません。
しかし
const int mymax_var=100
あなたはprintf("address of constant is %p",&mymax_var);
をすることができます。
より明確にするために、定義は前処理段階でその値によって置き換えられるので、プログラムには変数が格納されていません。定義が使用されたプログラムのテキストセグメントからのコードだけがあります。
しかし、静的constには、どこかに割り当てられた変数があります。 gccの場合、静的constはプログラムのテキストセグメントに割り当てられます。
上記では、参照演算子について説明したかったので、間接参照を参照に置き換えます。
私が正しいかどうかはわかりませんが、私の意見では、#define
d値を呼び出すほうが、他の通常宣言されている変数(またはconst値)を呼び出すよりもはるかに高速です。これは、プログラムが実行されていて、通常宣言されている変数を使用する必要があるときに、その変数を取得するためにメモリ内の正確な場所にジャンプする必要があるためです。
反対に、#define
d値を使用する場合、プログラムは割り当てられたメモリにジャンプする必要はなく、単に値を取得します。 #define myValue 7
とプログラムがmyValue
を呼び出している場合は、7
を呼び出したときとまったく同じように動作します。
私たちはMBF16X上で生成されたアセンブラコードを調べました。
そのため、型チェックにはconst int
が優先されますが、#define
は古いスタイルです。たぶんそれはコンパイラ特有のものです。それで、あなたの生成されたアセンブラコードをチェックしてください。