內(nèi)存模式的出現(xiàn)不是由編譯器決定的,而是由處理器的尋址方式?jīng)Q定的,在8086處理器中為了在16位寄存器的基礎(chǔ)上尋址20位的地址,引入了段寄存器和分段尋址的方式。在編譯器這一級,利用這種段式的尋址方式,可以實現(xiàn)多種不同的存儲分配方法,因此就產(chǎn)生了所謂的不同的
內(nèi)存模式。
1. tiny模式: 程序和數(shù)據(jù)在一個64K字節(jié)的段內(nèi)
2. small模式: 獨立的代碼段(64KB)和獨立的數(shù)據(jù)段(64KB)
3. medium模式: 單個數(shù)據(jù)段(64KB)和多個代碼段(1MB)
4. compack模式:單個代碼段(64KB)和多個數(shù)據(jù)段(1MB)
5. large模式: 多個代碼段(1MB)和多個數(shù)據(jù)段(1MB),數(shù)據(jù)指針不能跨越段邊界,否則將回繞
6. huge模式: 多個代碼段(1MB)和多個數(shù)據(jù)段(1MB),數(shù)據(jù)指針可以跨越段邊界,不會回繞
在TC中
內(nèi)存模式與far、near、huge等關(guān)鍵字又有著密切的關(guān)系。在tiny、small模式下,所有的函數(shù)定義、全局變量定義和指針變量的定義,如果沒有顯示的加上far、near、huge等關(guān)鍵字,都默認(rèn)為使用了near關(guān)鍵字;在medium模式下,函數(shù)定義默認(rèn)使用了far關(guān)鍵字,變量定義默認(rèn)使用了near關(guān)鍵字;在compact模式下函數(shù)定義模式使用了near關(guān)鍵字,變量定義默認(rèn)使用了far關(guān)鍵字;large模式下函數(shù)定義和變量定義模認(rèn)使用了far關(guān)鍵字;huge模式下函數(shù)定義模認(rèn)使用了far關(guān)鍵字,變量定義默認(rèn)使用了huge關(guān)鍵字。
near、far、huge關(guān)鍵字的真正含義是什么?這三個關(guān)鍵字只能用于修改函數(shù)、全局變量和指針變量,對于非指針類型的局部變量,這些關(guān)鍵字沒有實際意義。這些關(guān)鍵字用于修飾函數(shù)時,huge的含義與far相同,用于指明該函數(shù)的調(diào)用方式為far調(diào)用方式,即調(diào)用時需要一個段值和一個段偏移組成的32bits調(diào)用地址,使用far call進行跳轉(zhuǎn),跳轉(zhuǎn)前先壓棧保存當(dāng)前CS:IP。near修飾函數(shù)時,用于指明該函數(shù)的調(diào)用方式為near調(diào)用方式,調(diào)用時只需要一個16bits的近地址,即當(dāng)前CS的段內(nèi)偏移。
當(dāng)這三個關(guān)鍵字用于修飾指針時,near型指針實質(zhì)上為16bits的無符號整型數(shù),該整數(shù)給出了所指向變量在當(dāng)前數(shù)據(jù)段內(nèi)的偏移地址,也就是說,在使用near型指針尋址時實際上是進行如下的尋址操作:[DS:指針變量值]。對于far型的指針變量,可以尋址1MB地址空間的任意一個地方,far型指針的實質(zhì)是一個32bits的整型數(shù),高16bits為段值,低16bits為段內(nèi)偏移,Turbo C中在使用far型指針時,會先將高16bits放入ES寄存器中,然后再進行如下的尋址操作:[ES:指針變量低16bits值]。對于hug型的指針變量,與far性指針變量的不同之處在于,在對far型指針變量進行+/++/-/--等操作時,far型指針變量保持段值不變(也就是高16bits),而只對段內(nèi)偏移進行加減操作,所以會出現(xiàn)段內(nèi)回繞的現(xiàn)象,而huge型的指針,在進行加減操作時將會自動的改變段值,不會出現(xiàn)段內(nèi)回繞。所以給人的感覺就是huge指針能比far指針尋址更大的內(nèi)存空間。
對于局部變量,由于是創(chuàng)建在堆棧上,所以near、far等關(guān)鍵字將不具備任何意義,因為創(chuàng)建在堆棧上的變量的尋址方式就只有一種,即使用sp和bp維護函數(shù)堆棧,利用bp+/-一個偏移來尋址函數(shù)參數(shù)變量和局部變量。這樣的尋址方式是固定而唯一的,near和far等關(guān)鍵字都派不上用場,這里的near和far將沒有任何的實際含義。
對于使用near、far和huge修飾的全局變量的含義也很容易理解了。near型的全局變量,被分配到了當(dāng)前的數(shù)據(jù)段上,尋址這個變量只需要一個16bits的偏移量,而far型全局變量在尋址時,需要給出段值和偏移量。huge型數(shù)組可以使用大于64K的內(nèi)存空間。
far、near、huge型指針變量之間的相互轉(zhuǎn)換,從小尺寸的指針到大尺寸的指針將進行自動的類型轉(zhuǎn)換,轉(zhuǎn)換方式為加上當(dāng)前的DS形成32bits的指針。從大尺寸的指針到小尺寸的指針需要進行強制類型轉(zhuǎn)換,轉(zhuǎn)換的結(jié)果為只保留低16bits,但是這樣俄轉(zhuǎn)換沒有實際的意義或者說用處不大,并且極其容易引入內(nèi)存訪問的錯誤,所以要嚴(yán)格避免使用。
需要注意的是,near、far、huge三個關(guān)鍵字的使用,還需要內(nèi)存模式的緊密配合。但并不是說tiny模式下就不能使用near、far、huge三個關(guān)鍵字。tiny模式下同樣可以定義如下的指針:
char far *pbuf = 0xA0000000;
并且我能保證這個指針能夠絕對正確的工作,對函數(shù)、全局變量的修飾也是如此。但是如何正確的工作,如何才是最和合理的方式,請自己思考了。基本的原理我也講的很清楚,就不再多費唇舌。
Turbo C中,我想最為困惑的就是內(nèi)存模式了,我也是費了很多時間和精力,通過分析Turbo C的匯編代碼的出的以上結(jié)論。許多朋友都對此很困惑,所以這部分重點講了下,和大家分享。如有不正確之處,請不吝賜教,旨在拋磚引玉。tcc編譯匯編代碼的方法為:tcc -c -mt -S filename.c,-c指明compile only,-mx用于指定內(nèi)存模式,-S指明生成匯編代碼,如果大家有興趣可以嘗試使用這個方法分析tcc編譯結(jié)果的匯編代碼,從而更加深刻的理解C與匯編的關(guān)系。
當(dāng)我們在編寫、制作并向用戶提供自己的庫文件時,也需要注意內(nèi)存模式的匹配,否則在進行鏈接時會存在問題。一個較為簡單的方法就是向用戶提供全套內(nèi)存模式的庫文件,這也是Turbo C的ANSI C庫的做法,前文已經(jīng)提到。如果不想提供多個內(nèi)存模式的庫文件,可以對程序中每個函數(shù)、全局變量和指針變量進行顯式的類型聲明,以精確定義每個變量的類型。
節(jié)選自 RockCarry工作室 陳凱