什么是近,远和巨大的指针?

任何人都可以用适当的例子向我解释这些指针……当这些指针被使用时?

在过去,根据Turbo C手册,当您的整个代码和数据适合一个段时,近指针仅为16位。 远指针由段和偏移组成,但未执行标准化。 并且一个巨大的指针被自动标准化。 可以想象两个远指针指向内存中的相同位置但是不同,而指向相同内存位置的规范化巨大指针总是相等的。

主要的例子是Intel X86架构。

英特尔8086内部是一个16位处理器:它的所有寄存器都是16位宽。 但是,地址总线为20位宽(1 MiB)。 这意味着您无法在寄存器中保存整个地址,将您限制在前64 kiB。

英特尔的解决方案是创建16位“段寄存器”,其内容将向左移位4位并添加到地址。 例如:

DS ("Data Segment") register: 1234 h DX ("D eXtended") register: + 5678h ------ Actual address read: 179B8h 

这创造了64 kiB段的概念。 因此,“近”指针只是DX寄存器(5678h)的内容,除非DS寄存器已正确设置,否则无效,而“远”指针为32位(12345678h,DS后跟DX),总是会工作(但是因为你必须加载两个寄存器然后在完成后恢复DS寄存器,所以速度较慢)。

(正如下面的supercat说明,溢出的DX偏移会被添加到DS以获得最终地址之前 “翻转”。这允许16位偏移访问64 kiB段中的任何地址,而不仅仅是那个部分。 DX指向的位置为±32 kiB,在某些指令中使用16位相对偏移量寻址的其他架构中也是如此。)

但是,请注意,您可能有两个“远”指针,这些指针的值不同但指向相同的地址。 例如,远指针100079B8h指向与12345678h相同的位置。 因此,远指针上的指针比较是一个无效的操作:指针可能不同,但仍然指向同一个地方。

这就是我认为Macs(当时使用摩托罗拉68000处理器)并不是那么糟糕的地方,所以我错过了巨大的指针。 IIRC,它们只是远的指针,保证段寄存器中的所有重叠位都是0,如第二个例子。

摩托罗拉在他们的6800系列处理器上没有这个问题,因为它们被限制在64千字节,当他们创建68000架构时,他们直接进入32位寄存器,因此从未需要近,远,或巨大的指针。 (相反,他们的问题是只有地址的底部24位实际上很重要,所以一些程序员(众所周知的Apple)将使用高8位作为“指针标志”,当地址总线扩展到32位(4 GiB)时会导致问题。)

Linus Torvalds一直坚持到80386,它提供了一个“保护模式”,其地址是32位,段寄存器是地址的高半,并且不需要添加,并且从一开始就编写Linux以使用受保护的只有模式,没有奇怪的部分,这就是为什么你没有Linux中的远近指针支持(为什么没有设计新架构的公司如果想要Linux支持就不会再回到它们)。 而且他们吃了罗宾的吟游诗人,并且非常欢欣鼓舞。 (好极了…)

far和huge指针之间的区别:

正如我们所知,默认情况下指针near例如: int *pnear指针。 在16位编译器的情况下, near指针的大小是2个字节。 而且我们已经非常了解编译器的大小不同编译器; 它们只存储它引用的指针的地址偏移量。 仅由偏移量组成的地址的范围为0到64K字节。

Far指针:

Far指针的大小为4个字节。 它们存储指针引用的地址的段和偏移量。 那他们之间有什么区别

远指针的限制:

我们不能通过对其应用任何算术运算来更改或修改给定远地址的段地址。 那就是通过使用算术运算符,我们不能从一个段跳到另一个段。

如果要将far地址递增超出其偏移地址的最大值而不是递增段地址,它将以循环顺序重复其偏移地址。 这也称为包装,即如果offset是0xffff并且我们加1然后它是0x0000并且类似地如果我们将0x0000减少1然后它是0xffff并且记住该段没有变化。

现在我要比较巨大和远点指针:

1.当远指针递增或递减时,指针的偏移实际上是递增或递减的,但是在巨大指针的情况下,段和偏移值都将改变。

请考虑以下示例,取自以下示例:

  int main() { char far* f=(char far*)0x0000ffff; printf("%Fp",f+0x1); return 0; } 

然后输出是:

 0000:0000 

分部价值没有变化。

如果是巨大的指针:

 int main() { char huge* h=(char huge*)0x0000000f; printf("%Fp",h+0x1); return 0; } 

输出是:

 0001:0000 

这是因为递增操作不仅是偏移值而且段值也发生变化。这意味着在far指针的情况下段不会改变,但是在huge指针的情况下,它可以从一个段移动到另一个段。

2.当在远指针上使用关系运算符时,仅比较偏移量。换句话说,如果被比较的指针的段值相同,则关系运算符将仅对远指针起作用。 在巨大的情况下,这不会发生,实际上绝对地址的比较发生。让我们在far指针的例子的帮助下理解:

 int main() { char far * p=(char far*)0x12340001; char far* p1=(char far*)0x12300041; if(p==p1) printf("same"); else printf("different"); return 0; } 

输出:

 different 

huge指针:

 int main() { char huge * p=(char huge*)0x12340001; char huge* p1=(char huge*)0x12300041; if(p==p1) printf("same"); else printf("different"); return 0; } 

输出:

 same 

说明:我们看到pp1的绝对地址是123411234*10+11230*10+41 ),但在第一种情况下它们不被认为是相等的,因为在far指针的情况下,只比较偏移,即它将检查0001==0041 。 哪个是假的。

并且在大指针的情况下,对相等的绝对地址执行比较操作。

  1. 远指针永远不会被标准化,但是一个huge指针被规范化。 规范化指针是在段中具有尽可能多的地址的指针,意味着偏移量永远不会大于15。

    假设我们有0x1234:1234那么它的标准化forms是0x1357:0004 (绝对地址是13574 )。 只有在对其执行某些算术运算时,才会对一个巨大的指针进行归一化,并且在指定期间不进行归一化。

      int main() { char huge* h=(char huge*)0x12341234; char huge* h1=(char huge*)0x12341234; printf("h=%Fp\nh1=%Fp",h,h1+0x1); return 0; } 

    输出:

     h=1234:1234 h1=1357:0005 

    说明:在赋值的情况下, huge指针不规范化。但是如果对其执行算术运算,则将其归一化。因此, h1234:1234并且h11357:0005 ,其被归一化。

    4.由于规范化,大指针的偏移小于16,而远指针则不然。

    让我们举个例子来理解我想说的话:

      int main() { char far* f=(char far*)0x0000000f; printf("%Fp",f+0x1); return 0; } 

输出:

  0000:0010 

如果是huge指针:

  int main() { char huge* h=(char huge*)0x0000000f; printf("%Fp",h+0x1); return 0; } Output: 0001:0000 

说明:当我们将far指针递增1时,它将是0000:0010 。当我们将大指针递增1时,它将是0001:0000因为它的偏移量不能大于15,换句话说它将被归一化。

此答案中的所有内容仅与旧的8086和80286分段内存模型相关。

near:一个16位指针,可以处理64k段中的任何字节

far:一个32位指针,包含一个段和一个偏移量。 请注意,因为段可以重叠,所以两个不同的远指针可以指向相同的地址。

huge:一个32位指针,其中段被“规范化”,因此除非它们具有相同的值,否则没有两个远指针指向同一地址。

T恤:果酱和面包的饮料。

这会把我们带回来哦哦哦哦

当这些指针被使用时?

在20世纪80年代和90年代,直到32位Windows变得无处不在,

该术语用于16位架构。

在16位系统中,数据被划分为64Kb段。 每个可加载模块(程序文件,动态加载的库等)都有一个关联的数据段 – 最多只能存储64Kb的数据。

NEAR指针是具有16位存储的指针,并且在当前模块数据段中仅引用数据(仅)。

作为要求具有超过64Kb数据的16位程序可以访问将返回FAR指针的特殊分配器 – 其是高16位中的数据段id,以及指向该低位16位中的数据段的指针。

然而,较大的程序需要处理超过64Kb的连续数据。 一个巨大的指针看起来就像一个远指针 – 它有32位存储 – 但是分配器已经注意安排一系列具有连续ID的数据段,这样通过简单地递增数据段选择器,下一个64Kb的数据块就可以了到达。

底层的C和C ++语言标准从未在内存模型中正式识别这些概念–C或C ++程序中的所有指针都应该具有相同的大小。 所以NEAR,FAR和HUGE属性是各​​种编译器供应商提供的扩展。

在某些体系结构中,指向系统中每个对象的指针将比可以指向有用的事物子集的指针更大更慢。 许多人已经给出了与16位x86架构相关的答案。 各种类型的指针在16位系统上很常见,但是在64位系统中可能会出现接近/恐惧的区别,这取决于它们的实现方式(如果许多开发系统转向64位指针,我不会感到惊讶尽管事实上在许多情况下这将是非常浪费的事情。

在许多程序中,将内存使用细分为两类非常容易:小东西总共可以达到相当少量的东西(64K或4GB)但是经常被访问,而更大的东西可能总计达到更大的数量,但不需要经常访问。 当应用程序需要处理“大事物”区域中对象的一部分时,它会将该部分复制到“小事物”区域,与其一起使用,并在必要时将其写回。

一些程序员抱怨必须区分“近”和“远”内存,但在许多情况下,进行这样的区分可以使编译器产生更好的代码。

(注意:即使在许多32位系统上,也可以直接访问某些内存区域而无需额外的指令,而其他区域则不能。例如,如果在68000或ARM上,则保持寄存器指向全局变量存储,可以在该寄存器的第一个32K(68000)或2K(ARM)内直接加载任何变量。获取存储在别处的变量将需要额外的指令来计算地址。在首选区域中放置更常用的变量并让编译器知道将允许更有效的代码生成。