Cの以下のステートメントの中でどちらを使用するのが良いですか?
static const int var = 5;
または
#define var 5
または
enum { var = 5 };
Cの以下のステートメントの中でどちらを使用するのが良いですか?
static const int var = 5;
または
#define var 5
または
enum { var = 5 };
回答:
それはあなたが値を必要とするものに依存します。あなた(そして今までのところ全員)は3番目の選択肢を省略しました:
static const int var = 5;
#define var 5
enum { var = 5 };
名前の選択に関する問題を無視すると、次のようになります。
したがって、ほとんどのコンテキストでは、選択肢よりも「列挙型」を優先します。それ以外の場合、最初と最後の箇条書きが制御要因となる可能性が高く、両方を同時に満たす必要がある場合は、より深く考える必要があります。
C ++について質問している場合は、毎回オプション(1)(静的const)を使用します。
enum
は、int
([C99] 6.7.2.2/3)として実装されることです。Aを#define
使用すると、unsignedおよびlong with U
とL
suffix を指定でき、型を指定const
できます。enum
通常の型変換で問題が発生する可能性があります。
enum
も#define
使用しません。値は、データセグメント、ヒープ、スタックのストレージに割り当てられるのではなく、命令の一部としてオブジェクトコードに表示されます。にいくつかのスペースが割り当てられますがstatic const int
、アドレスを取得しない場合、コンパイラーはそれを最適化する可能性があります。
enum
s(およびstatic const
)のもう1つの「投票」:変更することはできません。define
することができ#undefine
「D enum
とがstatic const
与えられた値に固定されています。
一般的に言って:
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_ )、ライブラリを閲覧すると、これは(通常)特定の領域(ライブラリ)の名前が続き、次に意味のある名前が続くことに気づくでしょう。
それは一般的に長い名前になります:)
static
、アドレスが取得されたものだけが残るはずです。アドレスが取得された#define
場合、orまたはenum
(アドレスなし)を使用することはできませんでした。「コンパイル時評価」を廃止できる場合は、extern const
代わりに探している可能性があります。
#if
望ましい超えるかもしれません#ifdef
ブールフラグのが、この場合には、それはそれは不可能定義することになるだろうvar
として、0
コマンドラインから。したがって、この場合は、の正当な値である#ifdef
限り、より意味0
がありますvar
。
特にCでは?Cでの正解は次のとおりです。使用#define
(または、適切な場合はenum
)
const
オブジェクトのスコープと型のプロパティを持つことは有益ですが、実際にconst
は、Cのオブジェクト(C ++ではなく)は真の定数ではないため、通常、ほとんどの実用的なケースでは役に立ちません。
したがって、Cでは、定数をどのように使用するかによって、選択を決定する必要があります。たとえば、const int
オブジェクトをcase
ラベルとして使用することはできません(マクロは機能します)。const int
オブジェクトをビットフィールド幅として使用することはできません(マクロは機能します)。C89 / 90では、const
オブジェクトを使用して配列サイズを指定することはできません(マクロは機能します)。C99でもconst
、非VLA配列が必要な場合、オブジェクトを使用して配列サイズを指定することはできません。
これがあなたにとって重要である場合、それはあなたの選択を決定します。ほとんどの場合#define
、Cで使用するしかありません。また、C-で真の定数を生成する別の代替方法を忘れないでくださいenum
。
C ++ではconst
オブジェクトは真の定数なので、C ++ではほとんどの場合、const
バリアントを使用する方が適切です(static
ただし、C ++では明示的にする必要はありません)。
const int
case-labelsでオブジェクトを使用することは、C言語のすべてのバージョンで違法です。(もちろん、コンパイラーは非標準のC ++のような言語拡張としてそれを自由にサポートします。)
const
読み取り専用を意味します。const int r = rand();
完全に合法です。
constexpr
をconst
特別に使用する場合と比較して使用する方が良いです。stl
array
bitset
switch()
ステートメントではなく、ステートメントでテストしたはずですcase
。私は☺あまりにもこの1に巻き込まれました
違いstatic const
とは、#define
かつての用途メモリーことと、後は保存用のメモリを使用していないです。次に、anのアドレスを渡すことはできませんが、#define
aのアドレスを渡すことはできますstatic const
。実際の状況によって異なりますが、この2つから1つを選択する必要があります。どちらもさまざまな状況下で最高です。一方が他方よりも優れているとは思わないでください... :-)
もしそうなら、デニス・リッチー は最高のものを一人で残していたでしょう...ははは... :-)
const
がメモリを使用することは(もう)真実ではありません。GCC(4.5.3およびいくつかの新しいバージョンでテスト済み)const int
は、-O3を使用するときに、コード内のを直接リテラルに簡単に最適化します。したがって、低RAM組み込み開発(AVRなど)を行う場合、GCCまたは他の互換コンパイラを使用すれば、C constsを安全に使用できます。私はそれをテストしていませんが、Clangが同じことを行うと期待しています。
C #define
でははるかに人気があります。これらの値を使用して、配列サイズを宣言できます。次に例を示します。
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI Cではstatic const
、私の知る限り、このコンテキストでを使用することはできません。C ++では、これらのケースではマクロを使用しないでください。あなたは書ける
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
またstatic
、内部リンケージはconst
既に[C ++のみ]で暗黙指定されているため、除外することもできます。
const int MY_CONSTANT = 5;
一つのファイルにし、それにアクセスするextern const int MY_CONSTANT;
別に。const
「6.2.2:5オブジェクトの識別子の宣言にファイルスコープがあり、ストレージクラスの指定子がない場合、そのリンケージは外部です」というデフォルトの動作の変更に関する標準(少なくともC99)には情報が見つかりませんでした。
bar
VLA(可変長配列)です。コンパイラーは、その長さが一定であるかのようにコードを生成する可能性があります。
const
C のもう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
これらの場合は、typedを使用できます。それ以外の場合...
static uint8_t const ARRAY_SIZE = 16;
特に#define ARRAY_SIZE 256
ヘッダーが絡まったウェブの10層に埋め込まれている場合は、突然コンパイルできなくなった理由を追跡するのは少し難しいかもしれません。そのすべての大文字の名前ARRAY_SIZE
は問題を求めています。マクロ用にALL_CAPSを予約し、ALL_CAPS形式でないマクロを定義しないでください。
const
。これはもっと賛成できるかもしれません!
あなたがそれでうまくいくことができるならばstatic const
、多くの利点があります。これは、通常のスコープの原則に従い、デバッガーに表示され、変数が従う規則に従います。
ただし、少なくとも元のC標準では、実際には定数ではありません。を使用する場合、宣言として#define var 5
書き込むことはできますがint foo[var];
、それを行うことはできません(コンパイラ拡張としてを除く)static const int var = 5;
。これは、static const
バージョンが使用できる場所であればどこでも#define
バージョンを使用できるC ++の場合とは異なります。C99も同様です。
ただし、#define
定数に小文字の名前を付けないでください。それは、翻訳単位の終わりまで、その名前の可能な使用を上書きします。マクロ定数は、事実上、独自の名前空間である必要があります。これは、伝統的にすべて大文字であり、おそらくプレフィックスが付いています。
const
C99ではまだ実際の定数ではありません。const
C99では可変長配列をサポートしているため、配列サイズをC99で宣言できます。このため、VLAが許可されている場合にのみ機能します。たとえば、C99でも、を使用しconst
てのメンバー配列のサイズを宣言することはできませんstruct
。
const int
では、C ++ constまたはマクロのようにサイズを指定して配列を完全に初期化できます。標準からのGCCのこの偏差に依存するかどうかはもちろんあなたの選択ですが、GCCまたはClang以外のコンパイラの使用が本当に予測できない限り、私は個人的にそれを採用します。後者は同じ機能を備えています(Clangでテスト済み) 3.7)。
#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...
#define var 5
あなたがのようなものがある場合、あなたはトラブルを引き起こしますmystruct.var
。
例えば、
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
プリプロセッサがそれを置き換え、コードはコンパイルされません。このため、従来のコーディングスタイルでは、すべての定数#define
sが競合を避けるために大文字を使用することを推奨しています。
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
^
定義が警告を与えるとき、列挙はエラーを与えることに注意してください。
定義
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
、マシンコードを編集して修正することもできます。
5
。しかし#define
、これはプリプロセッサマクロであるため変更できません。バイナリプログラムには存在しません。CONST_VALUE
使用されているすべての場所を変更したい場合は、1つずつ変更する必要がありました。
#define CONST 5
、を書いて、if (CONST == 5) { do_this(); } else { do_that(); }
コンパイラがelse
ブランチを削除するとします。マシンコードを編集しCONST
て6 に変更することをどのように提案しますか?
#define
は十分ではありません。
#define
です。これを行う唯一の実際の方法は、ソースコードを編集して再コンパイルすることです。
問題は整数に関するものでしたが、定数の構造または文字列が必要な場合は、#defineと列挙型は役に立たないことに注意してください。これらは通常、ポインタとして関数に渡されます。(文字列の場合は必須です。構造体の場合ははるかに効率的です。)
整数に関しては、メモリが非常に限られている組み込み環境にいる場合、定数が格納されている場所と定数へのアクセスがどのようにコンパイルされるかについて心配する必要があるかもしれません。コンパイラーは実行時に2つのconstを追加しますが、コンパイル時に2つの#defineを追加します。#define定数は、1つ以上のMOV [即時]命令に変換できます。これは、定数がプログラムメモリに効果的に格納されることを意味します。const定数は、データメモリの.constセクションに格納されます。ハーバードアーキテクチャのシステムでは、パフォーマンスとメモリ使用量に違いが生じる可能性がありますが、小さい可能性があります。それらは、内部ループのハードコア最適化にとって重要かもしれません。
「これが常に最良」の答えがあるとは思わないでください、しかしマシューが言ったように
static const
タイプセーフです。#define
ただし、私の最大の不満は、Visual Studioでデバッグしているときに変数を監視できないことです。シンボルが見つからないというエラーが発生します。
ちなみに、#define
適切なスコープを提供しますが、「実際の」定数のように動作するの代替は「列挙型」です。例えば:
enum {number_ten = 10;}
多くの場合、列挙型を定義し、それらの型の変数を作成すると便利です。これが行われると、デバッガは列挙名に従って変数を表示できる場合があります。
ただし、C ++では、列挙型には整数との互換性に制限があります。たとえば、デフォルトでは、それらに対して演算を実行することはできません。私はそれが列挙型の好奇心が強いデフォルトの振る舞いであることがわかりました。「厳格な列挙型」があればよかったのですが、C ++がCと一般的に互換性を持つことを望んでいるので、「列挙型」のデフォルトの動作は整数と交換可能であると思います。
int
であるので、「列挙型ハック」は他の整数型では使用できません。(列挙型は、必ずしも実装定義の整数型と互換性がありますint
が、必ずしもそうではありませんが、この場合、型は匿名であるため、問題ではありません。)
int
列挙型の変数(コンパイラに許可されている)以外の型を割り当て、そのような変数に割り当てようとすると、MISRA-Cが不規則に動作することを読みました独自の列挙のメンバー。標準化委員会が、指定されたセマンティクスで整数型を宣言する移植可能な方法を追加してほしいと思います。 ANYプラットフォームは、かかわらずのchar
サイズ、コンパイラがたくさん追加する場合であっても、65536 MODをラップする型を宣言などのことができるようにすべきAND R0,#0xFFFF
か、同等の指示を。
uint16_t
もちろん使用できますが、これは列挙型ではありません。ユーザーが指定した列挙型を表すために使用される整数型を指定できるようにするにはいいだろうが、あなたは多くのと同じ効果を得ることができtypedef
ためuint16_t
と、一連の#define
個々の値について秒。
2U < -1L
偽真、その他として、私たちは今、いくつかのプラットフォームでは、との比較を実装するという事実で立ち往生しているuint32_t
し、int32_t
署名などを署名されていないものもありますが、すべてのコンパイラでセマンティクスが一貫している型を含む、Cの上位互換の後継を委員会が定義できなかったという意味ではありません。
単純な違い:
前処理時に、定数はその値に置き換えられます。したがって、逆参照演算子を定義に適用することはできませんが、逆参照演算子を変数に適用することはできます。
ご想像のとおり、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はプログラムのテキストセグメントに割り当てられます。
上記では、参照演算子についてお話したかったので、逆参照を参照に置き換えます。
const
。Cにはenum-constants以外の記号定数はありません。A const int
は変数です。また、言語と特定の実装を混同します。オブジェクトを配置する場所は必要ありません。また、gccについても当てはまりません。通常const
は、修飾された変数を.rodata
セクションに配置します。しかし、それはターゲットプラットフォームによって異なります。そして、あなたは演算子のアドレスを意味します&
。