轉自:https://blog.csdn.net/gatieme/article/details/50640424
C++程序通常由許多文件組成,為了讓多個文件訪問相同的變量,C++區分了聲明和定義。
變量的定義(definition)用於為變量分配存儲空間,還可以為變量指定初始值。在程序中,變量有且僅有一個定義。
聲明(declaration)用於向程序表明變量的類型和名字。定義也是聲明:當定義變量的時候我們聲明了它的類型和名字。可以通過使用extern聲明變量名而不定義它。不定義變量的聲明包括對象名、對象類型和對象類型前的關鍵字extern。
extern聲明不是定義,也不分配存儲空間。事實上它只是說明變量定義在程序的其他地方。程序中變量可以聲明多次,但只能定義一次。
只有當聲明也是定義時,聲明才可以有初始化式,因為只有定義才分配存儲空間。初始化式必須要有存儲空間來進行初始化。如果聲明有初始化式,那么它可被當作是定義,即使聲明標記為extern。
任何在多文件中使用的變量都需要有與定義分離的聲明。在這種情況下,一個文件含有變量的定義,使用該變量的其他文件則包含該變量的聲明(而不是定義)。
如何清晰的區分變量聲明和定義
extern通知編譯器變量在其他地方被定義
1.extern告訴編譯器變量在其他地方定義了。
例如:
extern int i; //聲明,不是定義 int i; //聲明,也是定義,未初始化
- 1
- 2
帶有初始化式的聲明必定式定義
2.如果聲明有初始化式,就被當作定義,即使前面加了extern。
只有當extern聲明位於函數外部時,才可以被初始化。
例如:
extern double pi=3.141592654; //定義
- 1
函數的聲明和定義
3.函數的聲明和定義區別比較簡單,帶有{ }的就是定義,否則就是聲明。
例如:
extern double max(double d1,double d2); //聲明
- 1
除非有extern關鍵字,否則都是變量的定義。
4.除非有extern關鍵字,否則都是變量的定義。
例如:
extern int i; //聲明 int i; //定義
- 1
- 2
程序模塊化設計風格
概要
1. 不要把變量定義放入.h文件,這樣容易導致重復定義錯誤。
永遠不要在.h文件中定義變量。定義變量和聲明變量的區別在於定義會產生內存分配的操作,是匯編階段的概念;而聲明則只是告訴包含該聲明的模塊在連接階段從其它模塊尋找外部函數和變量
2. 盡量使用static關鍵字把變量定義限制於該源文件作用域,除非變量被設計成全局的。
3. 可以在頭文件中聲明一個變量,在用的時候包含這個頭文件就聲明了這個變量。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
模塊化要點
(1) 模塊即是一個.c文件和一個.h文件的結合,頭文件(.h)中是對於該模塊接口的聲明;
(2) 某模塊提供給其它模塊調用的外部函數及數據需在.h中文件中冠以extern關鍵字聲明;
(3) 模塊內的函數和全局變量需在.c文件開頭冠以static關鍵字聲明;
(4) 永遠不要在.h文件中定義變量!定義變量和聲明變量的區別在於定義會產生內存分配的操作,是匯編階段的概念;而聲明則只是告訴包含該聲明的模塊在連接階段從其它模塊尋找外部函數和變量。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
一般情況下頭文件中只放變量的聲明,因為頭文件要被其他文件包含(即#include),如果把定義放到頭文件的話,就不能避免多次定義變量,C++不允許多次定義變量,一個程序中對指定變量的定義只有一次,聲明可以無數次。
不過有三個例外,一下三中實體的定義也可放到頭文件中。
1.值在編譯時就已知的const 變量的定義可以放到頭文件中
如:const int num(10);
2.類的定義可以放到頭文件中
3.inline 函數
- 1
- 2
- 3
- 4
這三個實體可以定義在多個源文件中,只要在每個源文件中的定義相同。
示例程序
#include <stdio.h> #include <stdlib.h> // 是定義,定義了A為整型的外部變量 // C中定義的變量默認就是extern的, // 因此一般來說int a = 10 <==> extern int a = 10; /*extern */int a = 10; //如果聲明有初始化式,就被當作定義,即使前面加了extern。 //只有當extern聲明位於函數外部時,才可以被初始化。 int main(void) { extern int a; // 聲明一個外部extern的int型變量a // 這是個聲明而不是定義,聲明A是一個已經定義了的外部變量 // 注意:聲明外部變量時可以把變量類型去掉如:extern a; printf("a = %d\n", a); return EXIT_SUCCESS; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22

在這個程序中,我們再函數外部定義了一個變量
注extern int a = 10;只有當extern聲明位於函數外部時,才可以被初始化。
我們后面還會提到這個問題
#include <stdio.h> #include <stdlib.h> int main(void) { int a; // 定義一個變量, 不初始化 int b = 10; // 定義一個變量, 同時進行初始化 extern int c; // 聲明一個外部extern的int型變量a printf("a = %d\n", a); printf("b = %d\n", b); printf("c = %d\n", c); extern int d = 10; return EXIT_SUCCESS; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
在這個程序中,
int a;是個定義,但是未初始化,打印他的值式不確定的,因此編譯時會報未初始化的異常。
int b = 10; 是個定義,並且被正確初始化,打印b的值沒有問題。

但是是個聲明,如果要對c進行讀寫操作,而我們並沒有對c進行定義,因此語法檢查沒有問題,但是在鏈接時,連接器會找不到c的地址。

對於d再明顯不過了,前面我們提到過如果聲明有初始化式,就被當作定義,即使前面加了extern。但是只有當extern聲明位於函數外部時,才可以被初始化。
現在這個定義很明顯被gcc編譯器認為是錯誤的。

