[C 語言] 程式設計教學:基本概念

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    前言

    在本文中,我們以 Hello World 為例,說明 C 語言的基本概念。

    第一個範例程式

    以下是加上註解的 Hello World 程式:

    /* Include the library for standard input and output. */
    #include <stdio.h>
    
    /* Entry to the main program. */
    int main(void)
    {
        /* Print some string to the console. */
        printf("Hello World\n");
    
        /* Exit the program successfully. */
        return 0;
    }
    

    我們會以這個程式為基準,說明 C 語言的基本概念。

    編譯 C 程式的步驟

    表面上,編譯 C 程式只是一行指令或一個 IDE 的按鈕就解決的事。但編譯 C 程式實際上分為四個步驟:

    • 前處理 (Preprocessing)
    • 編譯 (Compilation)
    • 組譯 (Assembly)
    • 連結 (Linking)

    前處理將含巨集 (macro) 的 C 程式碼轉換成沒有巨集的 C 程式碼。嚴格上來說,前處理不算編譯的一部分,只是經由一連串字串代換的動作改寫原始碼而已。我們會在後文介紹 C 巨集。

    編譯 (compilation) 是整個編譯 (building) 的前半段。在這個階段,C 編譯器會將 C 原始碼轉換成等效的組合語言原始碼。由此可知,C 仍然是高階語言,只是寫起來不若現代語言那麼方便。

    組譯會將組合語言原始碼轉換成機械碼。轉換後的檔案為目的檔 (object files)。目的檔只是編譯的中間產物,無法使用。

    最後,透過連結,將目的檔轉為執行檔 (executable)。執行檔即為可使用的電腦程式 (program)。Unix 或類 Unix 系統的執行檔不加副檔名,Windows 系統的執行檔的副檔名為 .exe

    剛開始練習的程式很簡短,只會用到標準函式庫的函式,所以編譯的步驟較簡單。隨著程式的規模上升,會使用模組化的方式將程式碼分散在多個檔案中,也有可能會用到外部函式庫,這時候編譯的步驟就會變複雜。

    空白 (Spaces)、縮進 (Indentations)、排版 (Code Formatting)

    C 語言對於空白、縮進等版面安排相當自由,並沒有 Python 那種強制縮進的規則。甚至還有故意寫出難以閱讀的 C 程式碼的比賽,像是 IOCCC (The International Obfuscated C Code Contest)。但我們仍然鼓勵讀者在寫 C 程式碼時排版程式碼,或是使用程式碼自動重排軟體。因為整齊的程式碼對於日後維護會有所幫助。

    註解 (Comments)

    程式碼中的註解不會執行,所以可寫入一般文字。C 語言有兩種風格的註解。ANSI C 是使用一對 /**/ 把註解文字包起來,像是以下範例:

    /* Some comment. */
    

    由於傳統的註解能夠跨越多行,故未完全淘汰。

    C99 後,引入單行註解。單行註解以 // 開頭,之後同一行內的所有文字皆視為註解。像是以下範例:

    // Some single-line comment.
    

    單行註解比較易寫,但無法跨越多行,所以未取代傳統的註解。

    除了這兩種正統的註解外,還有另一種利用巨集來註解掉整段程式碼的手法:

    #if 0
        printf("It won't compile\n");
    #endif
    

    這樣操作的原理在於巨集會在編譯前就執行,相關程式碼會被抹去,等同於這段程式碼不存在。

    註解的用途是記錄程式人撰寫程式碼時的意圖或想法。對於教學用的程式,註解可放入教學用文字。

    有時候我們會用註解暫時隱藏掉一段程式碼,防止該段程式碼編譯及運作。例如,我們在除錯時,利用註解遮蔽掉可能有問題的程式碼,然後逐步縮小註解的範圍,直到找到錯誤為止。

    引入函式庫

    C 語言的語法刻意保持精簡,大部分的功能由函式庫來實現。甚至標準輸出入這種基本的功能也是藉由函式庫來實現。所以,學 C 語法不會花太久時間,但要寫到熟練則需要反覆練習。

    引入函式庫的語法是 #include。該語法是一種巨集 (macro),但我們一開始不需要在意巨集的寫法,因為 #include 敘述的寫法是固定的。

    引入函式庫時,有兩種寫法。一種是以一對角括號 <> 包住函式庫名稱。一種則是用一對分號 "" 包住函式庫名稱。C 語言沒有強制要使用那一種方式。

    一般來說,使用標準函式庫或外部函式庫時,使用角括號:

    #include <stdlib.h>
    

    使用內部函式庫時,則使用分號:

    #include "mylib.h"
    

    但這只是一種風格上的建議,非強制事項,故僅供參考。

    主函式 (Main Function)

    主函式是 C 程式的起始點,一般的 C 語言皆有主函式。但在嵌入式系統或是作業系統本身的 C 程式碼中,主函式則不是必需的。

    如果該 C 程式不需要接收命令列參數,則使用以下方式來寫:

    int main(void)
    {
        /* Implement your code here. */
    }
    

    若該 C 程式需要接收命令列參數,則改用以下方式來寫:

    int main(int argc, char *argv[])
    {
        /* Implement your code here. */
    }
    

    除了命令列參數外,如果想要接收環境變數,可以用以下方式改寫:

    int main(int argc, char *argv[], char *env[])
    {
        /* Implement your code here. */
    }
    

    現在這種寫法比較少見,因為標準 C 可用 getenv() 函式取得環境變數,沒有必要再用這種寫法。

    至於以下寫法是錯誤的:

    void main(void)
    {
        /* DON'T USE THIS IN PRODUCTION CODE. */
    }
    

    雖然 Visual C++ 可編譯通過,但這種寫法並非標準 C 的一部分,故不建議使用。

    表達式 (Expression) 和敘述 (Statement)

    表達式會回傳值,像是 "Hello World\n" 是表達式,該表達式是 "Hello World" 字串再附帶一個換行符號。而敘述代表單一指令,像是 printf("Hello World\n"); 是一條敘述。C 語言的單行敘述會用 ; 結尾。

    C 程式由一條條敘述組成,預設情形下,程式會由上往下依序執行敘述。但我們有許多改變程式行進流程的方式,像是控制結構或函式呼叫等。

    離開狀態 (Exit Status)

    我們在程式結束時回傳 0

    return 0;
    

    這代表程式正常結束。

    C 程式在結束時,會向系統回傳一個整數,用來代表程式的狀態。一般來說,回傳 0 代表程式正常結束,回傳非零值 1 代表程式異常結束。

    有些程式會用不同的回傳值表示不同的異常狀態,但除了回傳 0 表示正常結束以及回傳 1 表示異常結束外,目前 C 語言對其他的回傳值沒有共識。也就是說,用回傳值來判斷程式狀態不是很牢靠,我們也不鼓勵讀者使用複雜的回傳值來表示程式的離開狀態。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    臉書討論區
    標籤: C 語言, 編譯