hello, world——《例C》(二)

提示:本章信息量略大

讀到此處,讀者應該可以在自己的電腦上運行C程序。如果遇到問題,請在評論中提出,或者到issues提出。

hello, world這句話,即使是沒接觸過編程的人也一定聽說過(和「程序猿」形象綁定)。這是因為C語言的創始人在自編教程《The C Programming Language》中,第一個教程是在終端(Terminal)輸出hello, world而得名。結合C語言的歷史超然地位,有相當多的語言教程的第一個程序也是在向它致敬,於是hello, world也成了程序員界無人不知的梗。

本章任務

修改示例代碼,讓程序輸出三行情書:

手機里依然留著「喜歡你」那句未曾送出的信息

提示:

  • 在字元串外輸入中文符號將導致轉換失敗。

  • Windows平台上,若是編譯失敗,先檢查「文件」->「高級保存選項」中的文件編碼是否為Unicode(UTF-8無簽名)代碼頁65001

示例代碼

本文對應的程序還在 hello 文件夾,C語言源代碼的文件擴展名是c(或是h)。

// 注釋在對應的程序文件夾內的程序文件未必有// 導入頭文件 (引入庫)#include <stdio.h> // 為了能讓程序在控制台顯示東西引入的/* * 以下是預處理語句 * #ifdef _WIN32 當宏 _WIN32 被定義時(僅在Windows中) * #else 否則 * #endif 結束判斷 */#ifdef _WIN32#include <stdlib.h> // 為了使用system函數而引入// 宏定義:防止閃退#define PAUSE system("pause");#else// 防止未定義宏,這個是C99的空白宏#define PAUSE#endif// main函數(子程序):沒有參數,返回整數int main() { // 列印函數:參數為文字(字元串),屬於庫stdio // 字元串的兩端有半形雙引號"",字元則是半形單引號 // 處理過程中向命令行輸出參數 // 返回整數(被忽略) printf("hello, world
"); // 在Windows下被替換成 system("pause"); // 在其他操作系統下被替換成 PAUSE // 表示main函數返回整數0 return 0;}

該程序的功能是:向控制台輸出字元串hello, world。

注釋

大多數情況下人們很難通過讀程序代碼來了解編寫程序的人的意圖,而需要額外的文字來解釋。於是注釋(Comment)便應運而生。在編譯過程中,注釋都會被忽略。(畢竟只是給人看的)

注釋又分為兩種:行注釋和塊注釋。行注釋以雙斜杠//開始,雙斜杠之後的內容到該行結束都代表注釋。

// 識得唔識得噶

行注釋是C99的特性,不過很多編譯器在C89階段擴展實現了。

塊注釋以起始符/*開始,終止符*/結束。

塊注釋的起始符和終止符不能嵌套,也就是

/* 我是注釋嗎 /* 我應該是注釋吧 */ (吃瓜群眾)*/

以及

/* */ 我應該是注釋吧 /* */

都是不行的。

語言標準

C語言最初是被拿來編寫Unix操作系統的,後來被廣泛地應用於不同的體系結構、不同的編譯器和不同的操作系統。為了防止各家使用C的過程中語法被分裂,美國國家標準局(ANSI)於1989年將C語言標準化。該標準被稱為C89. 國際標準化組織(ISO)通過修改C89,於1990年發布了更加國際化的C語言標準,被稱為C90。ANSI則選擇跟進ISO的標準。

1999年,ISO發布新的C語言標準,史稱C99, 這也是本系列所基於的語言標準。本系列的讀者也有一部分是由於工作平台的原因沒法使用C99特性,於是筆者將在應用C99特性之處明顯標出。之後還有C11。

語言標準特性也不能當作聖經來看。有的標準規定的特性沒什麼卵用,於是大家都沒實現。比如C++11有一個「對垃圾收集的微小資瓷」。

另外MSVC實現了部分C99特性,因為C99一方面是為了跟上C++98,實現的部分是指C++已經有的。另外C++有替代品的東西,微軟也懶得在C中實現。比如C++的std::vector與C99的變長數組。

還有一種標準說法是ANSI C,指的是C89,也有不少程序是拿ANSI C編寫的,比如Lua語言的解釋器就是100%的ANSI C,也意味著其代碼嚴格遵守ANSI C,而且沒有使用任何編譯器(全家桶)的擴展語法。除了語言標準規定的一些語法外,GCC、Clang、MSVC都有自己的一些擴展語法,當你編寫的程序不考慮移植到其他平台時,就可以盡情使用編譯器擴展語法。但本系列的示例代碼是跨平台的,所以會儘可能使用標準語法,擴展語法會被標出。

函數

原本「函數」(Function)是指輸入和輸出的對應關係,而C語言的函數實際上是「子程序」,子程序提供可選的輸入和輸出。C語言程序中,將會用到很多的函數。

「庫」(Library)就是函數的集合。

main函數

對於示例代碼中的main函數來說,是沒有輸入的,輸出是一個整數。函數體(函數的內部過程)用一對大括弧{}包裹。一般我們將輸入的數據稱為參數(parameter),輸出的數據稱為返回值(return value),下同。

main函數的地位更加特殊:任何C語言程序都將從main開始執行。換句話說,在C程序中可以沒有其他函數,而main函數是必須有的。至於為什麼從main函數執行,只是語言作者們的一個約定罷了,這個約定早在彙編語言時期就有。

在操作系統間有默認的約定:main函數返回0代表程序正常結束,否則代表運行過程中出現錯誤。

頭文件與庫函數

C語言設計了形式為「頭文件」(Header File, 擴展名為h)和「實現文件」(Implementation File,擴展名為c)的分離式編譯機制。現在看來,這個機制的問題比便利性要更突出,因此除了C++和Objective-C(這兩門語言都兼容C語言)也沒有哪門語言繼續用了。而導入頭文件是為了使用庫函數。

庫(Library)對於C語言就是函數的集合,屬於某個庫的函數叫庫函數。語言標準除了規定標準語法外,還規定了「標準庫」(Standard Library)。例如printf是一個庫函數,屬於標準庫stdio,為了使用printf函數,就必須引入stdio庫,則需要導入相應的頭文件。

本示例代碼中使用了一個庫函數printf(來自庫stdio),它是該程序核心功能的體現。printf函數輸入hello, world字元串,進行內部處理,將輸入的字元在控制台顯示,並返回一個整數(表示輸出的字元數,不過在本程序中被忽略了)。
是一個特殊的字元,表示換行(按下回車)。

庫的意義在於代碼重用,比如說你可以在不同的程序中使用printf函數,而不需要每次把printf函數的功能自己實現一遍。

C語言標準同樣規定了一批標準庫,比如stdio、stdlib、math等。

預處理

示例中涉及了判斷和宏,判斷詳見注釋。「預處理」(Preprocess)是編譯的上一步。其中的判斷部分,詳見注釋。

C語言中的「宏」(Macro)就是簡單的文本替換。比如例子中的

#define PAUSE system("pause");

就是把程序中出現的PAUSE替換成system("pause");。

C99中允許空白宏,空白宏被替換成空字元串。不能使用C99的讀者可以:

#define PAUSE ;

防止閃退

由於Visual Studio在運行控制台程序時將調用「命令提示符」,程序運行結束後,命令提示符便會一同退出。這樣一來就看不到程序輸出了(窗口出現又消失)。於是我們在程序完成輸出字元後傳入pause命令(藉助system函數),程序便會暫停並等待鍵盤輸入。藉此我們就能觀察程序輸出了。


推薦閱讀:

TAG:C編程語言 | C語言入門 | 編程入門 |