C++中的definition & declaration的區別,涉及到extern關鍵字

來源:互聯網
上載者:User
有關這兩者的區別和聯絡,之前其實一直都非常的模糊,特別是extern關鍵字。這次讀C++ Primer,在第二章正好讀到,於是好好理解了一次,而且做了一些代碼測試。結論是這樣的:

1. definition只能用於變數,也就是定義一個變數,此時,變數的記憶體空間會被分配。諸如int i, int i = 10這樣的都是definition,因為i變數會被分配記憶體。

2. declaration可以用於變數或類型(比如聲明一個struct,但是不定義變數),如果用於變數,該變數不會被分配記憶體,而且前面必須加上 extern(表示這個變數的definition不是在這裡,而且在其他地方,所以是extern);如果是類型,那就沒什麼好說的了,本來類型就不需 要分配記憶體,只有變數才需要記憶體,如果在類型的聲明的前面加上extern,也是可以的,只不過這樣做沒有任何意義。注意:如果這樣寫:extern int i = 10; ,那麼,這也是definition,因為給i賦值了,extern就和沒加一樣。

3. 無論整個Program有多少個源檔案,代碼量有多大,一個變數只能被definition一次,但是declaration不限次數。

註:說到這裡,有關function方法的定義和聲明,也是類似的。一般來說,如果沒有寫出方法中的代碼,那麼,這就是declaration, 如果寫出了代碼,那麼就是對方法的definition了。所以,一般在標頭檔中寫一個方法的declaration,比如void print_sth();,然後在源檔案中寫出方法的實現即可。只不過在這裡,void print_sth();和extern void print_sth();效果是一樣的,加不加extern都一樣。

4. 綜合以上三點,如果我們在一個a.cc中定義了一個全域變數int i,那麼在b.cc中如果想使用這個i,那麼,必須聲明成extern int i才可以,如果也寫int i,那就是重複definition。說 到這裡,我在學習的時候就有一個疑問了:如果說,我們把int i這句代碼寫在一個名為common.h的標頭檔中,然後前面加上條件編譯,最後這個標頭檔被a.cc和b.cc都include,這樣a.cc和 b.cc中不就不需要extern int i這樣的代碼不就可以使用i了嗎?事實上,經過實驗,這樣的想法是極端錯誤的。

測試代碼可以這樣構建:common.h

Code: Select all
#ifndef _COMMON_H
#define _COMMON_H

int i = 10;

void print_sth();

#endif

a.cc這樣:

Code: Select all
#include <iostream>
#include "common.h"
using namespace std;

int main()
{
    cout << "i is: " << i << endl;
    print_sth();
    return 0;
}

b.cc這樣:

Code: Select all
#include <iostream>
#include "common.h"
using namespace std;

void print_sth()
{
    cout << "i in b.cc is: " << i << endl;
}

然後編譯: g++ -o test a.cc b.cc,出現錯誤:
/tmp/ccDYjlJE.o(.data+0x0): multiple definition of `i'
/tmp/ccNz3Dvy.o(.data+0x0): first defined here
collect2: ld returned 1 exit status

為什麼會出現這樣的錯誤呢?這裡面有一個概念沒有搞清楚:

A. 以為使用條件編譯可以讓 int i = 10; 這句代碼只執行一次。事實上,我們通過這個命令列:g++ -E a.cc b.cc >& output ,這個命令列是讓g++在做完預先處理之後就停止,然後將預先處理的結果列印到螢幕,然後我們開啟output檔案,在裡面,我們會發現int i = 10;這句代碼出現了兩次。所以,結論就是:條件編譯只在一個源檔案中生效,換句話說,通過使用條件編譯,我們可以保證在一個源檔案中,不會產生多餘的 include,但是,在多個源檔案中,條件編譯是無效的。注意:一個小知識點,g++的-E option不要和-o option連用,否則會導致輸出的預先處理後的代碼不完整。

所以,上面的代碼是錯誤的。根據上面的例子,我們又可以總結一些東西了,接著上面的第四點:

5. 在標頭檔中(.h檔案中),一般我們唯寫declaration,所以,在標頭檔中,我們一般做的是:定義類型(各種struct,typedef等), 定義函數(前面說過了,沒有代碼的函式宣告是declaration)。如果要在標頭檔中定義變數,那麼,必須保證以下兩點中的一點:

a)這個標頭檔只會被一個源檔案include

b)將這個變數定義變成聲明,也就是前面加上extern,然後在一個且只能有一個源檔案中對該變數做definition

如果不是這樣,那就必然出現multiple definition。任何源檔案想要引用其他源檔案中或標頭檔中定義的變數,必須要使用extern,表示該變數不是在當前源檔案中definition的。當然,前提條件是這個變數是全域變數(廢話 )。

所以,前面給出的那個測試例子,可以這樣修改就OK了。在common.h中:

Code: Select all
#ifndef _COMMON_H
#define _COMMON_H

void print_sth();

#endif

a.cc:

Code: Select all
#include <iostream>
#include "common.h"
using namespace std;

int i = 10;

int main()
{
    cout << "i is: " << i << endl;
    print_sth();
    return 0;
}

b.cc:

Code: Select all
#include <iostream>
#include "common.h"
using namespace std;

extern int i;

void print_sth()
{
    i = 20;
    cout << "i in b.cc is: " << i << endl;
}

這樣就OK了,程式編譯通過,輸出是:
i is: 10
i in b.cc is: 20

或者直接在common.h中,將 int i = 10; 改成extern int i;然後在a.cc中做definition:int i = 10; ,這樣也是可以的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.