37

2 つのファイルで構成される C プログラムを考えてみましょう。

f1.c:

int x;

f2.c:

int x=2;

C99 標準のパラグラフ 6.9.2 を読んだところでは、このプログラムは拒否されるべきだということです。6.9.2 の私の解釈では、 variablexは で暫定的に定義されていますが、この暫定的な定義は翻訳単位の最後で実際の定義になるため、(私の意見では)定義が含まf1.cれているかのように動作するはずです。f1.cint x=0;

私が試すことができたすべてのコンパイラ(そして重要なことに、リンカー)で、これは起こりません。私が試したすべてのコンパイル プラットフォームは上記の 2 つのファイルをリンクしておりx、両方のファイルで の値は 2 です。

これが偶然に起こったのか、それとも標準が要求するものに加えて提供する「簡単な」機能として起こったのかは疑わしい. 考えてみれば、明示的にゼロに初期化されたものとは対照的に、イニシャライザを持たないグローバル変数に対するリンカーでの特別なサポートがあることを意味します。いずれにせよ、Fortran をコンパイルするにはリンカ機能が必要かもしれないと誰かが私に言いました。それは合理的な説明でしょう。

これについて何か考えはありますか?標準の他の解釈?f1.cファイルとf2.cリンクを拒否するプラットフォームの名前は?

注: 質問は静的分析のコンテキストで発生するため、これは重要です。2 つのファイルが何らかのプラットフォームでリンクされることを拒否する可能性がある場合、アナライザーはエラーを表示する必要がありますが、すべてのコンパイル プラットフォームがそれを受け入れる場合、それについて警告する理由はありません。

4

3 に答える 3

31

Cの外部変数とは何ですかも参照してください。これは、一般的な拡張として、有益な付録JのC標準で言及されています。

J.5.11複数の外部定義

キーワードexternの明示的な使用の有無にかかわらず、オブジェクトの識別子には複数の外部定義が存在する場合があります。定義が一致しない場合、または複数が初期化されている場合、動作は未定義です(6.9.2)。

警告

@litbがここで指摘しているように、また相互参照の質問に対する私の回答で述べているように、グローバル変数に複数の定義を使用すると、未定義の動作につながります。これは、「何かが起こる可能性がある」という標準の言い方です。発生する可能性のあることの1つは、プログラムが期待どおりに動作することです。そして、J.5.11は、おおよそ、「あなたはあなたが値するよりも頻繁に幸運であるかもしれない」と言っています。ただし、extern変数の複数の定義に依存するプログラム(明示的な'extern'キーワードの有無にかかわらず)は、厳密に準拠したプログラムではなく、どこでも機能することが保証されていません。同等に:それ自体が表示される場合と表示されない場合があるバグが含まれています。

于 2009-09-29T05:54:55.657 に答える
11

標準には「共通拡張」と呼ばれるものがあり、変数が一度だけ初期化される限り、変数を複数回定義することが許可されます。http://c-faq.com/decl/decldef.htmlを参照してください。

リンクされたページには、これは Unix プラットフォームに関連するものであると書かれています.c99 と c89 は同じだと思います.. 面白い。

于 2009-09-29T05:36:11.767 に答える
7

これは、olovb によるコメントに対する私の回答を明確にするためのものです。

「int x;」からコンパイルされたオブジェクト ファイルの nm の出力。このプラットフォームでは、シンボルの前に「_」が追加されます。つまり、変数 x は _x として表示されます。

00000000 T _main
         U _unknown
00000004 C _x
         U dyld_stub_binding_helper

「int x=1;」からコンパイルされたオブジェクト ファイルの nm の出力

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

「int x=0;」からコンパイルされたオブジェクト ファイルの nm の出力

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

「extern int x;」からコンパイルされたオブジェクト ファイルの nm の出力

00000000 T _main
         U _unknown
         U dyld_stub_binding_helper

編集:「extern int x;」からコンパイルされたオブジェクト ファイルの nm の出力 ここで、x は関数の 1 つで実際に使用されます

00000000 T _main
         U _unknown
         U _x
         U dyld_stub_binding_helper
于 2009-09-29T07:25:19.310 に答える