我們平時所說的程序,是指雙擊后就可以直接運行的程序,這樣的程序被稱為可執行程序(Executable Program)。在 Windows 下,可執行程序的后綴有 .exe 和 .com(其中 .exe 比較常見);在類 UNIX 系統(Linux、Mac OS 等)下,可執行程序沒有特定的后綴,系統根據文件的頭部信息來判斷是否是可執行程序。
可執行程序的內部是一系列計算機指令和數據的集合,它們都是二進制形式的,CPU 可以直接識別,毫無障礙;但是對于程序員,它們非常晦澀,難以記憶和使用。
例如,在屏幕上輸出“VIP會員”,C語言的寫法為:
puts("VIP會員");
二進制的寫法為:
你感受一下,直接使用二進制是不是想撞墻,是不是受到一噸重的傷害?
在計算機發展的初期,程序員就是使用這樣的二進制指令來編寫程序的,那個拓荒的年代還沒有編程語言。
直接使用二進制指令編程對程序員來說簡直是噩夢,尤其是當程序比較大的時候,不但編寫麻煩,需要頻繁查詢指令手冊,而且除錯會異常苦惱,要直接面對一堆二進制數據,讓人眼花繚亂。另外,用二進制指令編程步驟繁瑣,要考慮各種邊界情況和底層問題,開發效率十分低下。
這就倒逼程序員開發出了編程語言,提高自己的生產力,例如匯編、C語言、cpp、Java、py、Go語言等,都是在逐步提高開發效率。至此,編程終于不再是只有極客能做的事情了,不了解計算機的讀者經過一定的訓練也可以編寫出有模有樣的程序。
什么是編譯器
C語言代碼由固定的詞匯按照固定的格式組織起來,簡單直觀,程序員容易識別和理解,但是對于CPU,C語言代碼就是天書,根本不認識,CPU只認識幾百個二進制形式的指令。這就需要一個工具,將C語言代碼轉換成CPU能夠識別的二進制指令,也就是將代碼加工成 .exe 程序;這個工具是一個特殊的軟件,叫做編譯器(Compiler)。
編譯器能夠識別代碼中的詞匯、句子以及各種特定的格式,并將他們轉換成計算機能夠識別的二進制形式,這個過程稱為編譯(Compile)。
編譯也可以理解為“翻譯”,類似于將中文翻譯成英文、將英文翻譯成象形文字,它是一個復雜的過程,大致包括詞法分析、語法分析、語義分析、性能優化、生成可執行文件五個步驟,期間涉及到復雜的算法和硬件架構。對于學計算機或者軟件的大學生,“編譯原理”是一門專業課程,有興趣的讀者請自行閱讀《編譯原理》一書,這里我們不再展開講解。
注意:不了解編譯原理并不影響我們學習C語言,我也不建議初學者去鉆研編譯原理,貪多嚼不爛,不要把自己繞進去。
C語言的編譯器有很多種,不同的平臺下有不同的編譯器,例如:
- Windows 下常用的是微軟編譯器(cl.exr),它被集成在 Visual Studio 或 Visual C++ 中,一般不單獨使用;
- Linux 下常用的是 GUN 組織開發的?GCC,很多 Linux 發行版都自帶 GCC;
- Mac 下常用的是?LLVM/Clang,它被集成在 Xcode 中(Xcode 以前集成的是 GCC,后來由于 GCC 的不配合才改為 LLVM/Clang,LLVM/Clang 的性能比 GCC 更加強大)。
你的代碼語法正確與否,編譯器說了才算,我們學習C語言,從某種意義上說就是學習如何使用編譯器,讓編譯器生成可執行程序(例如 Windows 下的 .exe 程序)。
編譯器可以 100% 保證你的代碼從語法上講是正確的,因為哪怕有一點小小的錯誤,編譯也不能通過,編譯器會告訴你哪里錯了,便于你的更改。
什么是集成開發環境
實際開發中,除了編譯器是必須的工具,我們往往還需要很多其他輔助軟件,例如:
- 編輯器:用來編寫代碼,并且給代碼著色,以方便閱讀;
- 代碼提示器:輸入部分代碼,即可提示全部代碼,加速代碼的編寫過程;
- 調試器:觀察程序的每一個運行步驟,發現程序的邏輯錯誤;
- 項目管理工具:對程序涉及到的所有資源進行管理,包括源文件、圖片、視頻、第三方庫等;
- 漂亮的界面:各種按鈕、面板、菜單、窗口等控件整齊排布,操作更方便。
這些工具通常被打包在一起,統一發布和安裝,例如 Visual Studio、Dev C++、Xcode、Visual C++ 6.0、C-Free、Code::Blocks 等,它們統稱為集成開發環境(IDE,Integrated Development Environment)。
集成開發環境就是一系列開發工具的組合套裝。這就好比臺式機,一個臺式機的核心部件是主機,有了主機就能獨立工作了,但是我們在購買臺式機時,往往還要附帶上顯示器、鍵盤、鼠標、U盤、攝像頭等外圍設備,因為只有主機太不方便了,必須有外設才能玩的爽。
集成開發環境也是這個道理,只有編譯器不方便,所以還要增加其他的輔助工具。
1) 源文件(Source File)
在開發軟件的過程中,我們需要將編寫好的代碼(Code)保存到一個文件中,這樣代碼才不會丟失,才能夠被編譯器找到,才能最終變成可執行文件。這種用來保存代碼的文件就叫做源文件(Source File)。
每種編程語言的源文件都有特定的后綴,以方便被編譯器識別,被程序員理解。源文件后綴大都根據編程語言本身的名字來命名,例如:
C語言源文件的后綴是.c;
C++語言(C Plus Plus)源文件的后綴是.cpp;
Java?源文件的后綴是.java;
Python?源文件的后綴是.py;
JavaScript?源文件后置是.js。
源文件其實就是純文本文件,它的內部并沒有特殊格式,能證明這一結論的典型例子是:在 Windows 下用記事本程序新建一個文本文檔,并命名為demo.txt
,輸入一段C語言代碼并保存,然后將該文件強制重命名為demo.c
(后綴從.txt
變成了.c
),發現編譯器依然能夠正確識別其中的C語言代碼,并順利生成可執行文件。
源文件的后綴僅僅是為了表明該文件中保存的是某種語言的代碼(例如.c
文件中保存的是C語言代碼),這樣程序員更加容易區分,編譯器也更加容易識別,它并不會導致該文件的內部格式發生改變。
C++ 是站在C語言的肩膀上發展期來的,是在C語言的基礎上進行的擴展,C++ 包含了C語言的全部內容,將C語言代碼放在.cpp
文件中不會有錯,很多初學者都是這么做的,很多大學老師也是這么教的。但是,我還是強烈建議將C語言代碼放在.c
文件中,這樣能夠更加嚴格地遵循C語言的語法,也能夠更加清晰地了解C語言和C++的區別。
2) 工程/項目(Project)
一個真正的程序(也可以說軟件)往往包含多項功能,每一項功能都需要幾十行甚至幾千行、幾萬行的代碼來實現,如果我們將這些代碼都放到一個源文件中,那將會讓人崩潰,不但源文件打開速度極慢,代碼的編寫和維護也將變得非常困難。
在實際開發中,程序員都是將這些代碼分門別類地放到多個源文件中。除了這些成千上萬行的代碼,一個程序往往還要包含圖片、視頻、音頻、控件、庫(也可以說框架)等其它資源,它們也都是一個一個地文件。
為了有效地管理這些種類繁雜、數目眾多的文件,我們有理由把它們都放到一個目錄(文件夾)下,并且這個目錄下只存放與當前程序有關的資源。實際上 IDE 也是這么做的,它會為每一個程序都創建一個專門的目錄,將用到的所有文件都集中到這個目錄下,并對它們進行便捷的管理,比如重命名、刪除文件、編輯文件等。
這個為當前程序配備的專用文件夾,在 IDE 中也有一個專門的稱呼,叫做“Project”,翻譯過來就是“工程”或者“項目”。在 VC 6.0 下,這叫做一個“工程”,而在 VS 下,這又叫做一個“項目”,它們只是單詞“Project”的不同翻譯而已,實際上是一個概念。
3) 工程類型/項目類型
“程序”是一個比較寬泛的稱呼,它可以細分為很多種類,例如:
- 有的程序不帶界面,完全是“黑屏”的,只能輸入一些字符或者命令,稱為控制臺程序(Console Application),例如 Windows 下的 cmd.exe,Linux 或 Mac OS 下的終端(Terminal)。
- 有的程序帶界面,看起來很漂亮,能夠使用鼠標點擊,稱為GUI程序(Graphical User Interface Program),例如 QQ、迅雷、Chrome 等。
- 有的程序不單獨出現,而是作為其它程序的一個組成部分,普通用戶很難接觸到它們,例如靜態庫、動態庫等。
不同的程序對應不同的工程類型(項目類型),使用 IDE 時必須選擇正確的工程類型才能創建出我們想要的程序。換句話說,IDE 包含了多種工程類型,不同的工程類型會創建出不同的程序。
不同的工程類型本質上是對 IDE 中各個參數的不同設置;我們也可以創建一個空白的工程類型,然后自己去設置各種參數(不過一般不這樣做)。
控制臺程序對應的工程類型為“Win32控制臺程序(Win32 Console Application)”,GUI程序對應的工程類型為“Win32程序(Win32 Application)”。
控制臺程序是 DOS 時代的產物了,它沒有復雜的功能,沒有漂亮的界面,只能看到一些文字,雖然枯燥無趣,也不實用,但是它非常簡單,不受界面的干擾,所以適合入門,我強烈建議初學者從控制臺程序學起。等大家對編程掌握的比較熟練了,能編寫上百行的代碼了,再慢慢過渡到GUI程序。
4) 鏈接(Link)
上節我們講到,源代碼經過編譯(Compile)后就變成了可執行文件,其實這種說法有點籠統,甚至從嚴格意義上來講是錯誤的。源代碼要經過編譯(Compile)和鏈接(Link)兩個過程才能變成可執行文件。
編譯器一次只能編譯一個源文件,如果當前程序包含了多個源文件,那么就需要編譯多次。編譯器每次編譯的結果是產生一個中間文件(可以認為是一種臨時文件),而不是最終的可執行文件。中間文件已經非常接近可執行文件了,它們都是二進制格式,內部結構也非常相似。
將當前程序的所有中間文件以及系統庫(暫時可以理解為系統中的一些組件)組合在一起,才能形成最終的可執行文件,這個組合的過程就叫做鏈接(Link)。完成鏈接功能的軟件叫做鏈接器(Linker)。
如果程序只包含了一個源文件,是不是就不需要鏈接了呢?不是的!
經過編譯后程序雖然只有一個中間文件,不再需要和其它的中間文件組合了,但是這個唯一的中間文件還需要和系統庫組合,這個過程也是鏈接。也就是說,不管有多少個源文件,都必須經過編譯和鏈接兩個過程才能生成可執行文件。