ヘッダファイル内のグローバル変数

私は2つのモジュール(.cファイル)と1つの.hヘッダファイルを持っています:

file1.c:

#include 
#include "global.h"

int main()
{
    i = 100;
    printf("%d\n",i);
    foo();
    return 0;
}

file2.c

#include 
#include "global.h"

void foo()
{
    i = 10;
    printf("%d\n",i);
}

global.h

int i;
extern void foo()

gcc file1.c file2.cを実行するとすべて正常に動作し、期待される出力が得られます。今、私は0と言うようにヘッダーファイル内の変数 'i'を初期化し、再度コンパイルするとリンカーエラーが発生します:

/tmp/cc0oj7yA.o:(.bss+0x0): multiple definition of `i'
/tmp/cckd7TTI.o:(.bss+0x0): first defined here

ヘッダーファイル(gcc file1.c)の初期化でfile1.c(foo()への呼び出しを削除する)をコンパイルするだけで、すべて正常に動作します。何が起こっている?

38

5 答え

以下の3つのシナリオがあります。

    2つの .c ファイルと int i;2つの .c ファイルと int i = 100; のヘッダー(またはそれ以外の値) 1 .c ファイルと int i = 100;

それぞれのシナリオで、ヘッダファイルの内容を .c ファイルと .o ファイルにコンパイルしたこの .c これらは一緒につながっています。

次に、

    すべての .o ファイルにはそのうちの1つが含まれているので、リンカーは「ok」と言います。両方の .o ファイルには(たとえ値が同じであっても)衝突する値を持つ定義が含まれているため、
  1. は機能しません。 .o ファイル内のすべてのファイル名を指定します。

  2. .o ファイルが1つしかなく、衝突の可能性がないので、もちろん動作します。

きれいなことがIMHOになる

  • ヘッダーファイルに extern int i; または int i;
  • 次に、 file1.c にiの "実際の"定義(つまり int i = 100; )を置きます。この場合、この初期化はプログラムの開始時に使用され、 main()の対応する行は省略できます。 (また、命名は単なる例であり、実際のプログラムではグローバル変数の名前を i にしないでください)。
34
追加された
@chandreshヘッダーファイルのコピーのみが常にあります。 .cファイルは一度も含まれず、常にコンパイルされるので、 int i; は一度だけ発生します。 .hファイルは何回か含めることができますが、いくつかの extern int i; は害を与えません。それにもかかわらず、ヘッダーファイルのインクルードガードは決して悪い考えではありません。
追加された 著者 glglgl,
@FoadRezek私はあなたが質問のファイル構造を試したと仮定します。これは、アサイメントがファイルレベルでは有効でなく、関数内でのみ有効であるためです。
追加された 著者 glglgl,
@chandreshヘッダーファイルに int g = 0; があるためです。さらに、あなたがすることはC ++であり、Cではありません。
追加された 著者 glglgl,
@chandreshあなたがインクルードガードを誤実装しているか、それ以外のものが間違っているかもしれません。遠隔から伝えるのは難しい。
追加された 著者 glglgl,
@Assimilater質問は、Cについて記述されているように動作します。 C ++でCソースファイル/ヘッダーファイルを使用する場合、いくつかの注意が必要です。
追加された 著者 glglgl,
@ user2383973メモリはリンカによって割り当てられ、予約されています。割り当てと1つの "暫定的な"定義で1つの要求を見ると、すべて正常です。複数の割り当てが表示されても、同じ値を持っていてもOKではありません。
追加された 著者 glglgl,
私が " extern int i か、単に int i; のいずれかをヘッダファイル": extern int i 事故によって「本当の」定義が欠けているかどうかを直ちに教えてくれます。単なる int i では、 0 に対するサイレント定義があります。
追加された 著者 glglgl,
@glglgl。どうすればヘッダーに#ifndefを入れ、ヘッダーに変数 int a を宣言すればよいですか?ヘッダーファイルのコピーを1つだけ持っているにもかかわらず、複数の定義エラーがまだ発生しています。
追加された 著者 chandresh,
@glglgl。私の懸念は、ガードを含むにもかかわらず、複数の定義エラーがまだ残っていることです。それではなぜガードはそれを防止しないのでしょうか?
追加された 著者 chandresh,
@glglgl。あなた自身を見るには、ここからファイルをダウンロードしてください。リンク
追加された 著者 chandresh,
@glglgl。ありがとう。出来た。奇妙なことに、インクルードガードがC ++言語で int g = 0 または int g では機能しないということです。それは常に誤りです。
追加された 著者 chandresh,
私は、メモリが変数iに割り当てられたときと同じように詳細を説明することができます。今まで私が理解しているのは、global.hのint iはextern int iと等価です。これは、両方のオブジェクトファイルが他の場所に割り当てられているメモリを参照することを意味します。
追加された 著者 user2383973,
なぜfile1.cのiの使用法を関数のスコープ(グローバルスコープ)の外に移動するのですか?file1.c:4:1:警告:型指定子がない、デフォルトは 'int' [-Wimplicit-int]
追加された 著者 Foad Rezek,

ヘッダー内の変数を初期化しないでください。ヘッダの宣言と初期化を c ファイルのいずれかに入れます。

ヘッダーには:

extern int i;

file2.cの場合:

int i=1;
25
追加された
@Banthar:なぜ、それは2番目のケースで動作するのですか(私はfile1.cをコンパイルするだけです)?
追加された 著者 Bruce,
@ブルース:この場合、それは一度だけ初期化されるからです。
追加された 著者 glglgl,
OPが変数を初期化していないことに注意してください。これは仮定義です。
追加された 著者 ninjalj,

ヘッダーファイルにグローバル変数を定義しないでください。ヘッダファイルでそれらを extern として宣言し、それらを .c ソースファイルで定義することができます。

(注:Cでは、 int i; は暫定的な定義で、その変数に他の定義が見つからない場合は、変数(=定義)のストレージを割り当てます。

9
追加された
追加された 著者 ninjalj,
どうすればヘッダーに#ifndefを入れ、ヘッダーに変数 int a を宣言すればよいですか?ヘッダーファイルのコピーを1つだけ持っているにもかかわらず、複数の定義エラーがまだ発生しています。
追加された 著者 chandresh,

Dont define varibale in header file , do declaration in header file(good practice ) .. in your case it is working because multiple weak symbols .. Read about weak and strong symbol ....link :http://csapp.cs.cmu.edu/public/ch7-preview.pdf

このタイプのコードは、移植中に問題を引き起こします。

1
追加された
@tod。最高の言葉の答えではありませんが、定義/宣言が意味することを知りたい場合は、stackoverflow.com/a/1164190/310560
追加された 著者 Assimilater,
あなたは何を言おうとしていますか?
追加された 著者 tod,
@Assimilaterうわー、ありがとう
追加された 著者 tod,

@glglglは、あなたがしようとしていることが機能していなかった理由を既に説明しました。実際、ヘッダー内の変数を定義することを本当に目指しているのであれば、いくつかのプリプロセッサディレクティブを使って欺くことができます:

file1.c:

#include 

#define DEFINE_I
#include "global.h"

int main()
{
    printf("%d\n",i);
    foo();
    return 0;
}

file2.c:

#include 
#include "global.h"

void foo()
{
    i = 54;
    printf("%d\n",i);
}

global.h:

#ifdef DEFINE_I
int i = 42;
#else
extern int i;
#endif

void foo();

このような状況では、DEFINE_Iを定義したコンパイルユニットで i のみを定義し、他の場所では宣言します。リンカーは文句を言っていません。

私はこれを数回前に見てきました。その前に、列挙型がヘッダーで宣言されていましたが、その下に対応するラベルを含むchar **の定義がありました。私は、なぜ特定のソースファイルに入れるのではなく、ヘッダーにその定義があるのが望ましいのか理解していますが、実装がとてもエレガントかどうかはわかりません。

0
追加された