C 語言的標頭檔案是必須的嗎

發表於2016-11-22

N年前學習C語言開始,就被老師教導,要記得包含標頭檔案。自己也養成了二話不說就#include的習慣。從來沒有靜下信來想想,C語言必需要有標頭檔案嗎?標頭檔案到底起到什麼的作用。

最近一段時間做了一些靜態庫和動態庫相關的東西,一些內容在上一篇博文中,閱讀上一篇博文的看官可以看出,我的靜態庫和動態庫都沒有對應的標頭檔案,可是我的應用程式呼叫了庫,沒有包含標頭檔案,一樣是正常地編譯執行。意識到這個問題的時候,我還有一陣恐慌,不知道怎麼解釋這個現象。因為我們常規都會包含標頭檔案,比如呼叫多執行緒庫,都會 #include 。

對於這個問題,我又做了一些實驗算是把這個問題有個初步的理解。當然這個題目起的有點標題黨,這麼大的題目不是我這種菜鳥能夠駕馭的了的,但是我還是想把自己的理解說以下,歡迎各位路過的高手批評指點。

我寫了一個測試程式,裡面沒有包含任何的標頭檔案。

這個測試程式中呼叫了printf,sleep,creat,pthread_create,he pthread_join,按照慣例,我們是需要標頭檔案的,需要的標頭檔案如下:

現在我們觀察,能否編譯通過:

這是預料之中的事情,因為,沒有找到pthread_create 和pthread_join兩個函式的定義。這是因為連結時,沒有指定libpthread.so。加上-lpthread自然能夠編過。 有的看官就問了,為什麼creat sleep 和printf為什麼不報找不到函式定義。那是因為這些函式都在libc庫中,而libc庫不需要顯式指定,預設就包換了libc的動態庫。

有一些警告,但是畢竟編出了可執行檔案test,並能夠正確的執行。

我們的實驗做完了,沒有包含任何標頭檔案,我想做的事情,動態庫也幫我做了。這個實驗就證明了,標頭檔案並不是必不可少的,沒有標頭檔案,只要你清楚函式呼叫的介面,清楚需要包含那個庫,一樣可以做你想做的事情。

很顛覆常規是吧,既然沒有標頭檔案,我也能幹事,標頭檔案存在還有什麼意義呢?

我們想一下,如果我寫了一個功能很強大的庫,(靜態庫或者動態庫,whatever),完全沒有標頭檔案,因為函式通通是我寫的,我很清楚每個函式的入參個數 型別及返回值,清楚每個函式的功能,所以我可以不需要標頭檔案。但是如果另外一個人來用我的庫檔案,他就會很撓頭,因為沒有標頭檔案,他不知道我庫裡面有那些函式,每個函式入參 個數型別以及返回值。他就沒法呼叫我的庫做想做的事情。從這個角度上講,標頭檔案是描述性的檔案,它不涉及功能的實現,就好像一本書,沒有任何的目錄和章節名,也是OK的,但是讀者讀起來就很不方便。

除了這一點,我還想到了一點,就是讓編譯器幫我們做必要的檢查。編譯器的功能是很強大的,他能幫你做很多的事情。如果你沒有包含標頭檔案,函式呼叫錯了,編譯器也幫不了你。看下面的例子:

我將sleep函式呼叫多加了個引數,pthread_create函式少傳了個引數,因為我沒有標頭檔案,所以編譯器幫不了什麼。

看到了,編譯器gcc愛莫能助,發現不了sleep多了個引數,pthread_create少個引數。這種情況下還是能執行。

這種情況是很危險的,因為會破壞棧空間,有可能將棧空間踩壞。但是如果我包含了標頭檔案,就不同了,編譯器會幫我檢查。

看下編譯情況:

有種觀點是這樣的,如果你的程式可能崩潰,請讓它在第一時間第一現場崩潰,這樣除錯的代價比較低,方便troubleshooting,如果程式已經走到了背離預想軌道的分支,但是還有辦法不讓程式崩潰,請不要做這種事情,因為,早就背離了軌道,你不去檢查,硬讓程式繼續走下去,如果終於崩潰了,你找不到這個第一現場,你很難除錯。

編譯檢查也是這樣,對自己的程式碼嚴格檢查,讓它編譯不過也比編譯過了埋下隱患要好的多。因為越往後面,troubleshooting的代價也就越大。

相關文章