咱們一般在完成一個程序時,每每習慣將程序寫爲多文件的,這樣就能實現程序的模塊化管理,以及分工開發合做。而一些全局變量,全局函數,結構體等就能使各模塊聯繫在一塊兒。模塊化
在日常你們寫代碼的過程當中,一般會使用文件包含來聯繫各代碼文件,固然初學者也可能會直接寫成多文件程序,沒有文件包含,這樣也能編譯、運行。函數
在這裏,寫了一些小段的測試代碼,來講明:包含.c文件,,直接多文件,包含.h文件三種方式的區別與應用。測試
1.包含.c文件spa
可能有人不習慣寫.h文件,而直接將代碼寫在.c文件中,其餘文件要調用函數則直接包含.c文件。正以下面一段代碼:code
//fun.c int h = 0; int cal(int a, int b) { return (2*a+3*b); } //main.c #include "stdio.h" #include "fun.c" //包含.c文件 int main(void) { h = 1; //直接使用fun.c中的變量 int i = 1,j = 2; int c = 0; c = cal(i, j); //直接調用fun.c中的函數 printf("%d\n", c); return 0; }
編譯:gcc main.c -o main 直接編譯經過,運行正確。對象
咱們再看一個測試程序:blog
//fun.c int h = 0; int cal(int a, int b) { return (2*a+3*b); } //fun1.c #include "fun.c" // 就這一句 //main.c #include "stdio.h" #include "fun.c" //包含.c文件 #include "fun1.c" //添加包含fun1.c int main(void) { h = 1; //直接使用fun.c中的變量 int i = 1,j = 2; int c = 0; c = cal(i, j); //直接調用fun.c中的函數 printf("%d\n", c); return 0; }
編譯:gcc main.c -o main 編譯報錯:h cal 發生了重定義。開發
總結:io
優勢:能夠直接編譯main.c文件,不用編譯fun.c,由於在main.c的編譯預處理階段,會將fun.c 的代碼直接拷貝過來。讀者可使用gcc -E main.c -o main.i 查看預處理後.i文件的代碼。發現main.i中將fun.c的代碼拷貝了過來。編譯
缺點:fun.c不能被多個文件包含,由於這樣就會產生變量和函數的多個拷貝,形成嚴重的重定義,編譯通不過。
這種方式可解決較簡單程序的文件包含問題,一般不推薦使用。
2.直接多文件
多文件程序中,全局變量能夠被外部引用,函數也默認是全局函數,能夠被外部使用。廢話少說,先上代碼:
//fun.c int global_var = 0; int cal(int a, int b) { return (2*a+3*b); } //main.c #include "stdio.h" extern int global_var; // 聲明外部變量 int main(void) { global_var =1; //外部變量賦值 int i = 1,j = 2; int c = 0; c = cal(i, j); //外部函數調用 printf("%d\n", c); return 0; }
Makefile書寫爲:
CFLAG=-Wall -O -g
main:main.o fun.o
gcc $(CFLAG) main.o fun.o -o main
main.o:main.c
gcc $(CFLAG) -c main.c -o main.o
fun.o:fun.c
gcc $(CFLAG) -c fun.c -o fun.o
clean:
rm main.o fun.o main
編譯經過,會出現警告信息,說cal爲隱含的外部函數。運行正確。結果還算理想,那麼咱們再來看一段:
1 int global_var = 0; 2 struct student 3 { 4 int age; 5 int num; 6 }; //定義結構體 7 struct student wang; //定義結構體變量 8 int cal(int a, int b) 9 { 10 return (2*a+3*b); 11 } 12 13 #include "stdio.h" 14 extern int global_var; 15 extern struct student wang; //聲明外部結構體變量 16 int main(void) 17 { 18 wang.age = 10; //報錯 19 student li; //想使用外部的定義的結構體來定義一個變量 報錯 20 global_var =1; 21 int i = 1,j = 2; 22 int c = 0; 23 c = cal(i, j); 24 printf("%d\n", c); 25 return 0; 26 }
一樣的編譯,main.c文件的編譯會出現錯誤,首先是想使用外部定義的結構體變量(已做聲明),報錯。而後,想用外部結構體定義一個變量,報錯。
總結:
優勢:不會出現重定義問題
缺點:一、若是要用到的外部變量多,或是在多處要使用外部變量,則要在每個調用的文件中使用extern聲明,比較麻煩。
二、若是是在fun.c中有結構體定義或是類的定義,在main.c中用使用結構體定義變量,或是使用類定義對象。編譯不能經過,不知該如何處理。
三、若是是定義在fun.c中的結構體變量,在main.c中要聲明外部變量,並使用,會出錯。故推知:結構體不能聲明爲外部變量,只能是包含的頭文件中定義的結構體。
解決上述問題的方式就是第三種,包含.h文件方式。
3.包含.h文件
爲每個模塊都編寫一個.c文件,一個.h文件。
.c源文件中放:函數的定義,全局變量的定義,全局結構體變量的定義。
.h頭文件中放:函數的聲明,全局變量的聲明(extern),全局結構體的定義,全局結構體變量的聲明。
調用文件(main.c)文件中:包含.h頭文件便可。不用聲明任何東西。
測試代碼以下:
//fun.c #include "fun.h" int global_var = 0; struct student wang; //結構體變量定義 int cal(int a, int b) { return (2*a+3*b); } //fun.h #ifndef _fun_h_ //條件編譯,解決文件重複包含所引發的重定義問題 #define _fun_h_ struct student { int age; int num; }; extern int global_var; extern struct student wang; #endif //fun1.c #include "fun1.h" //沒有實際代碼 //fun1.h #include "fun.h" //包含fun.h //main.c #include "stdio.h" #include "fun.h" #include "fun1.h" //extern int global_var; //extern struct student wang; int main(void) { wang.age = 10; //調用全局結構體變量 struct student li; //定義變量 global_var =1; //使用全局變量 int i = 1,j = 2; int c = 0; c = cal(i, j); printf("%d\n", c); return 0; }
總結:
優勢:解決了文件重複包含形成的重定義問題;函數,全局變量,結構體,結構體變量都能在外部調用;每一處調用變量的文件處不用重複的寫聲明語句,由於聲明語句直接寫在了.h頭文件中,直接包含頭文件便可。