變數宣告和變數定義

  • 變數定義:用於為變數分配儲存空間,還可為變數指定初始值。程式中,變數有且僅有一個定義。

  • 變數宣告:用於向程式表明變數的型別和名字。

  • 定義也是宣告,extern宣告不是定義

  • 定義也是宣告:當定義變數時我們聲明瞭它的型別和名字。
  • extern宣告不是定義:通過使用extern關鍵字宣告變數名而不定義它。
    [注意]
    變數在使用前就要被定義或者宣告。
    在一個程式中,變數只能定義一次,卻可以宣告多次。
    定義分配儲存空間,而宣告不會。
   C++程式通常由許多檔案組成,為了讓多個檔案訪問相同的變數,C++區分了宣告和定義。

    變數的定義(definition)用於為變數分配儲存空間,還可以為變數指定初始值。在程式中,變數有且僅有一個定義。

    宣告(declaration)用於向程式表明變數的型別和名字。定義也是宣告:當定義變數的時候我們聲明瞭它的型別和名字。可以通過使用extern宣告變數名而不定義它。不定義變數的宣告包括物件名、物件型別和物件型別前的關鍵字extern。

    extern宣告不是定義,也不分配儲存空間。事實上它只是說明變數定義在程式的其他地方。程式中變數可以宣告多次,但只能定義一次。

    只有當宣告也是定義時,宣告才可以有初始化式,因為只有定義才分配儲存空間。初始化式必須要有儲存空間來進行初始化。如果宣告有初始化式,那麼它可被當作是定義,即使宣告標記為extern。

    任何在多檔案中使用的變數都需要有與定義分離的宣告。在這種情況下,一個檔案含有變數的定義,使用該變數的其他檔案則包含該變數的宣告(而不是定義)。

如何清晰的區分變數宣告和定義

extern通知編譯器變數在其他地方被定義

1.extern告訴編譯器變數在其他地方定義了。

例如:

extern int i;       //宣告,不是定義
int i;              //宣告,也是定義,未初始化

帶有初始化式的宣告必定式定義

2.如果宣告有初始化式,就被當作定義,即使前面加了extern。
只有當extern宣告位於函式外部時,才可以被初始化。

例如:

extern double pi=3.141592654;  //定義

函式的宣告和定義

3.函式的宣告和定義區別比較簡單,帶有{ }的就是定義,否則就是宣告。

例如:

extern double max(double d1,double d2);  //宣告

除非有extern關鍵字,否則都是變數的定義。

4.除非有extern關鍵字,否則都是變數的定義。

例如:

extern int i; //宣告
int i; //定義          

程式模組化設計風格

概要

1. 不要把變數定義放入.h檔案,這樣容易導致重複定義錯誤。

永遠不要在.h檔案中定義變數。定義變數和宣告變數的區別在於定義會產生記憶體分配的操作,是彙編階段的概念;而宣告則只是告訴包含該宣告的模組在連線階段從其它模組尋找外部函式和變數

2. 儘量使用static關鍵字把變數定義限制於該原始檔作用域,除非變數被設計成全域性的。

3. 可以在標頭檔案中宣告一個變數,在用的時候包含這個標頭檔案就聲明瞭這個變數。

模組化要點

 (1) 模組即是一個.c檔案和一個.h檔案的結合,標頭檔案(.h)中是對於該模組介面的宣告;

(2) 某模組提供給其它模組呼叫的外部函式及資料需在.h中檔案中冠以extern關鍵字宣告;

(3) 模組內的函式和全域性變數需在.c檔案開頭冠以static關鍵字宣告;

(4) 永遠不要在.h檔案中定義變數!定義變數和宣告變數的區別在於定義會產生記憶體分配的操作,是彙編階段的概念;而宣告則只是告訴包含該宣告的模組在連線階段從其它模組尋找外部函式和變數。

一般情況下標頭檔案中只放變數的宣告,因為標頭檔案要被其他檔案包含(即#include),如果把定義放到標頭檔案的話,就不能避免多次定義變數,C++不允許多次定義變數,一個程式中對指定變數的定義只有一次,宣告可以無數次。

不過有三個例外,一下三中實體的定義也可放到標頭檔案中。

1.值在編譯時就已知的const 變數的定義可以放到標頭檔案中
如:const int num(10);
2.類的定義可以放到標頭檔案中
3.inline 函式

這三個實體可以定義在多個原始檔中,只要在每個原始檔中的定義相同。

示例程式

#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;
}

這裡寫圖片描述

在這個程式中,我們再函式外部定義了一個變數
注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;
}

在這個程式中,

int a;是個定義,但是未初始化,列印他的值式不確定的,因此編譯時會報未初始化的異常。

int b = 10; 是個定義,並且被正確初始化,列印b的值沒有問題。

這裡寫圖片描述

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

這裡寫圖片描述

對於d再明顯不過了,前面我們提到過如果宣告有初始化式,就被當作定義,即使前面加了extern。但是隻有當extern宣告位於函式外部時,才可以被初始化。

現在這個定義很明顯被gcc編譯器認為是錯誤的。

這裡寫圖片描述