1. 程式人生 > >C語言儲存模式區別、near指標,far指標,huge指標

C語言儲存模式區別、near指標,far指標,huge指標

一、C語言各種儲存模式的區別

DOS用一種段地址結構來編址計算機的記憶體,每一個實體記憶體位置都有一個可通過段地址一偏移量的方式來訪問的相關地址。為了支援這種段地址結構,大多數C編譯程式都允許你用以下6種儲存模式來建立程式:

-----------------------------------------------------------------------————————————————————————

儲存模式             限制                    所用指標

-----------------------------------------------------------------------————————————————————————

Tiny(微)             程式碼、資料和棧一64KB      Near 

Small(小)          程式碼一64KB                Near  

                   資料和棧一64KB           Near 

Medium(中)          程式碼一1MB                 Far  

資料和棧一64KB            Near 

Compact(緊縮)       程式碼一64KB                Near  

資料和棧一1MB             Far 

Large(大)            程式碼一1MB                 Far 

資料和棧一1MB             Far 

Huge*(巨)            程式碼一1MB                 Far 

資料和棧一1MB             Far

-----------------------------------------------------------------------——————————————————————————————————————————————————————————————————

1)Tiny儲存模式的限制很嚴(所有的程式碼、資料和棧都被限制在64KB中),它通常用來生成"COM"檔案,在現在的高效能運算機上,它已很少被使用了。

2)對一個小的實用程式來說,Small儲存模式可能是最合適的,這種儲存模式允許有64KB的程式碼和64KB資料和棧。而相對較小的程式也可以使用Small儲存模式,只不過不必把整個程式都限制在64KB中。如果你在編寫一個Windows程式或者在使用一個32位編譯程式,那麼你最好使用Small儲存模式,因為這樣的環境並不受DOS程式的段地址結構的限制。

3)如果程式有更大一些的資料要求,你可以使用Medium儲存模式,它允許程式有多達1MB的可定址資料空間,或者說如果一個程式的程式碼量相對較大而靜態資料量相對較小,你可以用Medium儲存模式來建立程式。

4)Cornpact儲存模式允許程式有大量的靜態資料和相對較少(64KB或更少)的程式碼。滿足這種模式的程式很少,常常是一些轉移程式,它們有大量必須存到記憶體中的靜態轉移表。在今天的商業應用環境中,它同樣也不常用了。

5)對於更大的程式,你應該使用Large儲存模式,它允許程式有1MB的程式碼和1MB的資料和棧空間。如果程式很大(需要很多模組,大量的程式碼和資料),那麼你應該選用Large儲存模式,這種儲存模式常用在DOS下編寫商用軟體。

6)在Huge儲存模式下,靜態資料(如陣列)可以超過64KB,這在其它儲存模式下都不行。與Compact儲存模式相似,Huge儲存模式也很少被使用,這主要是因為它會帶來顯著的效能損失。由於Huge儲存模式的執行效率較低,因此你應該避免使用這種模式,除非你確實需要超過64KB的一個數組或其它靜態資料。Huge儲存模式與Large儲存模式基本相同,只是Huge儲存模式允許程式有超過64KB的靜態資料。

而最常使用的儲存模式有Small,Medium和Large這幾種。一般說來,你應該根據程式的大小選用Small,Medium或Large中的一種儲存模式。與Small,Medium和Large儲存模式相比,Tiny、Compact和Huge儲存模式要少用得多。記住,陣列和其它程式結構可通過malloc()和calloc()在程式執行時進行動態分配,它們在本質上並不必須是靜態的。

二、Dos下near指標,far指標,huge指標

1、   near指標和far指標在DOS下(真實模式)地址是分段的,每一段的長度為64K位元組,剛好是16位(二進位制的十六位)。near指標的長度是16位的,所以可指向的地址範圍是64K位元組,通常說near指標的定址範圍是64K。

far指標的長度是32位,含有一個16位的基地址和16位的偏移量,將基地址乘以16後再與偏移量相加,(所以實際上far指標是20位的長度。)即可得到far指標的1M位元組的偏移量。所以far指標的定址範圍是1M位元組,超過了一個段64K的容量。例如一個far指標的段地址為0x7000,偏移量為0x1244,則該指標指向地址0x71224.如果一個far指標的段地址是0x7122,偏移量為0x0004,則該指標也指向地址0x71224。如果沒有指定一個指標是near或far,那麼預設是near。所以far指標要顯式指定。far指標工作起來要慢一些,因為每次訪問一個far指標時,都要將資料段或程式段(指令指標暫存器IP與程式碼暫存器CS)的資料交換出來。另外,far指標的運算也比較反常,例如上面講到的far指標指向同一個地址,但是比較的結果卻不相同。

2、什麼時候使用far指標

當使用小程式碼或小資料儲存模式時,不能編譯一個有很多程式碼或資料的程式。因為在64K的一個段中,不能放下所有的程式碼與資料。為了解決這個問題,需要指定以far函式或far指標來使用這部分的空間(64K以外的空間)。許多庫函式就是顯式地指定為far函式的形式。far指標通常和farmalloc()這樣的記憶體分配函式一起使用。

FAR指標是|段地址:偏移地址|的形式

HUGE指標也是|段地址:偏移地址|的形式

因為可以有每個段都是64K的,可以定址多個段,所以這種指標的定址範圍很大,如果你的程式程式碼或者資料超過了64K,就只能用FAR指標或HUGE指標來操作了。它們二者也是有區別的:

HUGE指標是經過規範過的,可以直接比較大小。不過由於要處理後進行比較,所以運算速度較慢。FAR指標不能直接比較大小,但由於只比較偏移量,所以FAR指標的運算速度較快,你可以根據需要選用。

一、近(near)指標

近指標是用於不超過64K位元組的單個數據段或碼段。對於資料指標,在微、小和中編譯模式下產生的資料指標是近指標,因為此時只有一個不超過64K位元組的資料段。對於碼(即函式指標)指標,在微小和緊湊編譯模式下產生的碼指標是近指標,因為此時只一個不超過64K位元組的碼段。這裡只討論資料指標。  近指標是16位指標,它只含有地址的偏移量部分。為了形成32位的完整地址,編譯程式一般是反近指標與程式的資料段的段地址組合起來。因為在大部分情況下程式的資料段的段地址是裝在DS暫存器內,因此一般沒有必要裝載這個暫存器。此外,當用組合語言和C語言混合程式設計時,組合語言總是假設DS含有資料目標的地址。雖然近指標佔用空間最小,執行速度最快,但它有一個嚴格的限制,即只能64K位元組以內的資料,且只能存取程式的資料段內的資料。如果在小模式下編譯一個程式,而這個程式企圖增量一個近指標使之超過第65536個位元組,則這個近的指標就會復位到0。下面就是這樣一個例子:

char_near *p=(char _near *)0xffff;

p++;

由於近指標的這個嚴重限制,所有在比較大或比較複雜的程式中,都無法使用。

二、遠(far)指標

遠指標不是讓編譯程式把程式資料段地址作為指標的段地址部分,而是把指標的段地址與指標的偏移量直接存放在指標內。因此,遠指標是由4個位元組構成。它可以指向記憶體中的任一目標,可以用於任一編譯模式,儘管僅在緊湊、大和巨模式下遠指標才是預設的資料指標。因為遠指標的段地址在指標內,熟悉80X86組合語言的人都知道,這意味著每次使用遠指標時都需要重新裝載段暫存器,這顯然會降低速度。

應該注意:儘管遠指標可以定址記憶體中的任一單元,但它所定址的目標也不能超過64K 位元組。這是因為,遠指標在增量或減量之類的算術運算時,也只是偏移量部分參與運算,而段地址保持不變。因此,當遠指標增量或減量到超過64K位元組段邊界時就出錯。例如:

char far *fp=(char far *)0xb800ffff;

fp++;

在指標加1以後,fp將指向B800:0000,而不是所希望的C800:0000。此外,在進行指標比較時,far指標還會引起另外一些問題。far指標是由偏移量和段地址這樣一對16位數來表示的,對於某一實際記憶體地址,far指標不是唯一的,例如,far指標1234:0005、1230:0045、1200:0345、1000:2345、0900:9345等都是代表實際地址12345,這樣會引起許多麻煩。

第一,      為了便於與“空”(NULL)指標(0000: 0000)進行比較,當關系操作符“==”和“!=”用於far指標進行比較時,比較的是全部32位。否則,如果只比較16位偏移量,那麼任何偏移量為0的指標都將是“空”(NULL)指標,這顯然不符合一般使用要求。但在進行這32位比較時,不是按20位實際地址來比較,而是把段地址和偏移量當作一個32位無符號長整數來比較。對於上面這個例子,假設這些指標分別叫作a、b、c、d、e,儘管這5個far指標指向的都是同一記憶體單元,但下列表達式運算的結果卻都為“假”,從而得出錯誤的結論:

if(a==b)....

if(b==c)....

if(c==d)....

if(d==e)....

if(a==c)....

if(a==d)....

第二,      當用“>”、“>=”,“<”和“<=”關係操作符對指標進行比較操作時,比較的僅僅是偏移量部分,即按無符號的16位整數進行比較。因此,對於上面這個例子,下列表達式運算的結果將都為“真”,也得出錯誤的結論:

if(e>d)....

if(d>c)....

if(c>b)....

if(b>a)....

if(e>a)....

二、巨(huge)指標

只有巨指標才是一般C語言教科書上所說的指標,它像遠指標也佔4個位元組。與遠指標的顯著差別是:當增量或減量超過64K位元組段邊界時,巨指標會自動修正段基址的值。因此,巨指標不但可以定址記憶體中的任一區域,而且所定址的資料目標可以超過64K位元組。例如:

char huge *hp=(char huge*)0xb800ffff;

hp++;

在指標加1後,hp將指向C800:0000。但是,巨指標總是比較慢的,因為編譯必須生成一小段程式對指標進行32位而不是16位的加減運算。此外,由於huge指標是規則化指標,每一個實際記憶體地址只一個huge指標,所以在指標比較時不會產生錯誤。

四、基(based)指標

前面已經說過,巨指標綜合了近指標和遠指標的優點。像近指標一樣,基指標只佔兩個字

節,這兩個位元組是地址的偏移量。像遠指標一樣,基指標可以定址記憶體中的任一區域。近指標的段地址隱含地取自程式的資料段,遠指標的段地址取自指標本身

五、各類指標之間的轉換

 far指標可以強制轉換為near指標,做法很簡單,拋掉段地址只保留偏移量。near指標也可以轉換為far指標,Turbo C的做法是從相應的段暫存器中取得段地址。far指標有時也需要轉換為huge 指標,以便對指標進行比較或做其它操作。一種方法是通過下面這樣一個規則化函式:

void normalize(void far **p) {

*p=(void far *)(((long)*p^0xffff000f)+ (((long)*p^0x0000fff0)<<12));

}