多文件結構可以含有多個頭文件和源文件。前兩種結構均是多文件結構的特例。嚴格講,結構化C程序設計應該使用多文件結構。如果只使用一個文件,即使它的函數設計很符合結構化設計,但也給查錯和維護帶來不便。試想一下,幾千行的程序都在一個文件中,能算是好的設計方法嗎?
要進行模塊化和結構化設計,必須掌握多文件編程的知識。這主要涉及如何使用函數原型、頭文件和工程文件等方面的知識,而且與所使用的集成環境也有關係。
1.使用多個文件進行模塊化設計
假設要求編製兩個函數,分別計算兩個數的最大值和平均值,然後使用主函數調用它們。將這兩個函數分別設計在max.c和mean.c文件中,主函數在find.c文件中。這樣,每個文件是一個單獨模塊,功能單一,查錯容易。兩個函數模塊互不牽扯。然後將任務分派給3個人去完成。
但如何將這些文件組成一個整體呢?一般把這個整體稱為工程,目前VC又稱其為項目。使它們協調工作的方法不止一種,建議使用頭文件和原型聲明,充分利用編輯器的嚴格檢查來組織實施。下面編製的程序就是考慮到這些實施方法而設計的。雖然程序很小,但已經能說明問題的實質。3個人編寫的程序內容如下。
// 第1 個人編寫的求最大值函數文件:max.c double max (double m1 , double m2 ) { if (m1 > m2 ) return m1 ; else return m2 ; }
這個文件自成系統,所以最簡單。其實,工程應用時,許多文件就是以函數為單位的。這個模塊的正確性可以自己驗證,驗證正確無誤後,就可提交使用。
// 第2 個人的求平均值函數文件:mean.c // 求平均值函數mean #include 〞mean.h 〞 // 包含自定義的頭文件 double mean (double m1 , double m2 ) { return ((m2+m1 )*DIV2 ); } // 求平均值函數的頭文件mean.h const double DIV2 = 0.5 ;
為了說明使用const定義常數問題,特讓mean.c文件中的mean函數使用常係數DIV2。常數設計在它的頭文件中,這裡把它命名為mean.h。調試成功後,提供這兩個文件。
// 第3 個人的主函數文件:find.c // 主函數main #include\"find.h\" // 包含自定義的頭文件 void main ( ) { double a ,b ; printf (\"Input a and b :n\" ); scanf (\"%lf%lf\" ,&a ,&b ); printf ( \"max=%lfn\" ,max ( a ,b )); printf ( \"mean=%lfn\" ,mean ( a ,b )); } // 主函數使用的頭文件find.h #include <stdio.h> double max (double ,double ); double mean (double , double );
總共有3個C程序源文件和2個頭文件,共5個文件。
2.頭文件和函數原型的作用
一般是將所有的函數原型和外部變量的聲明,以及常數的定義都放在一個頭文件裡,需要這些頭文件的源文件,就可以將它們包含進去。雖然求最大值文件沒有頭文件,但也要在主程序的頭文件find.h中聲明它的函數原型,以保證find.c的main函數能正確分辨它。
常數DIV2定義在mean.h中,在mean.c中使用如下語句包含它。
#include \"mean.h\"
因為只有mean函數使用這個常數,又為了說明自帶頭文件的方法,所以單獨做了這個頭文件。主函數使用的函數庫的頭文件「stdio.h」,也有意放在頭文件find.h中。一般來講,如果是大家共有的常數和變量,可以協商放在一個公共的頭文件中。這裡之所以做成2個頭文件,主要是演示頭文件的定義和使用方法。
3.組合為一個工程項目
假設構造的項目為find,使用VC構成find項目的步驟如下。
(1)利用VC構造一個空項目find。如圖23-4所示,這裡面沒有源文件和頭文件。
圖23-4 利用VC構造一個空項目find示意圖
(2)將用戶的5個文件拷貝到find目錄中,然後將它們添加到項目中。可以使用Projecct菜單Add To Project選項的Files命令,也可以如圖23-4所示,單擊鼠標右鍵,選中Add Files Folder命令,彈出如圖23-5所示的Insert Files into Project對話框,找到find文件夾,插入所需文件。將C的源文件插入Source Files之下,頭文件插入Header Files之下。圖23-6給出結果示意圖。
圖23-5 Insert Files into Project對話框
(3)如圖23-6所示,雙擊左邊的文件圖標,右邊窗口顯示相應的源文件。
(4)可以分別編譯項目中的各個C程序文件,雙擊左邊的文件圖標,讓它們出現在右邊,就可以編譯該文件。也可以一次對所有文件編譯並產生執行文件。如果要一次編譯,可以選擇任意一個C文件,通過產生exe文件的選項(例如Build find.exe菜單項)一次編譯並產生exe文件。菜單和相應的工具按鈕如圖23-6所示。
圖23-6 插入所需文件示意圖
(5)如圖23-6所示,可以使用菜單命令或工具按鈕執行程序編譯產生find.exe文件(exe文件與項目同名),下面是運行示例。
Input a and b : 235.678 4567.89 max=4567.890000 mean=2401.784000
4.#define和const的異同
其實,在頭文件裡使用#define和const的作用並不完全等效。如果只是文件本身使用這個頭文件,則兩者等效。正如上面的文件mean.c一樣,在mean.h中,下面兩種方式均可。
const double DIV2 = 0.5 ; #define DIV2 0.5
如果只設計一個頭文件find.h,就不能簡單地將mean.h中的語句移到find.h中。其實,使用const的格式是定義一個內容不會改變的常數變量,所以它遵循變量的使用原則。即在頭文件裡聲明一個外部const變量,在文件裡賦初值。修改的find.h和mean.c文件內容如下。
// 取代mean.h 的find.h 文件 #include <stdio.h> extern const double DIV2 ; // 在頭文件中聲明為外部常量 double max (double ,double ); double mean (double , double ); //mean.c 文件 #include \"find.h\" const double DIV2=0.5 ; // 在使用的文件中定義這個常量 double mean (double m1 , double m2 ) { return (m2+m1 )*DIV2 ;}
5.使用條件編譯編寫頭文件
上一節修改的程序,如果find.c兩次包含頭文件find.h(頭文件過多時,會出現這種情況),在編譯這個文件時,就會對頭文件處理兩次,這種重複包含有時會導致編譯程序不能正常完成。為了避免這種情況,可以使用宏定義配合條件編譯。假設宏名字為「_H_C6_H」,例如:
// 取代mean.h 的find.h 文件 #ifndef _H_C6_H // 如果沒有定義c6.h #define _H_C6_H // 下面定義c6.h #include <stdio.h> extern const double DIV2 ; // 在頭文件中聲明為外部常量 double max (double ,double ); double mean (double , double ); #endif // 定義結束
至於這個宏的名字「_H_C6_H」,則是隨意選擇的名字。預處理程序處理完文件開始部分,名字_H_C6_H就有了定義。如果在find.c的預處理中再次遇見到包含find.h的語句,由於_H_C6_H已經有了定義,所以#if至#endif之間的東西都被丟掉。源程序包含的是頭文件,頭文件裡才使用宏,所以這個宏的名字與頭文件的名字無關。之所以使用「_H_C6_H」的怪異方式,是避免程序中定義重名的可能性。讓字符串中包含字符H,則清晰地表示這是為了處理頭文件。
也可以使用另外一種等效(推薦使用)形式。
#if !defined (_H_C6_H ) #define _H_C6_H …….// 這裡是原來頭文件的內容 #endif
6.使用文件包含的方法
雖然也可以使用將文件包含的方法,但沒有上一種結構清晰。建議只作瞭解,如果碰到這種使用方法,能知道其組成原理即可。
因為在一個工作目錄內,VC的項目不能同名,所以為它再建一個名為find1的空項目,將5個文件拷貝到find1目錄,然後裝入find.c並將它修改為如下的程序。
#include \"find.h\" #include \"max.c\" #include \"mean.c\" void main ( ) { double a ,b ; printf (\"Input a and b :n\" ); scanf (\"%lf%lf\" ,&a ,&b ); printf ( \"max =%lfn\" ,max ( a ,b )); printf ( \"mean=%lfn\" ,mean ( a ,b )); }
編譯運行find1.exe,結果正確。這時注意一下VC的窗口,如圖23-7所示,發現它自動將需要的文件都裝入External Dependencies的下面。雙擊這些文件,顯示在右邊的窗口中。
圖23-7 使用文件包含的VC窗口示意圖
7.一般的多文件模式
對一般比較大的程序設計而言,常常分成幾個源文件,每個源文件有自己的頭文件,然後組成工程文件。作為一個程序員,必須熟悉這種結構並正確運用它。