1.5 第一個C語言程序
經過前面一系列的不懈努力,終于可以編寫第一個C語言程序了,是不是有點激動?嘿嘿,作為有著豐富經驗的我(有點倚老賣老啊),未雨綢繆、高瞻遠矚,首先想到了本書中會有很多的代碼,為了便于對這些代碼進行管理,我決定采用三級目錄管理方式:首先在D盤下建立名稱為“大話C語言代碼”的總文件夾;然后在總文件夾下面為每一章建立一個對應名稱的章文件夾;再接著在各個章文件夾中建立具體的項目文件夾。例如,現在要編寫第一個C語言程序,我就在“D:\第一章\”下面建立了一個名稱為“第一個C語言程序”的項目文件夾。然后進入該項目文件夾,并以此為工作目錄,新建一個文本文件“first.c”,注意文件名后綴為.c,表明該文件為C語言的源文件(C標準的規定)。文件建好后,右擊該文件,在彈出的快捷菜單中選擇“Edit with Notepad++”命令,使用Notepad++來打開文件。現在就可以在文件里編寫代碼了,具體內容如下。

代碼輸入完成后保存,然后對這個源文件進行編譯。可以直接在當前工作目錄的路徑欄里輸入“CMD”并按下Enter鍵,這樣就可以打開控制臺窗口,并以當前目錄作為工作目錄,如圖1.17所示。

圖1.17 在當前目錄啟動控制臺窗口
在控制臺窗口中輸入編譯命令“gcc first.c”,就會使用GCC編譯器來對“first.c”這個源文件進行編譯,最終生成一個名為“a.exe”的可執行文件。如果想得到一個指定名字的可執行文件,如“first.exe”,則把之前的編譯命令改為“gcc first.c -o first.exe”即可,也就是在編譯命令中加入“-o”選項來指定輸出文件的名字。
最終生成的這個可執行文件,可以直接用鼠標雙擊執行,也可以在控制臺窗口中執行。不過直接執行的話,可能會出現窗口一閃而過的景象,導致用戶看不到運行的結果。但這并不是程序的問題,而是現在的計算機執行速度太快。當程序執行完畢后,系統會自動關閉窗口。看不到運行結果怎么辦?有很多解決辦法,最簡單的一種就是讓程序在控制臺窗口中運行,這樣即使程序執行完畢,這個控制臺窗口也不會消失,用戶就有足夠的時間來觀察程序運行的結果,控制臺窗口如圖1.18所示。
如果在窗口中看到的是一串亂碼,別擔心,這只是因為代碼字符和控制臺窗口的字符編碼不一致造成的,只要重新進行字符編碼就可以了。具體步驟為用Notepad++打開源文件“first.c”,選擇“編碼”菜單,執行“轉為ANSI編碼”菜單項,再保存文件,重新編譯即可。這時再重新運行程序,就不會仍然是一串亂碼。

圖1.18 編譯、執行和查看運行結果
是不是有點成就感了?經過輸入簡單的幾行代碼,就能通過編譯器生成一個可執行文件,通過執行這個可執行文件,就會得到一個程序的運行結果。但為什么要輸入這些代碼?這些代碼到底是什么意思?我能隨便改里面的代碼嗎?編譯器是怎么編譯的?下面就來解釋一下這些代碼的作用和意義。
1.5.1 C語言的代碼注釋
想必大家在學習時有過這樣的經歷:學習文言文時,遇到看不到的句子,就在旁邊加上白話文來說明,學習英文時,遇到不會讀的單詞就用音近的中文文字來標注,學習課文時,每一段都會寫出段落大意,并能說出整篇文章的中心思想。
類似地,C語言的代碼注釋的作用也是如此。A同學寫出的程序代碼,B同學拿過來可能就看不懂,甚至時間久了,A同學自己也理解不了。“好記性不如爛筆頭”,如果在代碼里加上恰當的注釋,對于代碼交流就相對容易多了,而且即使過了很長的時間,看到注釋也自然能回想到當初為何這么寫代碼。所以,代碼注釋就是為了更好地理解代碼,方便相互交流的一種文字說明。C語言中的代碼注釋方式有兩種:塊注釋和行注釋。
1.塊注釋
“第一個C程序”中的首行就是一個塊注釋,它是用“/*”和“*/”所包含的一段(一行或多行)文字。塊注釋非常靈活,可以出現在程序代碼的任何部分,甚至在某一條語句之中。但塊注釋不允許被嵌套使用,即不能在一個塊注釋中又出現另一個塊注釋。例如“/*aa/*bb*/cc*/”,塊注釋是從“/*”開始,到遇見第一個“*/”結束,所以真正的塊注釋部分只有“/*aa/*bb*/”,后面的“cc*/”就不是塊注釋的部分了。
2.行注釋
C語言程序代碼中,以“//”開頭的部分稱為行注釋。顧名思義,行注釋只能注釋一行,從“//”開始,直到該行的末尾,都為注釋的部分。如果要使用行注釋來注釋多行,就得在每一行的行首都使用“//”。
最后,還需要強調兩點:①注釋只是給人看的,并不會參與編譯,也就是說在真正編譯的時候這些注釋都會被忽略掉。②錯誤的注釋比沒有注釋更糟糕,注釋的內容要做到表述清晰、文詞達意,若代碼被修改,注釋也要及時更新。
1.5.2 文件包含
“第一個C程序”中的第二行是一條預處理語句,關于預處理我們會在后面的章節中學習,現在只要簡單理解一下該條語句的功能就可以。“#include <stdio.h>”的作用是將一個名字叫“stdio.h”的標準庫頭文件包含到當前源文件中。為什么要這樣做?現在只能說,因為在后面要用到一個標準庫函數printf,也就是說,如果在程序中需要用到標準庫里的函數,就需要包含相應的標準庫頭文件,而且在后面的程序案例中大多都會用上這個“stdio.h”頭文件,因為在這個頭文件中,除了printf外,還聲明了許多其他的標準輸入、輸出、文件等相關的函數。同樣地,函數也將在后面的章節中來詳細學習,因為對于初學者,如果一時接納的新知識太多,而又不可能在短時間內把這些知識都理解通透,就很容易讓自己迷茫,這樣的學習效果并不好,會失去對學習的熱情和信心。分清主次、循序漸進,以這樣的方式和心態來學習,才能成功。
1.5.3 main函數
雖然把函數放到了后面的章節中來學習,但現在依然不可避免地撞上了它。還是老辦法,先簡單地認識一下就行。函數是為滿足某些特殊功能而構建的子程序,例如上面的例子里所使用的printf函數“printf("第一個C程序!\n");”,它的功能就是把字符串“第一個C程序!\n”輸出到屏幕上。在C語言中用兩個“"”包含起來的文字稱為字符串,而字符串中最后的那個“\n”的作用就是換行,也就是在輸出“第一個C程序!”后進行一個換行的操作。當然printf函數的功能不僅如此,我們會在后面詳細學習。現在先來看一下main函數,上面的例子中,除了第一行的注釋和第二行的文件包含,剩下的就是main函數了,我們更習慣稱它為主函數或者入口函數。因為C語言規定,在C程序代碼中,必須有一個main函數。也就是C程序中可以沒有其他的函數,但必須要有main函數(又稱為主函數)。程序一運行,系統就會到程序中查找main函數,然后開始執行它,也就是程序總是從main函數開始執行的,所以又稱之為入口函數。
在main函數中(位于一對大括號之間)有兩行代碼:第一行調用printf函數來輸出字符串,第二行使用return語句返回一個0值。C語言規定,main函數在執行完畢需要返回一個整數值,用這個值可以來檢查程序的退出狀態。若返回的是0值,表示程序執行成功正常退出,返回非0值表示程序是非正常退出的。
1.5.4 C程序編譯流程
由上面的例子可知,只要事先寫好一個“.c”后綴的源文件,然后使用“gcc命令”進行編譯,就可以得到一個“.exe”后綴的可執行的C程序文件。但這期間并非只有一道工序,而是分別經過了預處理、編譯、匯編和鏈接四個流程,如圖1.19所示。

圖1.19 C程序的編譯流程
1.預處理
在源文件被編譯之前,首先要進行預處理的工作,也就是對源代碼進行相應的展開、替換和清理。在本例中,預處理工作有兩項:①把代碼注釋部分去掉,不讓其參與編譯;②把“stdio.h”文件包含進來,即用“stdio.h”中的內容替換在“#include <stdio.h>”位置。
2.編譯
源文件被預處理之后,再以字符流的形式進行處理,進行詞法和語法的分析,然后通過匯編器將源代碼指令轉變成匯編指令,生成相應的匯編文件。
3.匯編
匯編是指把匯編語言代碼翻譯成目標機器指令的過程,也就是把匯編碼轉換成機器所能識別的二進制碼,通常把經過匯編之后生成的文件稱為目標文件。
4.鏈接
經過匯編之后生成的目標文件并不能立即被執行,還需要由鏈接器將代碼在執行過程中用到的其他目標代碼及庫文件進行鏈接,最終生成一個可執行程序。例如本例中用到了printf函數,就需要找到包含該函數的標準庫文件,對它進行鏈接。
1.5.5 C語言調試
大家有沒有想過,在以后編寫程序代碼時可能會出現這樣那樣的錯誤,這是肯定的,雖然像是給大家潑冷水,但這是不可回避的問題。不可回避就勇敢接受吧。
從錯誤出現的時間上可分為兩類:編譯時錯誤和運行時錯誤。所謂編譯時錯誤就是在寫好源文件后,在進行編譯時出現的錯誤,主要分為語法錯誤和鏈接錯誤兩種。語法錯誤比較明顯,解決也相對簡單,編譯器通常會給出錯誤的代碼位置,程序員根據提示位置到代碼中進行修改就行了。而鏈接錯誤通常是由于所需的庫文件找不到或函數沒有具體的實現等原因造成的。另一種是運行時錯誤,也就是編譯可以順利通過,但程序運行時出現了錯誤。例如程序崩潰和異常退出,程序運行造成死機,程序運行的結果不正確等。這類錯誤處理起來相對棘手,通常需要對程序進行反復的調試,才能找到問題所在。所以程序的調試主要是針對運行時錯誤而言的。那下面就來看看有哪些調試方式吧。
1.人工調試
人工調試就是通過人的眼看、腦算、手記等方法對程序代碼進行調試的過程。通常采取逐行跟蹤追查的辦法,用人腦來推算、模擬出程序的運行軌跡,直至找出問題位置。這種調試方式適合代碼量少、結構簡單的程序。
2.代碼調試
代碼調試通常采取在程序不同位置設置一些特定條件或可輸出信息的程序代碼,然后根據特定條件的執行情況或輸出信息來判斷、尋找錯誤的大概位置。這種調試方式適合于代碼量大、結構復雜的程序代碼,但同時要求調試者有較強的邏輯判斷能力。
3.工具調試
工具調試采用功能強大的調試工具來對程序進行跟蹤調試,這些調試工具通常都具有代碼跟蹤、斷點設置、內存查詢、變量監視等功能,可以通過一步一步地執行代碼來觀察程序的運行狀態,極大地方便了對程序的調試。
其實在1.4節安裝的MinGW里包含了一套工具。除了GCC編譯器外,還包含一個名為GDB的調試工具,使用GDB可以很方便地配合GCC進行程序的調試。不過想要使用GDB來調試程序,在使用GCC編譯源文件時需要多加一個選項“-g”,即完整的編譯命令為“gcc -g first.c -o first.exe”。“-g”選項的作用是讓編譯后的程序里包含相關的調試信息,這樣才能讓GDB調試工具發揮作用。
在控制臺窗口輸入命令“gdb first.exe”,這樣就進入程序調試界面了,如圖1.20所示。

圖1.20 使用GDB調試工具
現在就可以在“〈gdb〉”提示符后面輸入相應的調試命令來對程序進行調試了。例如“list”或其簡寫“l”可以顯示當前的源代碼,也可以用“list 1,5”或“l 1,5”來顯示源代碼中的1~5行;使用“break 3”或“b 3”在第3行代碼處設置斷點,然后使用“run”或“r”來運行程序,程序將啟動并暫停在斷點處,然后不斷使用“step”或“s”來一步一步地執行程序,還可使用“continue”讓程序運行到下一個斷點處;使用“quit”或“q”可退出調試。一些常用的調試命令如表1.1所示。
表1.1 gdb調試命令

- OpenDaylight Cookbook
- JavaScript+DHTML語法與范例詳解詞典
- Docker進階與實戰
- Visual FoxPro程序設計教程
- Java Web及其框架技術
- Mastering Android Development with Kotlin
- Bootstrap 4 Cookbook
- CoffeeScript Application Development Cookbook
- Building Microservices with .NET Core
- Spring MVC+MyBatis開發從入門到項目實踐(超值版)
- R Data Science Essentials
- Java Web開發實例大全(基礎卷) (軟件工程師開發大系)
- 監控的藝術:云原生時代的監控框架
- 青少年學Python(第2冊)
- 啊哈C語言!:邏輯的挑戰(修訂版)