[C语言]为什么要有include?——从Hello World说起
本文轉(zhuǎn)自:http://mp.weixin.qq.com/s?__biz=MzAwOTgzNzQyMw==&mid=433613487&idx=1&sn=803995d612faadce6e4418789a6a65a8&scene=2&srcid=0312ElIT9UmR0ZygPGHxDxs2&from=timeline&isappinstalled=0#wechat_redirect
大家都會寫的Hello World程序長這個(gè)模樣:
#include <stdio.h>
int main( ){
? ? printf("Hello world!\n");
? ? return 0;
}
看,關(guān)鍵的就是第三行,調(diào)用了printf函數(shù),在屏幕上打印了偉大的"Hello world!"。第一行那個(gè)include是個(gè)啥?我聽到過這樣的解釋:“include是包含的意思,尖括號里的stdio.h是標(biāo)準(zhǔn)輸入輸出函數(shù)庫文件,這個(gè)文件里有printf的代碼,把這個(gè)代碼包含到main函數(shù)之前,就可以使用printf函數(shù)進(jìn)行打印啦~” —— 打住!這種話騙騙剛開始學(xué)習(xí)C語言的小孩還可以,怎么可以用來騙大學(xué)生呢?現(xiàn)在請大家把第一行刪除,重新編譯運(yùn)行看看?奇葩,居然依然可以順利執(zhí)行!但是細(xì)心的孩紙會發(fā)現(xiàn),這次盡管成功運(yùn)行了,但是編譯時(shí)有一條警告(Warning)信息,如果你的編譯器是gcc的話,這條Warning的樣子應(yīng)該是:
warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)'
What?這是咩意思?冷靜一下,請大家不要一看到英文就頭皮發(fā)麻、六神無主、舉手問老師。至少可以翻翻詞典嘛,前兩個(gè)詞就很明白了:隱含聲明。先不解釋,我們繼續(xù)修改代碼,變成這樣:
int main(){
? ? printf("Hello world!\n");
? ? hehe(3, 5);
? ? return 0;
}
然后大家就笑了:這肯定不能運(yùn)行了吧?那個(gè)hehe()函數(shù)根本就不存在嘛!沒錯(cuò),這個(gè)確實(shí)不能運(yùn)行了,但是依然可以編譯。據(jù)我所知大家都很偷懶,編好程序之后都是直接在集成開發(fā)環(huán)境(IDE)中直接點(diǎn)運(yùn)行按鈕的,那么這次請點(diǎn)擊編譯按鈕,或者在命令行中手動輸入編譯命令,我們就能發(fā)現(xiàn),又多了一條Warning:
?warning: implicit declaration of function 'haha' is invalid in C99
又是“隱含聲明”!
為什么printf和hehe兩個(gè)函數(shù)都提示“隱含聲明”,前一個(gè)能運(yùn)行,而后一個(gè)不行呢?我們從頭梳理:
首先,了解一下什么叫做函數(shù)的“聲明”(或者叫“原型”)。函數(shù)聲明就是描述一下這個(gè)函數(shù)長什么樣子,而不描述這個(gè)函數(shù)具體怎么實(shí)現(xiàn)的。舉個(gè)例子: void hehe(int, int); 就是一個(gè)函數(shù)的聲明,注意,括號后面是一個(gè)分號,而不是一對花括號{ },而且括號里面只寫了參量類型,沒有寫參量名啊。函數(shù)的聲明是給編譯器看的,當(dāng)編譯器看到上面那個(gè)聲明,她就會知道:哦,有一個(gè)函數(shù),名字叫hehe,帶有兩個(gè)整型參量,沒有返回值。至于這個(gè)函數(shù)具體是干什么的,編譯器不關(guān)心~ 同樣,編譯器也不關(guān)心這個(gè)函數(shù)的兩個(gè)參量叫什么名字,所以列表中可以只寫類型,不寫參量名。
然后,我們大家都學(xué)過:C語言規(guī)范要求在調(diào)用函數(shù)之前,必須出現(xiàn)該函數(shù)的聲明。這是為什么呢?是為了讓編譯器高興啊。編譯器從上往下檢查代碼,當(dāng)看到函數(shù)調(diào)用時(shí),就會對照此前出現(xiàn)過的聲明檢查一下調(diào)用是否符合聲明的格式。如果冷不丁地看到一個(gè)函數(shù)調(diào)用,但在此之前沒見到過該函數(shù)的聲明,編譯器就郁悶了,她的內(nèi)心活動是:“這函數(shù),沒見過、不認(rèn)識啊,所以,這么個(gè)調(diào)用法,到底是正確呢?還是錯(cuò)誤呢?我不知道啊!我作為一個(gè)編譯器,不知道這句是正確還是錯(cuò)誤,多沒面子啊!”這時(shí)候,編譯器能報(bào)一個(gè)“錯(cuò)誤”(Error)嗎?不能。但是編譯器究竟還是負(fù)責(zé)人的,她覺得這句話可能會有問題,因此給了一個(gè)警告,讓程序員自己看著辦……
接下來,分析一下為什么printf沒有聲明,能夠正確執(zhí)行。大家可以翻翻前面我說的那個(gè)“騙小孩子”的話,stdio.h里面并沒有printf的實(shí)現(xiàn)代碼,而是有printf的聲明(請?jiān)谧约河?jì)算機(jī)中自行搜索stdio.h,并打開文件查找printf,看看是如何聲明的)。printf的實(shí)現(xiàn)代碼,是在C標(biāo)準(zhǔn)庫的其它文件中(隨著OS不同,具體文件也不同)的,而且是編譯過之后的代碼,不像stdio.h中,都是“源代碼”。如果把include那行刪去,編譯器在看到printf調(diào)用時(shí),就不知道printf應(yīng)該長成啥樣,所以不知道調(diào)用是否正確,給了一個(gè)警告。但是在編譯之后的“鏈接”過程,C標(biāo)準(zhǔn)庫就被鏈接到程序中了,所以只要printf的調(diào)用寫得沒有錯(cuò)誤,就可以正確運(yùn)行。
最后,分析為什么添加hehe(3,5)之后能編譯,但是不能正確執(zhí)行。道理與上面是一樣的,編譯器看到hehe(3,5)的調(diào)用,因?yàn)闆]有聲明過,她不認(rèn)識這函數(shù)啊,所以,不能報(bào)錯(cuò),只能給一個(gè)警告。接下來的步驟,鏈接,C標(biāo)準(zhǔn)庫依然被鏈接到了程序中,但是……但是……但是……h(huán)ehe( )是個(gè)什么鬼?這函數(shù)根本就不在C標(biāo)準(zhǔn)庫中好嗎!而且程序員自己所寫的程序中也沒有提供hehe( )的實(shí)現(xiàn)代碼啊!所以鏈接失敗……
哦,似乎忘了說,C代碼寫好之后要想運(yùn)行,得經(jīng)過這么幾步:(1)對每個(gè)源文件分別進(jìn)行編譯(compile),各自生成目標(biāo)代碼(擴(kuò)展名可能是.obj .o 或者其它)(2)把編譯過后生成的這些目標(biāo)代碼以及C標(biāo)準(zhǔn)庫鏈接起來(link),生成可運(yùn)行的程序。
最后放一條冷笑話:某個(gè)峽谷上方架有一座橋,一次暴風(fēng)雨過后,橋斷了,負(fù)責(zé)維護(hù)的工人一時(shí)間還來不及修復(fù),于是在橋頭插了一塊牌子,上面寫道:“Warning! The bridge is broken!” 結(jié)果呢?還是有好多人毅然走上斷橋,摔到了峽谷里。工人們感到很奇怪,于是到了谷底查看,發(fā)現(xiàn)摔到谷底的全都是程序員。
上面的笑話沒看懂?沒關(guān)系,作為程序員,記住下面這一點(diǎn)就行了:絕不放過任何一個(gè)warning,否則可能會死得很慘~~~
總結(jié)
以上是生活随笔為你收集整理的[C语言]为什么要有include?——从Hello World说起的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 招商银行阿诺粉丝信用卡年费多少?怎么免?
- 下一篇: spectral hashing--谱哈