一. 研究過程
1.第一章:創建編譯環境:
我們首先下載TC2.0,找到其中與編譯連接相關的程序和文件:
(1) 編譯器:TCC.exe
(2) 連接器:tllike.exe
(3) 相關文件:c0s.obj、cs.lib、emu.lib、maths.lib
將文件放在C:\C目錄下。
編寫程序測試我們的編譯環境:
在這裡我們看到,程序被正常的編譯。生成了.exe文件。並且可以正確執行。
當然,在TC中,c0s.obj、cs.lib、emu.lib、maths.lib這四個文件時在TC目錄下的lib文件夾下,但是我們如果將lib文件夾直接放入C:\C目錄下,程序在編譯的時候會提示:
C0s.obj:Unable to open file
這是因為,TCC沒有找到lib目錄下的c0s.obj文件,我們可以推知,TCC默認在尋找文件的時候只在自己同層的目錄下尋找。
在這裡我們發現,我們只用了TCC,並沒有用到TLINK。那TLINK的作用是什麼呢?
我們刪除TLINK。然後進行編譯連接的工作。我們看到:
我們看到,TLINK其實是被TCC調用實現功能的。
書中的解釋是:TCC.EXE 將a.c編譯成a.obj
TCC調用TLINK將c0s.obj、cs.lib、emu.lib、maths.lib中的相關代碼連接到一起生成.exe文件。
在剛才的步驟中,雖然我們沒有生成.exe,但是我們發現,生成了1.obj。那麼,我們把TLINK重新找回來,能不能將這個1.obj連接成.exe呢?
我們嘗試:
我們發現我們成功的連接完成。
當我們用TCC編譯時,程序可有兩個最大為64K的段,一個段為代碼段,棧和數據段共用一個段。我們如何來驗證這一點呢?(注:這裡其實是用到了後面的內容)。我們編寫這樣一個程序,讓其顯示程序運行時CS和SS,DS的值。
我們編譯運行,查看結果:
我們發現,CS是一個值,DS,SS兩者值相等。這樣也就驗證了代碼段為一個段,棧和數據段共用一個段。而我們又知道,每個段地址不變,偏移地址從0000-ffff是64K的字節。所以這兩個段的最大值是64K。
另外,我們在CMD中直接輸入TCC,會顯示出TCC的使用參數,如下:
2.第二章:顯示函數的段地址和偏移地址:
我們繼續研究第二章的內容:
在main函數中添加語句,使下面的程序可以打印出所有函數的段地址和偏移地址。
程序如下:
我們最直接的想法是用取地址的方式來查看。我們知道,在C語言中,&的作用是取地址。比如:
運行後結果如下:
那麼,函數是不是也可以這樣來取地址呢?我們嘗試:
在這裡,我們直接加類似&f1這樣的取地址加函數名的形式可以麼?我們分析:在debug中,我們看到子程序調用是都是執行的CALL(地址)的方式。在這裡,函數名和標號有著類似的作用,就是方便編程人員編程、方便編譯器編譯和鏈接。他的本質應該是一個地址值。
我們直接編譯看看是否會報錯,證實我們的猜想。
我們發現沒有報錯。也就說明這裡的函數名確實被翻譯標號或與標號類似的東西。
為了方便查看,我們讓這些地址以16進制的方式顯示出來。結果如下:
那麼我們所找到的值是不是函數的入口地址呢?我們進入debug查看:
我們看到,在01fa中,是我們定義的F1函數中的語句,int a=1;也就是說我們的想法是正確的,在printf中直接取函數的地址輸出是可以的。
但是,我們看到,我們打印出的是函數的偏移地址,段地址如何打印呢?
我們知道,在C語言中,我們可以直接調用一些匯編的寄存器,而在匯編中,CS寄存器記錄的是程序段的段地址,也就是說,我們只要顯示出CS,就可以顯示出程序段的段地址。
我們編寫:
運行後其結果如下:
但是這個結果對不對呢?我們還得從debug中驗證:
我們看到,在Debug中-g運行後的結果與CS的值相同。都是0b3b。這也就說明了程序所顯示就是程序在執行時的程序段地址。
那麼這兩次執行2.exe所顯示的程序段地址為什麼會不同呢?我們知道,第一次我們是直接在cmd中運行的2.exe,程序接受系統調用自己執行。而第二次我們是用debug加載進入系統,然後執行。在debug加載的時候,程序被debug加載到了指定的程序段位置。
二. 附加研究:
在進行研究時,我發現在C語言顯示變量的偏移地址時,不同的變量顯示的地址值是不相同的。
比如,全局變量顯示如下:
局部變量顯示如下:
這個結果讓我疑惑,但是我想起了局部變量和全局變量的區別,又看到了-32這樣的值,我猜想這應該是局部變量記載的是相對位置而不是絕對位置。因為絕對位置不會出現負數。我編寫程序如下:
在debug中,我-g到第二個printf();函數前,也就是顯示出變量a的地址後。
通過當前的SS:SP的值與-28進行計算,查看結果單元,發現:
這個單元內所存放的數就是變量a的值,也就是說這個單元就是變量a的存儲單元。
所以得出結論:在C語言中,全局變量的地址是記錄其存儲單元的偏移地址,而局部變量的地址是記錄其存儲單元與現在棧頂指針的相對位置。
(注解,重要:在這裡後期我在回頭看的時候,發現這裡使用的是%d方式,這很重要。因為地址是FFE4,這是一個十六進制的數,本應該用%f的方式顯示出來。而正是兩種數不同的表達和顯示方式造成了這樣的現象。而非是上面的結論。這裡為了過程的完整而保留了這個問題。在此改正。)
C語言之父的作品 <<The C Programming Language>> 一定要看
再就是 《C專家編程》《C指針》《C陷阱與缺陷》這些都非常經典
這些看了,就絕對是大牛了!
大一的話,先看第一本就行了
The C programming Language是入門的經典,因為Dennis M. Ritchie是c的開發者之一。
不過你要有一定的c基礎,最好是以前有c的基礎,不然不要看。
該書沒有什麼廢話,用實例來介紹c的一些特性。並介紹了一些常規算法。
而且非常薄除去附錄才150頁。可以多看幾遍,我沒看一次都有不同的收獲,它還有配套的習題解答。
那上面的練習都上機編一下,你c基本上就沒什麼問題了,就該去看算法和數據結構了。
編程時候一定要嚴謹,可以參考下各種風格然後選一種貫徹。
後面書基本的各有側重,等你人門以後自然知道自己需要看哪本