1、 只有一個文件的狀況程序員
先來看一下比較簡單的情形,也就是隻有一個文件的時候,一個程序是什麼樣子的。函數
//main.cspa
#include <stdio.h>編譯器
int main(int argc, char** args)源碼
{io
printf("Hello\n") ;編譯
return 0 ;變量
}擴展
這個時候程序一目瞭然,咱們很容易就能夠看出它說了什麼。語法
2、 多個源代碼文件的狀況
可是,隨着咱們要編寫的程序的規模不斷擴大,咱們不得不把一個源代碼文件拆分開,把具備必定功能的某些方法放到其它單獨的源碼文件中。好比像下面這樣:
//main.c
#include <stdio.h>
int main(int argc, char** args)
{
sayhello() ;
return 0 ;
}
//sayhello.c
#include <stdio.h>
int sayhello()
{
printf("Hello\n") ;
}
把功能放在了sayhello.c文件中,而main.c只放主函數的代碼。這樣看起來更加的清晰明快。雖然形式上分紅多個文件,但編譯器在編譯的時候會自動把它們連在一塊兒,也就是說它們仍是至關於在一個大文件中寫代碼。可是若是咱們啓圖分別編譯這兩個文件,而後再連接成一個可執行程序的時候,就會發生錯誤。緣由在於main.c中使用了一個函數sayhello,這時編譯器並不知道sayhello是什麼,由於相對於main.c來講,它並不存在sayhello的定義和實現。因此,咱們必需要在main.c中加入sayhello的聲明(只要聲明就夠了,沒必要再實現一次)。方法是加一句「int sayhello() ;」但問題是,當咱們的工程愈來愈大的時候,咱們總不能引用一個函數就寫一下它的都聲明吧?
3、 引入頭文件
這時最好的解決辦法就是引用頭文件。就是編寫一個與sayhello.c同名的文件sayhello.h,用於定義常量、結構,聲明函數等。具體的作法以下:
//main.c
#include <stdio.h>
#include "sayhello.h"
int main(int argc, char** args)
{
sayhello() ;
return 0 ;
}
//sayhello.c
#include <stdio.h>
#include "sayhello.h"
int sayhello()
{
printf("Hello\n") ;
}
//sayhello.h
int sayhello() ;
4、 說說include宏
對於頭文件,咱們應僅把它看做是一個文本文件,它跟程序的代碼文件(即擴展名爲.c的文件)並不同。編譯器在編譯的過程當中,只會處理代碼文件,而不會去管其它的頭文件。只有當咱們在頭文件中使用#include的時候,相應的頭文件纔會被包含進來。編譯器只是在編譯前把#include所在的位置換成了相應頭文件中的內容罷了。
使用<>括起來的是系統的默認庫文件,也就是說不用我們本身去找這個文件所在的位置,只寫一個名字,編譯器就自動找到庫目錄中的文件了。而」」括起來的正好相反,大可能是咱們本身編的代碼或引用的非標準C的庫文件,它要求給出文件所在的絕對地址或相對地址。好比說,若是你的庫目錄設成/usr/share/include,那麼下面的寫法是等價的:
#include <stdio.h> == #include 「/usr/share/include/stdio.h」
5、 談談頭文件具體的使用
道理都懂了,那麼本身寫程序時,個人頭文件到底應該怎麼寫呢?其實,頭文件的寫法很隨意,不少人都有本身的使用習慣。可是我本身的見解是,儘可能模仿標準C的庫。如今就來研究一下吧。
好比咱們平時使用printf時,咱們都要包括一個頭文件,即stdio.h。它的特色是我在哪一個代碼文件用到了這個庫中的函數,我就在哪一個代碼文件中包括它的頭文件;包含它後,個人代碼中不該該引入錯誤,引用的庫函數不該該由於代碼文件中多引用了或少引用了一些其它的頭文件而出錯。
爲了達到這個目標,個人作法是:每寫一個代碼文件,就寫一個對應的頭文件;把全部的聲明、定義、結構體、常量、宏放在頭文件中,而代碼實現絕對不放在頭文件中;對頭文件的抱含也放到頭文件中,代碼文件中不含include宏。
下面看一些反例:
反例1:
//types.h
typedef int status ;
//sayhello.h
status sayhello() ;
//sayhello.c
#include <stdio.h>
#include "types.h"
#include "sayhello.h"
status sayhello()
{
printf("Hello!\n") ;
return 0 ;
}
//main.c
#include "sayhello.h"
int main(int argc, char** argv)
{
sayhello() ;
return 0 ;
}
在sayhello的定義中,出現了一個自定義類型status,它的聲明包括在types.h文件中。放對它的引用放在了sayhello.c中,這樣單獨編譯sayhello.c沒有任何問題。但是當編譯到main.c的時候,就出現問題了,編譯報錯:找不到status的聲明。這是由於在main.c中只抱括了sayhello.h,而它的聲明又須要types.h。因此,它出現了因爲少引用types.h而發生的錯誤。因此,我強調把全部的include都放到頭文件中去。若是這樣寫則不會出問題。
//sayhello.h
#include <stdio.h>
#include "types.h"
status sayhello() ;
//sayhello.c
#include "sayhello.h"
status sayhello()
{
printf("Hello!\n") ;
return 0 ;
}
//main.c
#include "sayhello.h"
int main(int argc, char** argv)
{
sayhello() ;
return 0 ;
}
這樣就符合了前面提到的原則。不過,我還能夠作以下的改動:
//main.c
#include "sayhello.h"
#include "types.h"
int main(int argc, char** argv)
{
status s = sayhello() ;
return 0 ;
}
在主程序中聲明瞭status類型的變量s。根據上面的原則,哪裏引用了它,哪裏就包括它的頭文件,因此咱們包括了types.h頭文件。有人會說:「沒有types.h,也同樣不會出錯啊,在sayhello.h中不是引用過types.h嗎?」這樣作真的是畫蛇添足嗎?固然不是,我以爲它是至關有意義的。第一,它維護了咱們本身定下的原則。保持一個不變的代碼習慣是頗有好處的。第二,因爲咱們保證了頭文件中不加入實現性質的代碼,只寫些聲明類的代碼,它們在編譯時只是起來語法制導的做用,並不會被成爲目標程序的一部分,因此這樣寫並不會形成浪費。這也是提倡頭文件中不要夾雜代碼的一個緣由。
若是真的不想把頭文件編譯屢次的話,還有一個辦法,以下:
#ifndef _HEADER_FILE_
#define _HEADER_FILE_
//聲明部分
//...........
#endif
這樣寫能夠保證編譯器只編譯一次,其中的_HEADER_FILE_本身定義的頭文件的惟一標識,只要別跟常量定義和別的頭文件衝突,您喜歡叫它什麼就叫它什麼吧。^^
6、 總結
最後總結一下吧。若是當你在編寫本身龐大的代碼文件羣的時候,遇到了一些猶豫,就想一想上面的原則和應用。當由於頭文件編譯出錯的時候,考慮一下是否本身有哪些動做違反了上面的原則。只要常常思考,每一個人都會總結是適合本身的使用習慣,儘可能減小在這些程序員看來可有可無的事情上出錯的機會。以上只是本人本身使用習慣的一次總結,不表明任何規範和標準,歡迎善意的批評指正.^_^