本篇研究TC2.0下其他幾個工具。同時看看TC由源代碼到exe程序的過程。
1. 用TCC將下面的程序編為.obj文件
我們知道,TCC在默認的編譯連接一個C語言的源程序a.c的時候分為以下兩步:
(1).TCC將源程序文件編譯成a.obj。
(2).TCC調用TLINK將c0s.obj,cs.lib,emu.lib,maths.lib中的a.obj中的程序要用到的代碼與a.obj的代連接到一起生成.exe文件。
並且,我們還知道,TCC可選參數有如下:
我們看到有這樣的選項:Compile only(只編譯)。這應該是只編譯生成.obj文件的選項。我們驗證如下:
我們看到,文件夾下確實只生成了.obj文件:
2.用C:\C\tcc a.c的方法編譯下面程序:
我們看源程序,很明顯的沒有定義和實現f函數。我們看編譯連接過程中的錯誤提示:
如我們所料,這裡出現了錯誤提示:在a.c中沒有發現f函數。
我們看錯誤提示,在a.c中沒有發現f函數。我們很容易想到,這裡是不是不止從a.c中尋找f函數?或者這裡是不是可以不止從a.c中尋找?我們又聯想到TCC函數有同時編譯連接多個.c文件的選項。我們嘗試編寫兩個文件,並共同編譯:
編譯連接過程:
我們看到並沒有錯誤提示。並且在文件夾下生成了A.exe。我們運行查看:
我們看到,將同一個程序寫在兩個文件中,程序也可以正常的編譯連接。
3.TLIB.exe
我們從書中看到,TC2.0給我們提供了一個工具tlib.exe,可以用tlib.exe將一個.obj文件中的代碼加到一個.lib文件中。
首先我們需要了解TLIB.exe的參數。我們仿照TCC的方法,在cmd中執行TLIB。其參數表如下:
我們看到其參數,+是將一個文件添加進lib文件中。我們嘗試:
我們看其屬性,他確實被修改了。
然後我們嘗試再次編譯a.c。發現沒有了錯誤提示:
我們加載進debug,找到程序的main函數和f函數的代碼:
我們看到main函數中只有一句調用的call指令,根據我們代碼的對比,我們知道這裡調用的就是f函數。我們查看f函數的代碼:
我們運行:發現其實a.c調用的f()函數就是4_1.c中的f函數。
回顧我們整個過程,我們可以得出這樣的結論,雖然我們沒有寫函數f,但是a.exe中函數f的代碼在連接的過程中從cs.lib中得到。
4.將下面的程序編譯為f.obj,將f.obj加入c:\c\cs.lib。
程序f.c如下:
下面的程序編譯連接為b.exe。
我們編譯完成後進入debug查看。主函數中代碼如下:
看到,在函數調用的部分,都采用了call的方式,並且call的位置離主函數的比較遠。也就是說,call部分的函數沒有和main連續。我們轉跳到其調用的部分。查看其代碼。
我們對應f.c查看,我們可以看到,其實這三個子程序就是f1,f2,f3函數實現的語句。並且,雖然在b中沒有調用f3,f3的代碼也在b中。
很明顯,經過修改cs.lib,我們的程序可以調用f1,f2,這就說明了,b.exe中的代碼是在連接的時候從cs.lib中加入的。
那麼,是因為這三個函數在同一個文件中被加入cs.lib中,所以才出現這樣同時都被加載進入的結果麼?
我們把f3單獨拿出來,加入cs.lib(在此過程中,把cs.lib還原成tc2.0自帶的cs.lib)
這時我們在編譯連接b.c,然後進入debug加載:
我們看到,這裡沒有了f3。那麼是不是被放在了其他地方呢?我們看看調用f3的時候它應該再什麼地方。
我們修改一下b.c,使他調用一下f3。
進入debug加載查看。
我們看到,f3實現的子函數就在f1,f2的後面。也就是有如下事實:如果f3被調用,那麼他的地址就是在f2後面。如果f2後面沒有f3,也就是f3沒有被調用。這樣,就否定了f3在被放在了其他地方的推論。也進而說明了,分別編譯,分別加入cs.lib這個方案是可以實現用到哪個函數,裝入哪個函數的代碼的。
5.替換printf
用TLIB.EXE將cs.lib中的printf函數代碼變為下面程序的代碼:
通過前面的幾個程序我們知道,cs.lib中也有printf函數對應的代碼。那麼我們就不能像前面的幾個程序那樣添加.obj進入cs.lib中了。而應該是替換。我們看TLIB.EXE的參數表,發現-+是替換的。我們嘗試:
發現無錯誤提示。我們編寫一個程序如下,測試printf是否被替換成功:
編譯連接後執行:
這裡證實,printf確實被替換成了我們自己編寫的printf。
6.思考:
TCC這樣做有什麼好處呢?首先我們知道,在cs.lib中包含常用的一部分函數,可以使得TCC在編譯基本功能的程序時,不需要在包含頭文件。其次,我們知道,一般情況下,同一個文件中的函數之間一般是相互關聯的。有些是相互調用實現一個共同的功能,有些是實現不同的功能但是是對同一類數據操作的。這些函數寫在同一個文件中,包含的時候同時包含進去,這樣就減少了由於沒有包含而出現錯誤的情況。另外,可以自定義的替換其中的函數,保證了程序的多樣性和能夠方便的修改的特性。