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

29 c c++ x86 pointers x86-16

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

Mik*_*one 30

主要的例子是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
Run Code Online (Sandbox Code Playgroud)

这创造了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 kiB.当他们创建68000架构时,他们直接进入32位寄存器,因此从未需要近,远,或巨大的指针.(相反,他们的问题是只有地址的底部24位实际上很重要,所以一些程序员(众所周知的Apple)会使用高8位作为"指针标志",导致地址总线扩展到32位(4 GiB)时出现问题.)

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

  • 内存不是免费的,即使在今天,许多应用程序在为 32 位 x86 编译时比为 x64 编译时运行得更快,尽管 64 位模式具有更大的寄存器集。对于 64 位模式,我能看到的唯一显着的性能下降是 64 位对象引用占用的缓存是 32 位对象引用的两倍。8086 确实需要更多的段寄存器,但即便如此,我还是会说它比之前或之后的任何其他 16 位架构(M68K 是 32 位架构)在寻址 1MiB 地址空间方面做得更好。 (2认同)

Vik*_*rma 21

far和huge指针之间的区别:

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

Farhuge指针:

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

远指针的局限性:

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

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

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

1.当远指针被递增或递减ONLY偏移指针实际递增或递减的,但在巨大的指针段和偏移值的情况下会发生变化.

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

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

然后输出是:

0000:0000
Run Code Online (Sandbox Code Playgroud)

分部价值没有变化.

如果是巨大的指针:

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

输出是:

0001:0000
Run Code Online (Sandbox Code Playgroud)

这是因为递增操作不仅偏移值而且段值也改变.这意味着在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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

different
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

same
Run Code Online (Sandbox Code Playgroud)

说明:正如我们所看到的绝对地址都pp112341(1234*10+11230*10+41),但他们没有,因为在的情况下,考虑在第一种情况下等于far指针只偏移进行比较,即它会检查是否0001==0041.哪个是假的.

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

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

    假如我们有0x1234:1234它的标准化形式是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;
     }
    
    Run Code Online (Sandbox Code Playgroud)

    输出:

    h=1234:1234
    
    h1=1357:0005
    
    Run Code Online (Sandbox Code Playgroud)

    说明:huge指针不会在assignment.But的情况下归一化,如果在其上执行的算术运算,这将是normalized.So,h1234:1234h11357:0005这是归一化的.

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

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

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

输出:

    0000:0010
Run Code Online (Sandbox Code Playgroud)

如果是huge指针:

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

        Output:
        0001:0000
Run Code Online (Sandbox Code Playgroud)

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

  • +1 内容。确保下次正确格式化您的答案。 (2认同)

PP.*_*PP. 13

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

  • @Vishwanath:不,它们并不适用于新代码.它们仅适用于16位英特尔平台,这些平台在很久以前就被淘汰了(我相信英特尔386是第一款有效支持32位平板内存模型的芯片).如果您正在编写必须关心此问题的代码,那么您正在编写遗留代码. (8认同)
  • @Lucas 最好的方法是找到一本 20 世纪 80 年代的 PC 编程书 --- 所有老式 DOS 编程都使用这些书。尝试查找 Turbo C 或 Pacific C。 (2认同)