一、 學習過程
要在計算機中用到一段存儲空間,必須要知道兩個信息:(1)存儲空間在哪?(2)存儲空間有多大。對於寄存器來說,只要給出寄存器的名字就可以了,因為每個寄存器在計算機中都是唯一的,而且寄存器大小是規定的。對於內存空間來說,就要給出地址和數據類型,數據類型就代表一個存儲空間的大小。
以前學習C語言的時候,並沒有仔細研究過,原來C語言中還可以以這種語法直接向內存單元中寫入數據:*(char *)0x2000=’a’;向偏移地址為2000h的內存空間寫入char型數據a。*(char far *)0x20000000=’a’;向段地址為2000h,偏移地址為0000的內存空間寫入char型數據a。比較上面兩種寫法可以發現,前一種只給出了偏移地址,那麼段地址在哪裡呢,是該語句所在的函數的段地址嗎?
我們在編寫C語言程序時要賦值給內存單元,通常的做法是聲明變量再對變量進行賦值,即把值賦給內存空間,類似於在匯編中把偏移地址賦給寄存器,再把數據賦給寄存器表示的內存空間,而這裡的直接賦值,類似於匯編中mov ds:[si],200h的語句。
編寫程序um1.c編譯連接:
在debug中用U命令查看相關代碼:
這裡我發現,編譯器在編譯時會把段地址給es,偏移地址給bx,結果存放在
es:[bx]中。
執行第一條語句後,內存單元內容為:
執行第二條語句後,內存單元內容為:
執行第三條語句後,內存單元內容為:
執行第四條語句後,內存單元內容為:
執行第五條語句後,內存單元內容為:
執行第六條語句後,內存單元內容為:
可見在c語言中直接使用對內存空間進行操作和先把內存單元地址給寄存器再對寄存器所代表的內存空間進行操作是一樣的,而且C語言中可以將內存空間的地址直接與寄存器相加減(如*(char far *)(0x20001000+_BX)=*(char *)_AX;),而且在語句*(char *)(_BX+_BX)=’a’;執行後寄存器bx的值是已經改變了的,即後一條語句*(char far *)(0x20001000+_BX)=*(char *)_AX;是把0x2000裡的值賦給0x20003000而不是0x20002000.
要在屏幕的中間顯示一個綠色的字符a,則要將a賦給b800:[12*160+40*2],將2賦給b800:[12*160+40*2+1],但是書上要求要用一條C語句實現。一個字符a在C語言中為char類型,占一個字節,int類型占兩個字節,那麼我們可以把2和字符a轉化成一個int型數據再賦給b800:[12*160+40*2]。如圖:
執行結果是正確的。
再看下一個程序:
編譯發現有許多警告,這裡不用去管。
編譯連接後用debug的u命令查看,發現a1的地址為01a6,a2的為01a8,a3的為01aa,b1、b2、b3的沒有直接給出,而是由bp-6、bp-4、bp-2表示,而在開頭程序將sp的值給了bp,sp自減了6,因為sp存放的是棧的棧頂偏移地址,可知實際上程序是將b1、b2、b3依次放入了棧中。但是在程序結尾處有mov bp,sp,把棧頂指針又還原了,這時b1、b2、b3的內存空間已不在棧中,隨時可以被其他程序的數據覆蓋。可知,全局變量是存放在指定的內存空間中,而局部變量是存放在棧中,到函數生命周期結束時釋放。因為要用bp記錄sp原來的位置,以便函數結束時將sp還原,所以要對bp進行保護,所以會有push bp;mov bp,sp.
再看下一個程序:
觀察程序可以發現,函數f()的返回值放在ax中。那麼如果程序有多個返回值應該怎麼存放呢?還有如果從main函數向f()傳參,也是用ax存放嗎?如果返回的參數不是int型,而是char或者long int型,應該用什麼存放呢?通過網絡查閱資料,發現如果是1字節的數據,用al存放,如果是4字節的數據,高16位用dx傳遞,低16位用ax傳遞。
再看下一個程序:
#define是宏定義,在程序中用Buffer代替((char *)*(int far *)0x02000000)
malloc(20)是開辟20個字節的內存空間。
我個人的理解:這裡是將指向開辟的20個字節的內存空間的指針賦給buffer,即200:0000~200:0001裡存儲的是開辟的20個字節的內存空間的首地址。Buffer[10]處是一個計數器,將a~h分別存到buffer[0]到buffer[7]的內存空間中。
二、 解決的問題
(1) c語言中直接使用對內存空間進行操作和先把內存單元地址給寄存器再對寄存器所代表的內存空間進行操作是一樣的,而且C語言中可以將內存空間的地址直接與寄存器相加減
(2) 為什麼*(char far *)(0x20001000+_BX)=*(char *)_AX;是把0x2000裡的值賦給0x20003000而不是0x20002000.?
答:在語句*(char *)(_BX+_BX)=’a’;執行後寄存器bx的值是已經改變了的,為0x2000.
(3) C語言將全局變量存放在哪裡?將局部變量存放在哪裡?每個函數開頭的“push bp;mov bp,sp。有何含義?”
答:全局變量是存放在指定的內存空間中,而局部變量是存放在棧中,到函數生命周期結束時釋放。因為要用bp記錄sp原來的位置,以便函數結束時將sp還原,所以要對bp進行保護,所以會有
push bp;mov bp,sp.
(4) C語言將函數的返回值放在哪裡?
答:如果是1字節的數據,用al存放,如果是2字節的數據,用al存放,如果是4字節的數據,高16位用dx傳遞,低16位用ax傳遞。
三、 未解決的問題
(1)*(char *)0x2000=’a’ 只給出了偏移地址,那麼段地址在哪裡呢,是該語句所在的函數的段地址嗎?
(2)函數有多個返回值在匯編應該怎麼存放呢?
(3)如果從main函數向f()傳參,也是用ax存放嗎?
(4)最後一個程序是怎麼在匯編中實現的?
四、 學習感想
在研究試驗中,我們是以匯編的角度去看C語言的問題,弄明白C語言的一些東西在匯編中是怎麼實現的。但是這樣就涉及到一些C語言方面的知識,我的做法是查資料和請教別人,但是別人的回答都是從C語言的角度而不是匯編的角度,這樣並不能給我們解決問題帶來幫助。有時候一個東西從不同的角度來看會有不同的樣子,也許從一個角度看來不能理解的問題,從另一個角度來看可能會比較容易理解。但是如果需要大量時間才能搞清楚,卻對當前學習沒有很大幫助的問題,是可以暫時放一放,去完成更加重要的學習進度的。