變量本質的認識


變量的本質

研究過程:

  1. 1.   程序1

對程序進行編譯連接之后,生成.exe文件,再次用debug加載此程序,執行其匯編代碼。

再次得到之前已經得到的結論,C語言中函數的參數傳遞是通過堆棧的方式進行參數傳遞的。

 

圖1  堆棧傳參

同時看出,函數的返回值保存在寄存器AX中,這也是之前得到過的結論。

同時發現,程序運行時,全局變量n的段地址在DS中,而且它具有一個特定的地址。

而對於局部變量a,b,我們發現它所代表的就是堆棧中的實參,也就是說,參數a,b的段地址在寄存器SS中。

而對於變量C而言,我們發現,匯編語言中用寄存器SI代替了C的功能。

函數的返回值一直在AX中保存。

接下來,做了這樣的事情,在F2中定義了d,e,f三個變量,但是沒有進行其他任何操作,結果發現對應的匯編代碼中仍然是用SI來代替C變量,其他代碼也不受影響,隨即對d,e,f賦值,並做了一定的運算,發現變量d成了棧中的空間,變量e,f被SI,DI等效替代。而CX的作用則被AX取代。

從這里其實可以得出這樣的結論了,局部變量只在被用的時候才會分配空間,而且對於局部變量,分配的空間有可能是堆棧段內存區,也有可能是寄存器,這取決於變量使用的頻繁度和重要性。

而對於其釋放,應該說,子程序調用結束后,但是在還未返回之前堆棧被釋放,則以堆棧內存區作為空間的變量就被釋放了,以寄存器作變量的從實際意義上講,也被釋放了,盡管里面的值未發生任何變化。

而對於參數,他的空間分配時機要早的多,在其他函數調用此調用函數的時候,也就是還沒有進入子程序之前,參數的空間就被分配了,也就是堆棧區。對於其釋放,則通過程序發現了一個現象:

圖2  參數返回時機

通過程序可以清晰的看出,參數的釋放是在子程序已經返回之后,即將執行下一條要執行指令之前,釋放到了寄存器CX中。

通過程序,發現,f3返回時匯編指令使RETF而不是RET,也就是說f3本身是個遠調用類型。

對於全局變量而言,通過在使用n相關的操作之前,對其進行printf()顯示其段地址和偏移地址,發現能夠正常顯示,這說明,在

進入main函數之前,或者說,在需要用到n之前,它就已經被定義了,具有實實在在的物理地址。

  1. 2.   程序2

  關於靜態變量和局部變量的區別研究

  程序結果顯示為:

 

圖3  靜態變量和局部變量

從結果可以看出來,局部變量兩次顯示的結果一樣,而靜態變量a 卻不同,通過程序1知道,局部變量在退出程序調用后,變量就被釋放了,所以兩次調用f函數顯示的結果自然一樣,而a的值是不同的可見,在結束第一個f函數調用的時候,變量a並未得到釋放。觀察匯編代碼以了解其本質。

 

圖4  靜態變量匯編代碼

可以發現的是,在未進入f函數之前,對靜態變量的初始化卻已經完成了,這是一個比較有趣的現象,推測其原因是在f函數的聲明的時候系統預知要進行這樣的處理。

總之,我們得到的一個結論是:靜態變零不會再函數調用結束后釋放,而且其聲明和初始化在進入函數之前就完成了,而且其段地址在DS中,存儲空間地址和全局變量似乎很類似。

那么問題是:靜態變量和全局變量有什么區別,如何區分他們?

這個問題目前尚未找到一個好的解決方法。

  1. 3.   程序3

  對變量類型進行研究,所得匯編代碼如下:

 

圖5  數據類型所占空間

從匯編代碼能夠看出來,連續定義的變量其存儲空間是連續的。無符號整型變量為一個字大小,無符號字符型為一個字節,無符號長整型為兩個字空間。

  從匯編代碼可以看出,對於字符型和普通整型無差別,但是對於長整型而言,inc指令變成了ADD指令,而且還要考慮進位情況。可見,數據類型對運算方式還是有差別的。其實從本質上思考這個問題,是因為8086CPU是16位的CPU,所以才會導致這個問題,如果是32位CPU的話,上面的運算方式應該是無差別的。

  1. 4.   程序4

  從源程序可以看出,struct stu 類型的變量a是一個全局變量,

struct stu 類型的變量b是一個局部變量。

 

圖6  變量a的匯編代碼

從其匯編代碼中,我們可以得出基本結論:全局變量的段地址仍然放在了DS寄存器中,這一點是具有普遍規律的,全局變量仍然有一個固定的地址,局部變量仍然是通過堆棧的方式定義的。

而且承接程序三得出結論:連續定義的且屬於同一類別(比如全局變量)的變量,他們在內存中地址是連續的。

  1. 5.   程序5

  這個程序的研究確實花費了比較長的時間不過確實發現了一些有趣的現象

  首先來說,這里再次強調前面已經得到的一般性規律:局部變量是放在了堆棧里面。

  現在研究如何返回結構體變量,

 

圖7  結構體返回時的動作

   開始的時候讀到這里的時候感覺莫名奇妙,不知道為什么會有這樣奇怪的指令,后來重新看了一遍代碼,並把棧空間以及它的地址給畫了出來,再了解了LDS,LES指令的作用,突然想到這兩條指令是串搬移的初始化操作,更巧的是DS:SI所指位置就是func函數中a.a變量值1的地址,而042E就是主函數中結構體變量a的值,而且發現cx為3,而下面的指令是字搬移指令,所以,我么得出結論:結構體變量返回是一種串操作!  

通過下面的指令我們同時也發現,在像函數傳遞結構體的時候采用的也是同樣的機制,即仍然是一種串操作!

未解決問題:

  1. 1.   全局變量和靜態變量有什么區別,如何區分?
  2. 3.   匯編中為什么會出現遠調用?

  總結:

       這個實驗研究的比較廣,幾乎涉及到所有類型的變量,研究了所有的變量的存儲位置,參數傳遞方式,申請和釋放時機等,是我們深入學習C語言額基礎。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM