iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0
Software Development

建構屬於自己的C/C++專案 : 我的30天CMake學習之旅系列 第 7

[Day 7] 定義與宣告—多檔案共用變數

  • 分享至 

  • xImage
  •  

當我們將專案中的程式碼依照各自的的功能分開成不同的檔案時,常常會遇到在不同的原始碼間共用變數的需求,這個時候C/C++提供了一個方便的關鍵字:extern,但是剛開始其實不太懂這個關鍵字背後的原理是什麼,只知道如果要共用一個變數就加 extern,直到後來出現了bug後,才了解到宣告與定義之間的區別。

接下來會介紹宣告與定義的原理,並有一個多檔案共用變數的範例。

宣告與定義

C++的編譯方式為個別編譯(separate compilation),能讓程式分為數個檔案,並且每一個都能獨立編譯成 xxx.o檔,最後才練接(linking)成最後的執行檔。
https://ithelp.ithome.com.tw/upload/images/20230920/20162026Uhyf0D14OP.png

當一個程式被分為多個檔案,我們就需要一種在檔案間共用程式碼的方法,就是先宣告(declarations),再去其他地方尋找定義(definitions),通常在.h檔中宣告,在.cpp中定義。

  • 宣告讓程式得知變數的名稱以及其型別
  • 定義則創建關聯的那個實體(以進行初始化或其他操作),並配置(allocates)記憶體

通常宣告一個物件的同時也會被定義,除非符合以下規則

規則

  • 宣告即定義,除非
    1. 宣告了某函式,但沒有寫出其實現
    2. 宣告式包含前修飾符號extern(外部聲明),且不帶初值亦無實現
    3. 在class定義區間內宣告靜態成員變數
    4. 單純宣告class名稱
    5. 單純為typedef宣告
  • 定義即宣告,除非
    1. 定義靜態成員變數
    2. 定義non-inline函式

接下來多檔案共用變數的範例就是其中的第二點例外, 先使用修飾符號extern宣告後,在鍊結階段去其他檔案尋找定義。

extern

當需要多檔案共用變數時,則必須使用 extern ,這個關鍵字用於將變數聲明為全域變數,以便可以從程序中的任何位置訪問在同一文件或另一個文件的另一個作用域中聲明的變數。當連結器在全域變數宣告之前看到 extern 時,它會在其他文件中尋找定義。

範例

$ git clone https://github.com/m11112089/2023_iT_CMake.git
$ cd ~/2023_iT_CMake/Day7

在count.h第6行使用extern修飾"宣告", 在count.cpp第2行才會"定義"

include/count.h

extern int counter;

src/count.cpp

int counter = 0;

$ cmake .
$ make
$ ./main

kai@esoc:~/2023_iT_CMake/Day7/build$ ./main 
0
0
1
1
2
2

可以看到 counter 這個變數在 main.cpp 和 count.cpp 中共用同一個數值。


但是使用extern全域變數必須小心一點,可以在不同作用域中使用相同的變數名稱定義出不同的物件,可以理解為區域變數和全域變數之間的區別。

將main.cpp的第6行解除註解之後,結果則完全不同。
src/main.cpp

    int counter = 10;//並不會修改到count.cpp中定義的counter
kai@esoc:~/2023_iT_CMake/Day7/build$ ./main 
0
10
1
10
1
11

原本預期的output是10, 10, 11, 11, 12, 12但是因為第6行定義了在main.cpp的main function作用域中新的的counter,因此結果會是0, 10, 1, 10, 1, 11

Reference

C 語言的宣告、定義、儲存類型 (storage class) 與連結性 (linkage)
書籍: C++ Primer 2.2.2變數宣告與定義
書籍: 大規模c++程式設計 1.1.1宣告與定義
https://learn.microsoft.com/zh-tw/cpp/cpp/extern-cpp?view=msvc-170


上一篇
[Day 6] 編譯多檔案與檔案架構
下一篇
[Day 8] Define Guard
系列文
建構屬於自己的C/C++專案 : 我的30天CMake學習之旅29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言