現在、小さなCプログラムのコンパイル/リンク中にGCCからエラーが発生しないのはなぜかと思っています。
version.h
で次の文字列を宣言しました。
const char* const VERSION;
version.c
で、変数の初期化を設定しました。
const char* const VERSION = "0.8 rev 213";
問題ありません。プログラムの残りの部分で文字列を使用できます。
Cファイルがない場合、コンパイル/リンク中にエラーは発生しませんが、変数にアクセスしようとすると、プログラムは(もちろん)SIGSEGVで失敗します。
変数VERSION
を設定する私の方法は正しいですか、またはより良い方法がありますか?または、コンパイル/リンク中にエラーが発生する可能性はありますか?
defined(宣言だけでなく)ヘッダーに変数があります。
このヘッダーを複数のソースファイルからインクルードする場合、動作はundefinedです。標準からの関連する引用は次のとおりです。
J.2未定義の動作
…
外部リンケージを持つ識別子が使用されていますが、プログラムには識別子の外部定義が1つだけ存在しないか、識別子が使用されておらず、識別子に複数の外部定義が存在します。
…
ここでは、重複tentativeデータ定義のマージである、GCC固有の(実際には多くのコンパイラーに共通ですが、まだ標準ではない)動作に依存しています。 -fcommon
および-fno-common
GCCコンパイルフラグのヘルプを参照してください。すべてのコンパイラがこのように動作するわけではありません。これは、Cが登場する前のFortranの動作方法であったため、歴史的にはリンカーの一般的な動作です。
この言語拡張を想定して、定義の1つ(明示的な初期化子を持つ定義)は、文字列リテラルを指すように変数を初期化します。ただし、この定義を省略すると、ゼロで初期化されたままになります。つまり、定数NULLポインターになります。あまり役に立たない。
長い話を短くするために、決してそれをしないでください。ヘッダーでグローバル変数を宣言する(定義しない)には、extern
を使用します。そうすると、他の場所で定義を省略しようとすると、可能性の高いリンカーエラーが発生します(標準ではこの違反の診断は必要ありませんが、すべての既知の実装は1つを生成します)。
あなたの例は、仮定義( 6.9 .2p2 )これは、一般的ですが非標準では複数のファイルに拡張されています。
仮の定義は、extern
がなく、初期化子がない変数宣言です。一般的な実装(意図的なしゃれ)では、暫定的な定義により、common
シンボルと呼ばれる特別な種類のシンボルが作成されます。リンク中に、同じ名前の通常のシンボルが存在する場合、他の一般的なシンボルは通常のシンボルへの参照になります。つまり、インクルードのためにそこに作成された翻訳単位内のすべての空のVERSION
sは通常のシンボルへの参照になりますシンボルconst char* const VERSION = "0.8 rev 213";
。そのような通常のシンボルがない場合、共通のシンボルは単一のゼロで初期化された変数にマージされます。
Cコンパイラでこれに対して警告を出す方法がわかりません。
この
const char* const VERSION;
const char* const VERSION = "0.8 rev 213";
私が試したものに関係なくgccで動作するようです(g++
は受け入れません-C++には仮の定義機能がなく、明示的に初期化されていないconst変数が好きではありません)。ただし、-fno-common
(かなり「一般的な」(および強く推奨)の非標準オプション(gcc、clang、およびtccにはすべてあります))を使用してコンパイルできます。初期化されていない場合、そして、初期化されたextern-less宣言は異なる翻訳単位にあります。
例:
v.c:
const char * VERSION;
main.c
const char* VERSION;
int main(){}
コンパイルとリンク:
gcc main.c v.c #no error because of tentative definitions
g++ main.c v.c #linker error because C++ doesn't have tentative definitions
gcc main.c v.c -fno-common #linker error because tentative defs. are disabled
(C++の例のために、この例の2番目のconst
を削除しました。C++は、さらにconstグローバルを静的にするか、仮定義機能のデモンストレーションを複雑にするようなものにします。)
一時的な定義を無効にするか、C++を使用すると、ヘッダー内のすべての変数宣言にextern
を含める必要があります
version.h:
extern const char* const VERSION;
また、各グローバルに対して正確に1つの定義が必要であり、その定義にはイニシャライザが必要です。または、extern
がありません(extern
を初期化された変数に適用すると警告するコンパイラもあります)。
version.c:
#include "version.h" //optional; for type checking
const char* const VERSION = "0.8 rev 213";