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指针时,都要将数据段或程序段的数据交换出来。另外,far指针的运算也比较反常,例如上面讲到的far指针指向同一个地址,但是比较的结果却不相同。
什么时候使用far指针?
当使用小代码或小数据存储模式时,不能编译一个有很多代码或数据的程序。因为在64K的一个段中,不能放下所有的代码与数据。为了解决这个问题,需要指定以far函数或far指针来使用这部分的空间(64K以外的空间)。许多库函数就是显式地指定为far函数的形式。far指针通常和 farmalloc()这样的内存分配函数一起使用
参考资料:http://www.turinger.com/forum_view.asp?forum_id=4&view_id=199
FAR* 现在不用了, 以前用在16位代码里, 因为代码是16位的, 所以指针不够长. 为了表示32位的地址. 应该使用 **r 来修饰 一个指针。
16位机情况:
由于PC机的存储器地址是由段地址和偏移地址组合而成,每个段不能超过64K字节地址,因此同一个段内的地址存取,
仅用偏移地址就可以实现,用指针时,16位就可以了。称为近程指针nea*,当要在另一个段内取数据时,就要跨越段,
即要指明存取段的段地址和偏移地址,这时段地址寄存器所存段地址要变,因此在使用指针指向另一个段内地址时,
就要用32位表示(即段地址:偏移地址),这类指针,称为远程指针,是***型.
far*为远程指针
远程指针是指32位的指针,它表示段地址:偏移地址,如定义远程指针P指向B500段的2号地址,即B500:0002,则可写作:
cha* far*p=(cha* far*)0xB5000002;
因而用远程指针可以指向任何的数据段内地址,在大数据内存模式下,当跨越段进行数据存取时(即存取超过64K的数据),可以采用
远程指针。当使用far型指针时,因要进行跨段寻址,数据段寄存器的值要更换,因而加长了程序的执行时间。
另外远程指针的值在进行加减时,只有偏移的地址部分进行运算,段地址不参加运算,即段地址不变,因此会出现折回现象。
******************************************************************************************************************************************************************
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指针时,都要将数据段或程序段的数据交换出来。另外,far指针的运算也比较反常,例如上面讲到的far指针指向同一个地址,但是比较的结果却不相同。
9、什么时候使用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)指针
前面已经说过,巨指针综合了近指针和远指针的优点。像近指针
一样,基指针只占两个字节,这两个字节是地址的偏移量。像远指针
一样,基指针可以寻址内存中的任一区域。近指针的段地址隐含地取
自程序的数据段,远指针的段地址取自指针本身,基指针的段地址取
法以及基指针的许多技术和应用问题,请见第11章。
五、各类指针之间的转换
far指针可以强制转换为near 指针,做法很简单,抛掉段地址只
保留偏移量。near指针也可以转换为far指针,Turbo C的做法是从相
应的段寄存器中取得段地址。
far指针有时也需要转换为huge 指针,以便对指针进行比较或做
其它操作。一种方法是通过下面这样一个规则化函数: void normalize(void far **p) {
*p=(void far *)(((long)*p^0xffff000f)+
(((long)*p^0x0000fff0)<<12));
}