新說`HelloWorld`

基本上每門語言都是用"helloworld"做爲她的第一講,C語言也不例外。程序員

##傳統HelloWorld

   傳統的教材都是讓你安裝一種IDE集成環境,而後照例子敲入代碼,按下ctrl+r之類的運行程序,感覺一下運行的結果。HelloWorld程序以下:shell

  • 在編輯器中敲入:
#include <stdlib.h>
#include <stdio.h>

void main() {
        printf("Hello World\n");
}
  • 按下Ctrl+r運行,固然我沒有IDE環境,就用Linux終端代替一下了哈。以下:
[zhoukai@zhoukai-MBPR:tmp]$HelloWorld
  • 固然這裏有幾個問題
    1. 當時你其實不知道這個程序是怎麼被編譯出來的。
    2. 當時你也不知道她在什麼環境下運行的。
    3. 當時你確定也不會去想,我這樣寫在其它計算機上能運行嗎?
    4. 如今你確定也沒有考慮過,我能不能換個花樣玩玩呢?

##老生常談

  • 爲什麼我說一個gcc程序和一個編輯器vim程序就是c的開發環境呢?而傳統的書籍,特別是國內的c程序數據,以來就讓你裝一個大得一逼的IDE環境,之前是vb6.0幾百MB,如今是vs2015之類的好幾GB。而我所說的gcc、vim充其量就幾個MB。由於IDE環境集成了太多功能了,編輯、編譯、調試、自動補全、語法錯誤提示等等。我通常不用這樣環境,初學者更不該該用,她會使你太依賴IDE,不瞭解原理,也少了不少樂趣。
  • 其實全部程序都是編譯器或者解釋器讀取一個純文本中的代碼,而後對要麼生成目標二進制文件(編譯型語言),要麼直接就運行(解釋型語言)了。對於C語言,固然是編譯後,生成有特定格式二進制文件,在計算終端中運行,終端也是個程序,她是操做系統的一部分,操做系統也是程序(一兩句話說不清這個關係>_<)。像C這樣的語言,有語法解釋,中間文件編譯,目標程序連接這三部曲構成,固然還能夠細分。語法解釋主要是判斷語法正確與否,而後是將#include<stdlib.h>這樣的語句進行預處理生成最終的代碼源文件,而後編譯成與特定類型的操做系統相關的目標代碼,這個目標代碼其實能夠在只要與當時生產的操做系統類型相同的操做系統中重複使用的,而後是連接成目標文件,這個文件大多數也能夠在相同的操做系統中直接使用,可是因爲有的程序會依賴特定的庫,因此出表現出不能運行成功的狀況罷了。好比剛纔的helloworld程序在類Unix系統中,使用以下操做生成可執行文件:
[zhoukai@zhoukai-MBPR:tmp]$gcc main.c

而後是運行:vim

[zhoukai@zhoukai-MBPR:tmp]$./a.out
[zhoukai@zhoukai-MBPR:tmp]$HelloWorld

能夠執行環境就是開啓一個終端程序,而後./a.out運行。編輯器

  • 這段代碼在絕大多數類Unix操做系統中都被編譯運行,甚至這個二進制文件若是在內核相同操做系統也能夠直接運行,而不須要從新編譯。可是你可能注意到這個程序在編譯是產生了警告,由於她的main()入口函數不是標準的,即不是可移植的。

守則一:一個負責的程序員編寫程序要考慮可移植性函數

  • 標準的能夠移植性入口函數應該是這樣的:
int main(int argc, const char *argv[]) { ... }

其中的形式參數的做用就是接收運行時傳入的命令參數,後面咱們會討論到。操作系統

  • 你真沒想過怎麼將這段代碼換個花樣玩? 這個不行,做爲一個程序員,腦洞過小很差,腦洞須要大開的,有多大得開多大。

##換着花樣玩

###使用全局複用調試

全局變量基本是沒種語言都支持的,即爲全局,便是對全部人可見code

  • 有一天你的老闆說,如今咱們生意很差,咱們要改程序輸出的內容,發發牢騷,而不是友好的問候。偏偏這要交給你來作,並且當時沒有使用任何全局變量或着宏來代替這些輸出內容,並且涉及的幾十個文件多是零零散散的分佈在好幾百個位置中,那麼恭喜你,即便使用多文件文本替換也是挺麻煩的事,並且你老是要修改好幾十個文件。若是當時使用全局變量也很好用修改的。開發

    1. 找一個這些文件都會引用的頭文件,好比叫着utils.h,添加以下外部變量聲明:
extern char g_SayHello[];
  1. 再任何一個.c文件均可以,可是推薦仍是utils.c中,這個就是規範,下次,接手項目的人要修改哪一個文件.h有相似上面的外部引用,天然而然的就在對應的.c中尋找其定義了:
char g_SayHello[] = "Kick the bucket!";
  1. 修改helloworld.c,其實這個名字不合適,最好加一個前綴,表面她含有入口函數,main_helloworld.c:
...
#include "utils.h"
...
int main(int argc, const char *argv[]) {
        printf("%s\n", g_SayHello);
        return EXIT_SUCCESS;
}
  1. 加入新的源文件一塊兒編譯、運行以下:
[zhoukai@zhoukai-MBPR:tmp]$ gcc main_helloworld.c utils.c
[zhoukai@zhoukai-MBPR:tmp]$ ./a.out
Kick the bucket!
[zhoukai@zhoukai-MBPR:tmp]$

###使用宏複用編譯器

宏是c語言強有力的特性之一,不使用宏,你會作不少不討喜的工做,可是濫用宏也會不討喜,全部任何東西都有利有弊。就像沒有壞人,就體現不出好人;沒有細菌這樣的微生物,滿世界都是屍體同樣。這個是一個哲學問題〜〜〜

  • 繼續上面的情景。你好不容易完成了需求,結果你老闆說,咱們須要在不一樣操做系統上讓運行程序發不一樣的牢騷〜〜〜,雖然你心中有一萬匹草泥馬在狂奔,但爲了工資忍了吧。顯然上面的代碼在不一樣的計算機上發佈時,若是在編譯後須要讓程序運行時顯現一些不一樣的東西,是須要修改源代碼的,很不方便,作這樣的事情,宏的優勢就體現出來了,由於在編譯時,能夠給定參數定義一個宏讓源代碼相同的程序有不一樣的行爲:
    1. 仍是utils.h,添加以下宏:
//稍做解釋,下面的宏定義是關聯預編譯條件宏使用,即若是沒有定義宏,則定一個默認的宏
#ifndef SAY_HELLO
#define SAY_HELLO "Kick the bucket!"
#endif
  1. 修改main_helloworld.c:
int main(int argc, const char *argv[]) {
        printf("%s\n", SAY_HELLO);
        return EXIT_SUCCESS;
}
  1. 編譯不一樣的行爲的程序:
[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 <源文件名>,而後就會生成默認的程序文件a.out,可是通常都但願又一個自定義的程序文件名,因此加上選項參數-o <目標名>;在不加-c <源文件名>的狀況下都是直接完成三部曲,生成可執行文件的;其它還有不少可選的參數,後面慢慢說。

  • 能夠看到不一樣的程序使用一樣的源代碼編譯的,可是編譯時能夠重定義宏,從而改變其行爲。這裏使用了可選參數-Dmacro="string"(加上''只是由於shell環境須要轉義雙引號),這樣能夠將編譯時宏參數帶入預處理,從而替換默認的宏參數。

##結束語

爲何要寫這麼多,其實也不是高深的代碼。目的就是一個,你在編寫代碼的時候是否比別人多想了一步呢?是否考慮過代碼的可移植性呢?是否考慮過代碼的可複用性呢?是否考慮過代碼的可維護性呢?這些!都是一名合格的程序員應該考慮的問題。

相關文章
相關標籤/搜索