关闭

C语言存储模式区别、near指针,far指针,huge指针

标签: c语言指针存储
890人阅读 评论(0) 收藏 举报
分类:

一、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));

}

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:1284次
    • 积分:35
    • 等级:
    • 排名:千里之外
    • 原创:2篇
    • 转载:1篇
    • 译文:0篇
    • 评论:0条
    文章存档