研究實驗4
研究過程:
問題引出:C語言編程非得用主函數main嗎,不用是否可以?
對此問題進行研究,用tc.exe書寫代碼如下:
圖1 沒有main函數的c程序
對其進行編譯,鏈接發現,編譯階段可以完成,但是鏈接階段無法完成。即無法通過這種方式生成.exe文件。並顯示錯誤信息:
圖2 f()程序提示錯誤信息
錯誤信息提示沒有定義在c0s中沒有定義main。
用link.exe對其生成f.exe文件,查看其匯編代碼如下:
圖3 f.c對應匯編代碼
從圖中可以看出來,f()的偏移地址是0,這個與main函數不同,使用g命令
G 1d發現無法返回,且DOS卡死!f.exe的代碼共1Dh(29)個字節。
圖4 m.c匯編代碼
可以發現,m.exe程序比f.exe程序多一條ret語句,即m.exe有30個字節,且程序能夠正常返回,且程序執行完076a:0217處的指令就返回。
由此可見,main函數與f.exe首先在偏移地址上就不同f.exe偏移地址是0000h,main函數偏移地址是01fah,其次main函數比f.exe多一條ret指令,使得程序能夠正常返回
問,添加了這樣一條語句,為什么無法執行,且還會導致dos環境崩潰???!!!!!
問題2:main函數結尾的兩個ret分別具有什么含義?
圖5 探究ret追蹤調用main 函數的地址
易知,call指令占用三個字節,當單步執行ret的時候,我們發現,ip變成了011d,基於之前學習對於cs:ip的理解,我們斷定,“call main”的偏移地址為011a,結果如下圖:
圖6 調用main函數指令地址
由圖可以發現,偏移地址確實是地址確實是011a。
下面對c0s進行研究:
將c0s.obj生成.exe文件,並用debug加載,查看匯編代碼:如下
圖7-8 c0s.exe開頭對應匯編代碼
圖9-10 m.exe開頭對應匯編代碼
通過觀察發現,開頭處的匯編語句,出了第一句對DX的賦值不同以外,其余都是相同的。
圖11 m.exe調用main處匯編代碼
圖12 c0s.exe相同偏移處代碼
從圖7-圖12我們可以發現,c0s.exe和我們任意的一個m.exe程序很多內容都是相同的。通過圖中第一處紅線我們還可以發現,main是c0s.exe的一個實參,即調用main函數的是c0s.exe這個程序!!還可以發現,其余存在很多相同的內容,應該是資源配置的初始化,總之,在這里,我們得到的最重要一條結論就是:main函數被c0s.0bj文件調用!!!隨着以后編譯環境的改變,或者C語言學習的深入,我們仍然要注意這個本質性問題,雖然對於我們學習語言本身沒多大幫助,但對於理解其工作機制卻很有幫助!
通過上述,我們知道,main函數只不過是一個入口參數而已,並不具有特殊性。因此,我們想辦法看能不能不用main函數編程,首先我們重寫一個c0s.asm程序,對其編譯,替換原c0s.obj。重新編譯連接f.c,發現可以成功對f.c編譯連接!!我們直接運行f.exe ,發現程序可以正常運行並返回!如下圖
圖13 得以正常運行的f.exe
我們通過debug加載f.exe,u命令查看開始處代碼:如下
圖14-15 debug加載f.exe
通過代碼,我們發現,程序將我們自己寫的c0s代碼也加載進去了,再次印證了以下觀點:1.c0s文件和用戶obj文件一同連接,生成相應的exe文件;2.所謂的main,f不過是一個入口參數而已,本身並無特地位!
下面重新研究向一塊安全的內存中寫入”a”到”h”,匯編代碼如下:
圖16-19 安全空間寫入”a”到”h”
我們可以發現一個很奇怪的現象,程序中經常刷新es寄存器的值!!!
圖20-21 內存結果顯示
可以看出ds:0開始處存放額是a-h,在這里我們需要注意的是,es只管下一條語句的段地址!之后默認的是ds!務必明確這一點!
未解決的問題:
Main函數的結尾處為什么有兩個ret?
對於向安全的空間寫數據,為什么會經常刷新es寄存器的值?
總結感悟:
對於這次學習我認為學習到最重要的一點是對MIAN函數的認識更加深刻了,即MAIN函數本身並不具有一定的特殊性!僅此而已。期間對於追蹤調用MAIN函數指令地址進行了思考,並最終通過實踐得到了驗證。此次研究我認為對於編程技巧沒有太大的幫助,但是對於理解本質卻很有幫助,另外,對於最后一個向安全空間寫數,我認為還任由許多問題應該探討!