以往我們都是將所有的代碼寫到一個源文件里面,對于小程序,代碼不過幾百行,這或許無可厚非,但當程序膨脹代碼到幾千行甚至上萬行后,就應該考慮將代碼分散到多個文件中,否則代碼的閱讀和維護將成為一件痛苦的事情。
本節我們來演示一下多文件編程。在下面的例子中,我們創建了兩個源文件 main.c 和 module.c:
- module.c 是整個程序的一個模塊,我們在其中定義了一個全局變量和一個函數;
- main.c 是程序的主模塊(主文件),它使用到了 module.c 中的變量和函數。
module.c 源碼:
/**
* 系統學習C語言 https://xiecoding.cn/c/
**/
#include <stdio.h>int m = 100;void func(){printf("Multiple file programming!\n");
}
main.c 源碼:
/**
* 系統學習C語言 https://xiecoding.cn/c/
**/
#include <stdio.h>extern void func();
extern int m;int n = 200;int main(){func();printf("m = %d, n = %d\n", m, n);return 0;
}
在 Visual Studio 中,將兩個源文件都添加到工程中,點擊“運行(Run)”按鈕就可以運行程序。
在 Linux GCC 中,可以使用下面的命令來編譯和運行程序:
$gcc main.c module.c
$./a.out
程序最終的運行結果為:
Multiple file programming!
m = 100, n = 200
m 和 n 是在所有函數之外定義的全局變量(Global Variable),它的作用域默認是整個程序,也就是所有的代碼文件,包括.c
和.h
文件。
如果你一直在編寫單個源文件的程序,那么請注意,全局變量的作用范圍不是從變量定義處到該文件結束,在其他文件中也有效。
這里需要重點理解的是?extern?關鍵字,它用來聲明一個變量或函數。
C語言extern關鍵字
我們知道,C語言代碼是由上到下依次執行的,不管是變量還是函數,原則上都要先定義再使用,否則就會報錯。但在實際開發中,經常會在函數或變量定義之前就使用它們,這個時候就需要提前聲明。
所謂聲明(Declaration),就是告訴編譯器我要使用這個變量或函數,你現在沒有找到它的定義不要緊,請不要報錯,稍后我會把定義補上。
例如,我們知道使用 printf()、puts()、scanf()、getchar() 等函數要引入 stdio.h 這個頭文件,很多初學者認為 stdio.h 中包含了函數定義(也就是函數體),只要有了頭文件程序就能運行。其實不然,頭文件中包含的都是函數聲明,而不是函數定義,函數定義都在系統庫中,只有頭文件沒有系統庫在鏈接時就會報錯,程序根本不能運行。
1) 函數的聲明
函數的定義有函數體,函數的聲明沒有函數體,編譯器很容易區分定義和聲明,所以對于函數聲明來說,有沒有 extern 都是一樣的。
總結起來,函數聲明有四種形式:
//不使用 extern
datatype function( datatype1 name1, datatype2 name2, ... );
datatype function( datatype1, datatype2, ... );
//使用 extern
extern datatype function( datatype1 name1, datatype2 name2, ... );
extern datatype function( datatype1, datatype2, ... );
?
2) 變量的聲明
變量和函數不同,編譯器只能根據 extern 來區分,有 extern 才是聲明,沒有 extern 就是定義。
變量的定義有兩種形式,你可以在定義的同時初始化,也可以不初始化:
datatype name = value;
datatype name;?
而變量的聲明只有一種形式,就是使用 extern 關鍵字:
extern datatype name;
另外,變量也可以在聲明的同時初始化,格式為:
extern datatype name = value;
這種似是而非的方式是不被推薦的,有的編譯器也會給出警告,我們不再深入討論,也建議各位讀者把定義和聲明分開,盡量不要這樣寫。
extern 是“外部”的意思,很多教材講到,extern 用來聲明一個外部(其他文件中)的變量或函數,也就是說,變量或函數的定義在其他文件中。
不過我認為這樣講不妥,因為除了定義在外部,定義在當前文件中也是正確的。例如,將 module.c 中的int m = 100;
移動到 main.c 中的任意位置都是可以的。所以我認為,extern 是用來聲明的,不管具體的定義是在當前文件內部還是外部,都是正確的。