near指针,far指针,huge指针
时间:2010-08-29 来源:sinodragon21
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()这样的内存分配函数一起使用。
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));
}
===========================
As far as 'far' pointer is concerned the answer is to just remove the word 'far'. It doesn't make sense on a 32 bit platform.
The 'far' keyword comes from the old DOS platform using 16 bit pointers and addresses. Of course, that gave you only 65536 different bytes to address or 64K. Which is far too little. The solution provided by intel was to add a number of 'segment' registers each of which is 16 bit and have a mode where a 16 bit address could be combined with a 16 bit segment into a 32 bit address. This 32 bit address was referred to as a 'far pointer'. Now you might think that with 32 bit addresses you could access 4Gb of data but in their lack of wisdom they thought that nobody would ever need taht much memory space so they made the 16 segment and 16 bit address combine like this:
segment * 16 + address
instead of the theoretically possible:
segment * 65536 + address.
here segment and address are both 16 bit values and range from 0 to 65535. This gives one headache with the far pointers, two different addresses can very well point to the same physical address:
0x12345 == 0x1234 * 16 + 0x0005 == 0x10000 + 0x2345
so using segment 0x1234 and address 0x0005 is the same location as segment 0x10000 and address 0x2345. In fact for every location there are 4096 different segment and address values that each point to that specific location. Well, taht was until the 'high area' was invented.
As machines developed and memory became cheaper and more compact they put more than one megabyte memory in the machines and the people at intel found a way that you can access above one megabyte:
Traditionally when doing the segment * 16 + address thing, the address had wrapped around in one megabyte or bit 20 on the address was ignored. Using high memory they let bit 20 be significant and so you could address above one megabyte:
0xffff:0x000f is address 0xfffff
0xffff:0x0010 is address 0x100000 instead of 0x00000
0xffff:0xffff is address 010ffef instead of 0x0ffef
This is the so-called A20 as you may have heard of in MS-DOS and BIOS. Also, the Loadhigh or devicehigh where commands provided by MS-DOS to load drivers and other code into the high area from 0x100000 to 0x10ffef.
Addresses above that address 0x10ffef was not available until the 32 bit architecture came with the 386, well I think the 286 also had partial support for 32 bit addresses not sure about that though. The 386 had it.
In 32 bit architecture every address is 32 bit and so there's no 'far' or 'near' pointers anywhere. You still have the segment registers but for one thing they mean something else in protected mode than what they did in the old Intel CPU and secondly, the Win32 system doesn't really use them much. When a program is loaded, DS, CS and SS and I think also ES and GS are all set to point to the same virtual memory space and so an address is an address. The only exception to this - I think - is that FS is set to point to the thread's local storage area. Not sure exactly how it works in details, never studied it much but I believe FS play a role in finding the thread local storage on a Win32 system.
In any case, the bottom line is: drop the far and you're fine as far as pointers go.
However, you have a more serious problem. You are attempting to access the area formerly known as B800:0000 which is the console character display.
Well, the bad news is that it doesn't work that way any more. On Win95 you can access that area and it still work but you shouldn't, you will step on Win95's toes and shouldn't do that.
On Win2000 or WinXP you can forget it, you are not allowed to touch that area.
Use the Console API in Win32 to manipulate the console for regular text output - manipulate background color, foreground color, cursor etc etc etc.
Check out
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/console_functions.asp
For details about the console functions.
Alf