gcc是一種C編譯器,這次我們根據書上的代碼嘗試着使用它。
使用之前,先補充前置知識。編譯器將源代碼轉換為可執行代碼的流程:首先,預處理器對源代碼進行處理,將#define指定的宏進行替換,將#include包含的文件插入,隨后,編譯器生成源文件對應的匯編代碼,以.s結尾。然后匯編器會將匯編代碼轉換為機器代碼,以.o結尾,最后,鏈接器將多個機器代碼(如果有多個的話)以及代碼中用到的庫函數(如printf)合並,產生可執行文件。
若要比較詳細地了解gcc常用參數,可以參考這篇文章:
https://www.cnblogs.com/zhangsir6/articles/2956798.html
里面講得比較詳細,當然,如果像我一樣想要以書為導向,看到不懂的再學,可以先不看那個,先繼續看下面的文章。
為了演示gcc的用法,書中演示了命令行 gcc -Og -o p p1.c p2.c的運行效果,我們需要做下實驗:
我使用的是MAC,直接打開終端就可輸入命令行來實驗了,要注意的是p1.c和p2.c需要自己生成,且這兩個文件必須位於當前目錄下。
首先用xcode新建個c程序,名為p1.c,里面敲入最簡單的代碼,如下:
同理,再創建個p2.c,隨后將兩個文件放到想要測試的目錄下,比如我在桌面新建了"測試gcc"文件夾:
打開終端,使用cd指令進入到此目錄下(暫時不會linux,先用最簡單的方法,打下cd,隨后拖動對應目錄進終端,會自動把路徑導入,如下圖)
按回車,進入此目錄:
先嘗試下
gcc -Og -o p p1.c p2.c 這條指令,會發現居然報錯了!結果如下:
因為p1.c和p2.c都有main函數,導致報錯了,好,那現在就把p2.c的主函數注釋掉吧,如下:
重新執行指令,效果如下:
生成了名為p的可執行文件,可以看到它沒有后綴,點開看看有什么驚喜:
看到那個p1.clogout沒?那是p1.c的main函數中輸出的東西。
至此,書上的代碼執行完畢,開始思考。
1.gcc的是區分大小寫的,這點自己試試就能發現。
2.-Og(注意,是大寫的字母O,不是數字0)代表要求gcc編譯器使用符合原始C代碼整體結構的機器代碼的優化等級,說白了就是編譯器在把源代碼變成機器語言時會作一定程度的優化,導致產生的機器代碼出現了變形。同樣的,也有-O1,-O2,-O3,-O4等等優化等級,數字越大優化等級越高。-Og是在gcc 4.8版本引入的,基本相當於沒有優化。
3.編譯時可以指定文件生成到流程的哪一步,比如-S用來指定生成匯編代碼(以.s結尾),-c可以指定生成到機器代碼(以.o結尾),若什么都不輸入,則默認生成到可執行文件,展示如下:
可以看到,生成的.s以及.o文件默認都是和源文件同名的,可執行文件則默認命名為a.out。
3.-o是用來指定目標名稱的,可以在指定名字的時候加上對應的后綴,以生成不同類型的文件,比如hello.exe,hello.asm等,如下圖所示:
這里有個疑問,若我不生成可執行文件,只生成.s或者.o文件,但不想用默認的p1.s作為名字,是否能通過-o改呢?另外,改的時候如果沒有用.s作后綴名,會怎樣呢?下面是結果:
可以看到,不僅僅是可執行文件,即使是.s文件,也是可以用-o的方式改名的。另外,雖然這個時候名字被改變了,乃至后綴都變了,比如那個test,但其實內容和直接生成的p1.s是一樣的,可以看到它們的大小都是相同的,用文本編輯器打開會發現內容相同。當然了,為什么test.s和p1.s大小不同就不懂了,試着打開了test.s,結果是亂碼。。。難道是生成了機器碼嘛?
當然,以上結果對.o文件應該也適用,簡便起見就沒做測試了。
4.我發現一個奇怪的現象,似乎gcc編譯器對語法的檢測是有限制的?
現象如下:我先把p1.c里的main函數改個名,如mafin,此時居然能生成.s和.o文件,直到生成可執行文件時才報出錯來:
按我以前了解的知識,編譯器在編譯的時候不應該就直接檢測語法錯誤了嗎?為什么生成.s時不報錯?難道以前是我記錯了,是在鏈接的時候檢測的?
我做了另一個實驗,把main改正常,並在main函數里隨便打了一串字符,結果如下:
這次倒是對了,生成.s文件的時候肯定進行了語法檢測,那只能說明main改成mafin不算語法錯誤?不可思議,我又嘗試把main函數注釋掉,加了個函數進去,結果和第一次一樣,如下:
.s和.o能直接生成,也是到了生成可執行文件才報錯。
好吧,我沒有耐心了,后面還有很多東西要看,姑且暫時認為main函數里出了錯不算語法錯誤吧,可能需要學完編譯原理才能解釋這個現象。
5.還記得當時測試書上的代碼時我們同時編譯了p1.c和p2.c嗎?如果按照那個方式寫,編譯器會分別對二者生成到.o並鏈接,最后生成可執行文件,顯然,有兩個main函數會報錯的,那如果我們不生成可執行文件,僅僅是同時生成.s和.o文件,在存在兩個main函數的情況下,會報錯嗎?結果如下:
答案是:不會報錯,能同時生成。
那么如果同時對兩個文件生成.s,且要同時分別命名是否可以呢?結果如下:
根據提示來看,是不支持的,不管你是用gcc -S -o test1 p1.c p2.c還是妄圖施展gcc -o test1 -S p1.c -o test2 -S p2.c這樣的騷套路,統統都是行不通的。。
當然,gcc的命令參數很多很多,這兒僅僅是基於書上的內容作的一些測試和拓展,想要了解更多請看開頭的鏈接,讓我們愉快地結束這一節吧。
(有疑惑的,請看博客的“寫在前面”一章)