C/C++の役割ドメインのグローバル変数を分析する


スレ主は今日1件のとても卵の痛みの事に出会って、30行のコードを书いて意外にも间违って、ニマは怒って半日原因が见つからないで、本当に若くて努力しないでとても悲しんでいます!
くだらないことは言わないで、まずコードを見てください.
(IDE=vs2010)
Head.h
#ifndef HEAD_H   //     ,      
#define HEAD_H 

struct Node{

int data;
};

Node Dt;   //       。
void setData(int dt);
#endif;

Head.cpp
#include"Head.h"

void setData(int dt)
{
	Dt.data=dt;
}

Main.cpp
#include<iostream>
#include"Head.h"
using namespace std;

int main()
{

	int input=1000;
	setData(input);
	cout<<Dt.data;
	return 0;
}
コンパイル中、エラーメッセージ:
Main.obj:error LNK 2005:"struct Node Dt"(?Dt@@3 UNode@@A)は既にHead.objでd:document_を定義するx64\documents\visual studio 2010\Projects\Test\Debug\Test.exe:fatal error LNK 1169:1つ以上の多重定義シンボルを見つける
あら、道理がないですね.どうして再定義しますか.コンパイラのお兄さん、風を引いたでしょう.よく考えてみるとやはり自分の問題だ.結果:
分析:Node Dt;//構造体変数を定義します.この変数を他のファイルで使用するには、externタイプ、すなわちextern Dtとして宣言する必要があります.Head.h#ifndef HEAD_H//プリプロセッシングのコンパイル、#define HEAD_を複数回含まないようにするH struct Node{int data;};Node Dt;//構造体変数を定義します.void setData(int dt);#endif; Head.cpp
#include"Head.h"

void setData(int dt)
{
	Dt.data=dt;
}

Main.cpp
#include<iostream>
#include"Head.h"
using namespace std;
extern Dt;//    
int main()
{

	int input=1000;
	setData(input);
	cout<<Dt.data;
	return 0;
}

あら、どうしたんですか.直接文法が間違っていて、葛藤しています.本を調べてみると、externの使い方はincludeファイルを使わないときに使うようです.
一計は成り立たず、一計を再生する:直接Head.hではextern Node Dtと宣言されていますが、今回はなるでしょう.
Head.h
#ifndef HEAD_H   //     ,      
#define HEAD_H 

struct Node{

int data;
};

extern Node Dt;   //       。
void setData(int dt);
#endif;

Head.cpp
#include"Head.h"

void setData(int dt)
{
	Dt.data=dt;
}

Main.cpp
#include<iostream>
#include"Head.h"
using namespace std;

int main()
{

	int input=1000;
	setData(input);
	cout<<Dt.data;
	return 0;
} 

コンパイラは定義された識別子Dtが見つからないことを示す.これはおじいさんにどうしたらいいですか.
最初からよく考えてみると間違いないでしょうが、なぜ再定義されたのでしょうか.
#ifndef   HEAD_H
#define HEAD_H
コードセグメント
#endif;
 
これは重複定義を避けることができるのではないでしょうか.どうして、私はまたコンパイラのお兄さんを疑ったのですか.その後、葛藤の中で、高人点化を経てやっと何が起こっているのかが分かった.もとは、上記のコードセグメントは、1つのファイルに同じファイルが複数回含まれていないことを保証するだけで、複数のファイルが複数回含まれてコンパイルされていないことを保証することはできない.
これは実は私自身も理解していないので、例を挙げましょう.
私たちは書類を持っています:A,B,C
Bは(include)Aを含む;CはBを含む;AはCの中で2回Aを含む;上記の前処理がなければ同じファイルの中の再定義イベントが発生する.
しかし、BにはAが含まれ、CにはAが含まれているため、Aは2回コンパイルされ、定義文があれば2回定義され、グローバル変数が異なるファイルで再定義されるイベントが発生します.
例:
A.h
int i;
B.cpp
#include"A.h"
cout<C.cpp
#include"A.h"
#include"B.h"
cout<実際、B.cppは以下のように書くことができます.
int i;//#includeはコードをインポートすることです
cout<同理C.cppも以下のように書くことができる:(ここでは構想がより明確に2つの段階に分けられ、コンパイラが具体的にどのように操作するか分からない)
第一段階:B.h A.hを導入する
int i;
#include"A.h"
cout<cout<第2段階:B.hから導入するA.hを導入する
int i; 
int i;//明らかに再定義
cout<これが最終的なC.cppです
前処理モジュールを追加する場合:C.cppは:
int i;                    
cout<しかし、グローバル変数のデフォルト可視性はexternal(外部)であるため、同じエンジニアリングではA.h,B.h,C.hともにint iが実行される.このようにiは3回定義され,異なるファイルにおけるグローバル変数の再定義が現れた.
はい、これで私たちは依然として#includeのベールを解いて、間違いの所在も分かりました.
解決策は、A.hでグローバル変数を定義するのではなく、宣言することです(関数も.hで定義すべきではなく、宣言するだけです).改善後、Aは
A.h
extern int i;//externは、外部に表示され、使用可能な変数を宣言します.
注:externを追加しても、現代コンパイラにはファイルAが含まれていなければ使用できません.現代コンパイラはまず単一のファイルをコンパイルし、コンパイル時に各ファイル間が透明であるためです.
これで問題は解決しました.次は正しいコードを貼ります.
Head.h
#ifndef HEAD_H   //     ,      
#define HEAD_H 

struct Node{

int data;
};

extern Node Dt;   //       。
void setData(int dt);
#endif;

Head.cpp
#include"Head.h"
Node Dt;
void setData(int dt)
{
	Dt.data=dt;
}

Main.cpp
#include<iostream>
#include"Head.h"
using namespace std;

int main()
{

	int input=1000;
	setData(input);
	cout<<Dt.data;
	return 0;
}

出力結果:1000
最後に補足します.
extern Node Dt;
  
static Node Dt;
  static             ,        ,         。

まとめ編
(1)
#Includeの理解:含まれているファイルコードが現在のファイルにインポートされた場合(インライン関数に似ています)
A.hコードセグメントA
B.h#include“A.h”コードセグメントB
等価B.hコードセグメントAコードセグメントB
そのため、複数のファイルに同一のファイルIが含まれる場合があり、含まれるファイルIにグローバル変数の定義(グローバル変数が外部に見える)があると、
同じ変数は、異なるファイルで複数回定義されます.
上記の例のように、2つのファイルのみであっても、Aで1回定義すると、BのコードセグメントAが再コンパイルされ、また1回定義され、同様に再定義されます.
(2
)#ifndef識別子
#define識別子
//コードセグメント
    #endif;
このコードの役割は、1つのファイルに同じファイルが複数回含まれないことを保証することです(つまり、同じファイルコードが複数回インポートされないことです.これにより、同じファイルの再定義の問題が回避されます).
注意:このコードセグメントの識別子はファイル内でのみ有効であり、異なるファイルは互いに影響しません.例えば、Aで定義可能Head_HはBにHead_が定義されているわけではないH(現代のコンパイラは静的コンパイルであり,ファイル単位であるため,異なるファイル間コンパイルは透明である).
(3)グローバル変数の可視性:グローバル変数はデフォルトでexternal(外部)で表示されるため、できるだけグローバル変数を使用しないと名前の競合が発生しやすく、再定義エラーになります.グローバル変数を使用する必要がある場合は、ヘッダファイルで宣言し、あるcppファイルで定義し(1つのcppでのみ定義することを覚えておいてください)、他の必要な場所で使用する必要があります.プログラミングスタイルは、できるだけ1つのスタイルを維持する必要があります:.cppファイルには.hのみが含まれます..hに.c ppが含まれます..cppに.cppが含まれます..cppに.cppが含まれます.