全域性變數與模組化

2021-06-02 10:29:19 字數 3082 閱讀 3599

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

這句話在上面的例子中已經有體現,即某模組提供給其它模組呼叫的外部函式和全域性變數需在.h 中檔案中冠以extern 關鍵字宣告,下面重點說一下全域性變數的使用。使用模組化程式設計的乙個難點(相對於新手)就是全域性變數的設定,初學者往往很難想通模組與模組公用的變數是如何實現的,常規的做法就是本句提到的,在.h檔案中外部資料冠以extern關鍵字宣告。比如上例的變數value就是乙個全域性變數,若是某個模組也使用這個變數,則和使用外部函式一樣,只需在使用的模組.c檔案中包含#include「lcd_device.h」即可。

另一種處理模組間全域性變數的方法來自於嵌入式作業系統ucos-ii,這個作業系統處理全域性變數的方法比較特殊,也比較難以理解,但學會之後妙用無窮,這個方法只需用在標頭檔案中定義一次。方法為:

在定義所有全域性變數(ucos-ii將所有全域性變數定義在乙個.h檔案內)的.h標頭檔案中:

#ifdef ***_globals

#define ***_ext

#else

#define ***_ext extern

#endif

.h 檔案中每個全域性變數都加上了***_ext的字首。*** 代表模組的名字。

該模組的.c檔案中有以下定義:

#define ***_globals

#include "includes.h"

當編譯器處理.c檔案時,它強制***_ext(在相應.h檔案中可以找到)為空,(因為***_globals已經定義)。所以編譯器給每個全域性變數分配記憶體空間,而當編譯器處理其他.c 檔案時,***_global沒有定義,***_ext 被定義為extern,這樣使用者就可以呼叫外部全域性變數。為了說明這個概念,可以參見uc/os_ii.h,其中包括以下定義:

#ifdef os_globals

#define os_ext

#else

#define os_ext extern

#endif

os_ext int32u osidlectr;

os_ext int32u osidlectrrun;

os_ext int32u osidlectrmax;

同時,ucos_ii.h 有中以下定義:

#define os_globals

#include 「includes.h」

當編譯器處理ucos_ii.c 時,它使得標頭檔案變成如下所示,因為os_ext 被設定為空。

int32u osidlectr;

int32u osidlectrrun;

int32u osidlectrmax;

這樣編譯器就會將這些全域性變數分配在記憶體中。當編譯器處理其他.c 檔案時,標頭檔案變成了如下的樣子,因為os_global沒有定義,所以os_ext 被定義為extern。

extern int32u osidlectr;

extern int32u osidlectrrun;

extern int32u osidlectrmax;

在這種情況下,不產生記憶體分配,而任何 .c檔案都可以使用這些變數。這樣的就只需在 .h檔案中定義一次就可以了。

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

這句話主要講述了關鍵字static的作用。static是乙個相當重要的關鍵字,他能對函式和變數做一些約束,而且可以傳遞一些資訊。比如上例在lcd驅動模組.c檔案中定義的延時函式static void delay (uint us),這個函式冠以static修飾,一方面是限定了函式的作用範圍只是在本模組中起作用,另一方面也給人傳達這樣的資訊:該函式不會被其他模組呼叫。下面詳細說一下這個關鍵字的作用,在c 語言中,關鍵字static 有三個明顯的作用:

1.在函式體,乙個被宣告為靜態的變數在這一函式被呼叫過程中維持其值不變。

2.在模組內(但在函式體外),乙個被宣告為靜態的變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問。它是乙個本地的全域性變

量。3.在模組內,乙個被宣告為靜態的函式只可被這一模組內的其它函式呼叫。那就是,這個函式被限制在宣告它的模組的本地範圍內使用。

前兩個都比較容易理解,最後乙個作用就是剛剛舉例中提到的延時函式(static void delay (uint us)),本地化函式是有相當好的作用的。

(4) 永遠不要在.h 檔案中定義變數!

呵呵,似乎有點危言聳聽的感覺,但我想也不會有多少人會在.h檔案中定義變數的。

比較一下**:

**一:

/*module1.h*/

int a = 5; /* 在模組1 的.h 檔案中定義int a */

/*module1 .c*/

#include "module1.h" /* 在模組1 中包含模組1 的.h 檔案 */

/*module2 .c*/

#include "module1.h" /* 在模組2 中包含模組1 的.h 檔案 */

/*module3 .c*/

#include "module1.h" /* 在模組3 中包含模組1 的.h 檔案 */

**二:

/*module1.h*/

extern int a; /* 在模組1 的.h 檔案中宣告int a */

/*module1 .c*/

#include "module1.h" /* 在模組1 中包含模組1 的.h 檔案 */

int a = 5; /* 在模組1 的.c 檔案中定義int a */

/*module2 .c*/

#include "module1.h" /* 在模組2 中包含模組1 的.h 檔案 */

/*module3 .c*/

#include "module1.h" /* 在模組3 中包含模組1 的.h 檔案 */

這樣如果模組1、2、3 操作a 的話,對應的是同一片記憶體單元。

注:乙個嵌入式系統通常包括兩類(注意是兩類,不是兩個)模組:

(1)硬體驅動模組,一種特定硬體對應乙個模組;

(2)軟體功能模組,其模組的劃分應滿足低偶合、高內聚的要求。

下面以keil c 編譯器為例,講一下模組化程式設計的步驟。

下面這個程式分為三層,共7個模組,共同為主程式服務(它們之間也會相互呼叫)。

模組化程式設計中全域性變數跨檔案使用

想定義個全域性變數,其他檔案都可以使用,但是如果定義在.h檔案中,就容易出現重複定義的問題 在gcc中如果未初始化,不會出現重複定義的問題 具體該怎麼做呢,如下 eg 乙個工程中有五個檔案,main.c first.c first.h second.c second.h 想在first.c 中定義個...

Nodejs fs模組 全域性變數

fs模組提供了用於與檔案進行互動相關方法 const fs require fs 寫入資料 fs.writefile 檔案,資料,err 讀取檔案中資料 fs.readfile 檔案 utf8 err,data 檢查檔案是否存在 返回 true false fs.existssync path 獲取...

C 靜態全域性變數與普通全域性變數

一 儲存區域 1 棧 由編譯器在需要的時候分配,在不需要的時候自動清除的變數的儲存區域。通常儲存區域性變數,函式引數。2 堆 由new分配的記憶體塊,需要手動釋放。如果程式設計師沒有手動釋放,在程式執行結束後,作業系統自動 3 自由儲存區 由malloc等分配的記憶體塊,與堆相似,用free來釋放。...