通常一本教編程的書中第一個例子都是打印“Hello, World.”,這個傳統源自[K&R],用C語言寫這個程序可以這樣寫:
例 1.1. Hello World
#include <stdio.h> /* main: generate some simple output */ int main(void) { printf("Hello, world.\n"); return 0; }
將這個程序保存成main.c
,然後編譯執行:
$ gcc main.c $ ./a.out Hello, world.
gcc
是Linux平台的C編譯器,編譯後在當前目錄下生成執行檔a.out
,直接在命令行輸入這個執行檔的路徑就可以執行它。如果不想把檔案名叫a.out
,可以用gcc
的-o
參數自己指定檔案名:
$ gcc main.c -o main $ ./main Hello, world.
雖然這只是一個很小的程序,但我們目前暫時還不具備相關的知識來完全理解這個程序,比如程序的第一行,還有程序主體的int main(void){...return 0;}
結構,這些部分我們暫時不詳細解釋,讀者現在只需要把它們看成是每個程序按慣例必須要寫的部分(Boilerplate)。但要注意main
是一個特殊的名字,C程序總是從main
裡面的第一條語句開始執行的,在這個程序中是指printf
這條語句。
第3行的/* ... */
結構是一個註釋(Comment),其中可以寫一些描述性的話,解釋這段程序在做什麼。註釋只是寫給程序員看的,編譯器會忽略從/*
到*/
的所有字元,所以寫註釋沒有語法規則,愛怎麼寫就怎麼寫,並且不管寫多少都不會被編譯進執行檔中。
printf
語句的作用是把消息打印到屏幕。注意語句的末尾以;號(Semicolon)結束,下一條語句return 0;
也是如此。
C語言用{}括號(Brace或Curly Brace)把語法結構分成組,在上面的程序中printf
和return
語句套在main
的{}括號中,表示它們屬於main
的定義之中。我們看到這兩句相比main
那一行都縮進(Indent)了一些,在代碼中可以用若干個空格(Blank)和Tab字元來縮進,縮進不是必須的,但這樣使我們更容易看出這兩行是屬於main
的定義之中的,要寫出漂亮的程序必須有整齊的縮進,第 1 節 “縮進和空白”將介紹推薦的縮進寫法。
正如前面所說,編譯器對於語法錯誤是毫不留情的,如果你的程序有一點拼寫錯誤,例如第一行寫成了stdoi.h
,在編譯時會得到錯誤提示:
$ gcc main.c main.c:1:19: error: stdoi.h: No such file or directory ...
這個錯誤提示非常緊湊,初學者往往不容易看明白出了什麼錯誤,即使知道這個錯誤提示說的是第1行有錯誤,很多初學者對照着書看好幾遍也看不出自己這一行哪裡有錯誤,因為他們對符號和拼寫不敏感(尤其是英文較差的初學者),他們還不知道這些符號是什麼意思又如何能記住正確的拼寫?對於初學者來說,最想看到的錯誤提示其實是這樣的:“在main.c
程序第1行的第19列,您試圖包含一個叫做stdoi.h
的檔案,可惜我沒有找到這個檔案,但我卻找到了一個叫做stdio.h
的檔案,我猜這個才是您想要的,對嗎?”可惜沒有任何編譯器會友善到這個程度,大多數時候你所得到的錯誤提示並不能直接指出誰是犯人,而只是一個線索,你需要根據這個線索做一些偵探和推理。
有些時候編譯器的提示信息不是error
而是warning
,例如把上例中的printf("Hello, world.\n");
改成printf(1);
然後編譯運行:
$ gcc main.c main.c: In function ‘main’: main.c:7: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast $ ./a.out Segmentation fault
這個警告信息是說類型不匹配,但勉強還能配得上。警告信息不是致命錯誤,編譯仍然可以繼續,如果整個編譯過程只有警告信息而沒有錯誤信息,仍然可以生成執行檔。但是,警告信息也是不容忽視的。出警告信息說明你的程序寫得不夠規範,可能有Bug,雖然能編譯生成執行檔,但程序的運行結果往往是不正確的,例如上面的程序運行時出了一個段錯誤,這屬於運行時錯誤。各種警告信息的嚴重程度不同,像上面這種警告几乎一定表明程序中有Bug,而另外一些警告只表明程序寫得不夠規範,一般還是能正確運行的,有些不重要的警告信息gcc
預設是不提示的,但這些警告信息也有可能表明程序中有Bug。一個好的習慣是打開gcc
的-Wall
選項,也就是讓gcc
提示所有的警告信息,不管是嚴重的還是不嚴重的,然後把這些問題從代碼中全部消滅。比如把上例中的printf("Hello, world.\n");
改成printf(0);
然後編譯運行:
$ gcc main.c $ ./a.out
編譯既不報錯也不報警告,一切正常,但是運行程序什麼也不打印。如果打開-Wall
選項編譯就會報警告了:
$ gcc -Wall main.c main.c: In function ‘main’: main.c:7: warning: null argument where non-null required (argument 1)
如果printf
中的0是你不小心寫上去的(例如錯誤地使用了編輯器的查找替換功能),這個警告就能幫助你發現錯誤。雖然本書的命令行為了突出重點通常省略-Wall
選項,但是強烈建議你寫每一個編譯命令時都加上-Wall
選項。