基本上每門語言都是用"helloworld"作為她的第一講匹厘,C語言也不例外脚线。
傳統(tǒng)HelloWorld
傳統(tǒng)的教材都是讓你安裝一種IDE集成環(huán)境屎媳,然后照例子敲入代碼,按下ctrl+r之類的運(yùn)行程序阔逼,感受一下運(yùn)行的結(jié)果兆衅。HelloWorld程序如下:
- 在編輯器中敲入:
#include <stdlib.h>
#include <stdio.h>
void main() {
printf("Hello World\n");
}
- 按下Ctrl+r運(yùn)行,當(dāng)然我沒有IDE環(huán)境嗜浮,就用Linux終端代替一下了哈羡亩。如下:
- 當(dāng)然這里有幾個問題
- 當(dāng)時你其實(shí)不知道這個程序是怎么被編譯出來的。
- 當(dāng)時你也不知道她在什么環(huán)境下運(yùn)行的危融。
- 當(dāng)時你肯定也不會去想畏铆,我這樣寫在其它計算機(jī)上能運(yùn)行嗎?
- 現(xiàn)在你肯定也沒有考慮過吉殃,我能不能換個花樣玩玩呢辞居?
老生常談
- 為何我說一個gcc程序和一個編輯器vim程序就是c的開發(fā)環(huán)境呢?而傳統(tǒng)的書籍蛋勺,特別是國內(nèi)的c程序數(shù)據(jù)瓦灶,以來就讓你裝一個大得一逼的IDE環(huán)境,以前是vb6.0幾百M(fèi)B抱完,現(xiàn)在是vs2015之類的好幾GB贼陶。而我所說的gcc、vim充其量就幾個MB。因為IDE環(huán)境集成了太多功能了碉怔,編輯烘贴、編譯、調(diào)試撮胧、自動補(bǔ)全庙楚、語法錯誤提示等等。我一般不用這樣環(huán)境趴樱,初學(xué)者更不應(yīng)該用,她會使你太依賴IDE酪捡,不了解原理叁征,也少了很多樂趣。
- 其實(shí)所有程序都是編譯器或者解釋器讀取一個純文本中的代碼逛薇,然后對要么生成目標(biāo)二進(jìn)制文件(編譯型語言)捺疼,要么直接就運(yùn)行(解釋型語言)了。對于C語言永罚,當(dāng)然是編譯后啤呼,生成有特定格式二進(jìn)制文件,在計算終端中運(yùn)行呢袱,終端也是個程序官扣,她是操作系統(tǒng)的一部分,操作系統(tǒng)也是程序(一兩句話說不清這個關(guān)系>_<)羞福。像C這樣的語言惕蹄,有語法解釋,中間文件編譯治专,目標(biāo)程序鏈接這三部曲構(gòu)成卖陵,當(dāng)然還可以細(xì)分。語法解釋主要是判斷語法正確與否张峰,然后是將
#include<stdlib.h>
這樣的語句進(jìn)行預(yù)處理生成最終的代碼源文件泪蔫,然后編譯成與特定類型的操作系統(tǒng)相關(guān)的目標(biāo)代碼,這個目標(biāo)代碼其實(shí)可以在只要與當(dāng)時生產(chǎn)的操作系統(tǒng)類型相同的操作系統(tǒng)中重復(fù)使用的喘批,然后是鏈接成目標(biāo)文件撩荣,這個文件大多數(shù)也可以在相同的操作系統(tǒng)中直接使用,但是由于有的程序會依賴特定的庫饶深,所以出表現(xiàn)出不能運(yùn)行成功的情況罷了婿滓。比如剛才的helloworld程序在類Unix系統(tǒng)中,使用如下操作生成可執(zhí)行文件:
然后是運(yùn)行:
可以執(zhí)行環(huán)境就是開啟一個終端程序粥喜,然后./a.out
運(yùn)行凸主。
- 這段代碼在絕大多數(shù)類Unix操作系統(tǒng)中都被編譯運(yùn)行,甚至這個二進(jìn)制文件如果在內(nèi)核相同操作系統(tǒng)也可以直接運(yùn)行额湘,而不需要重新編譯卿吐。但是你可能注意到這個程序在編譯是產(chǎn)生了警告旁舰,因為她的main()入口函數(shù)不是標(biāo)準(zhǔn)的,即不是可移植的嗡官。
守則一:一個負(fù)責(zé)的程序員編寫程序要考慮可移植性
- 標(biāo)準(zhǔn)的可以移植性入口函數(shù)應(yīng)該是這樣的:
int main(int argc, const char *argv[]) { ... }
其中的形式參數(shù)的作用就是接收運(yùn)行時傳入的命令參數(shù)箭窜,后面我們會討論到。
- 你真沒想過怎么將這段代碼換個花樣玩衍腥?
這個不行磺樱,作為一個程序員,腦洞太小不好婆咸,腦洞需要大開的竹捉,有多大得開多大。
換著花樣玩
使用全局復(fù)用
全局變量基本是沒種語言都支持的尚骄,即為全局块差,即是對所有人可見
-
有一天你的老板說,現(xiàn)在我們生意不好倔丈,我們要改程序輸出的內(nèi)容憨闰,發(fā)發(fā)牢騷,而不是友好的問候需五。恰恰這要交給你來做鹉动,而且當(dāng)時沒有使用任何全局變量或著宏來代替這些輸出內(nèi)容,而且涉及的幾十個文件可能是零零散散的分布在好幾百個位置中宏邮,那么恭喜你训裆,即使使用多文件文本替換也是挺麻煩的事,而且你總是要修改好幾十個文件蜀铲。如果當(dāng)時使用全局變量也很好用修改的边琉。
- 找一個這些文件都會引用的頭文件,比如叫著utils.h记劝,添加如下外部變量聲明:
extern char g_SayHello[];
- 再任何一個.c文件都可以变姨,但是推薦還是utils.c中,這個就是規(guī)范厌丑,下次定欧,接手項目的人要修改哪個文件.h有類似上面的外部引用,自然而然的就在對應(yīng)的.c中尋找其定義了:
char g_SayHello[] = "Kick the bucket!";
- 修改helloworld.c怒竿,其實(shí)這個名字不合適砍鸠,最好加一個前綴,表面她含有入口函數(shù)耕驰,main_helloworld.c:
...
#include "utils.h"
...
int main(int argc, const char *argv[]) {
printf("%s\n", g_SayHello);
return EXIT_SUCCESS;
}
- 加入新的源文件一起編譯爷辱、運(yùn)行如下:
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c
[zhoukai@zhoukai-MBPR:tmp]$ ./a.out
Kick the bucket!
[zhoukai@zhoukai-MBPR:tmp]$
使用宏復(fù)用
宏是c語言強(qiáng)有力的特性之一,不使用宏,你會做很多不討喜的工作饭弓,但是濫用宏也會不討喜双饥,所有任何東西都有利有弊。就像沒有壞人弟断,就體現(xiàn)不出好人咏花;沒有細(xì)菌這樣的微生物,滿世界都是尸體一樣阀趴。這個是一個哲學(xué)問題???
- 繼續(xù)上面的情景昏翰。你好不容易完成了需求,結(jié)果你老板說刘急,我們需要在不同操作系統(tǒng)上讓運(yùn)行程序發(fā)不同的牢騷???棚菊,雖然你心中有一萬匹草泥馬在狂奔,但為了工資忍了吧排霉。顯然上面的代碼在不同的計算機(jī)上發(fā)布時,如果在編譯后需要讓程序運(yùn)行時顯現(xiàn)一些不同的東西民轴,是需要修改源代碼的攻柠,很不方便,做這樣的事情后裸,宏的優(yōu)點(diǎn)就體現(xiàn)出來了瑰钮,因為在編譯時,可以給定參數(shù)定義一個宏讓源代碼相同的程序有不同的行為:
- 還是utils.h微驶,添加如下宏:
//稍作解釋浪谴,下面的宏定義是關(guān)聯(lián)預(yù)編譯條件宏使用,即如果沒有定義宏因苹,則定一個默認(rèn)的宏
#ifndef SAY_HELLO
#define SAY_HELLO "Kick the bucket!"
#endif
- 修改main_helloworld.c:
int main(int argc, const char *argv[]) {
printf("%s\n", SAY_HELLO);
return EXIT_SUCCESS;
}
- 編譯不同的行為的程序:
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c -DSAY_HELLO=\"Drop\ dead\!\" -o a.out
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c -DSAY_HELLO=\"Go\ to\ hell\!\" -o ab.out
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c -DSAY_HELLO=\"Damn\ you\!\" -o abc.out
[zhoukai@zhoukai-MBPR:tmp]$ ./a.out
Drop dead!
[zhoukai@zhoukai-MBPR:tmp]$ ./ab.out
Go to hell!
[zhoukai@zhoukai-MBPR:tmp]$ ./abc.out
Damn you!
[zhoukai@zhoukai-MBPR:tmp]$
稍作解釋苟耻,gcc最簡單的使用就是
gcc <源文件名>
,然后就會生成默認(rèn)的程序文件a.out扶檐,但是一般都希望又一個自定義的程序文件名凶杖,所以加上選項參數(shù)-o <目標(biāo)名>
;在不加-c <源文件名>
的情況下都是直接完成三部曲款筑,生成可執(zhí)行文件的智蝠;其它還有很多可選的參數(shù),后面慢慢說奈梳。
- 可以看到不同的程序使用同樣的源代碼編譯的杈湾,但是編譯時可以重定義宏,從而改變其行為攘须。這里使用了可選參數(shù)-Dmacro="string"(加上''只是因為shell環(huán)境需要轉(zhuǎn)義雙引號)漆撞,這樣可以將編譯時宏參數(shù)帶入預(yù)處理,從而替換默認(rèn)的宏參數(shù)。
結(jié)束語
為什么要寫這么多叫挟,其實(shí)也不是高深的代碼艰匙。目的就是一個,你在編寫代碼的時候是否比別人多想了一步呢抹恳?是否考慮過代碼的可移植性呢员凝?是否考慮過代碼的可復(fù)用性呢?是否考慮過代碼的可維護(hù)性呢奋献?這些健霹!都是一名合格的程序員應(yīng)該考慮的問題。