谁能用合适的例子向我解释这些指针......以及何时使用这些指针?

有帮助吗?

解决方案

在过去,根据《涡轮C手册》,当您的整个代码和数据拟合在一个细分市场中时,几乎只有16位。一个遥远的指针由段和偏移组成,但没有进行归一化。并且一个巨大的指针自动归一化。可以想象,两个遥远的指针可以指向内存中的同一位置,但要不同,而指向同一内存位置的归一化巨大指针始终相等。

其他提示

主要示例是 Intel X86 架构。

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

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

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指向同一位置。因此,远指针上的指针比较是无效操作:指针可能不同,但仍然指向同一个地方。

正是在这里,我认定 Mac(当时配备 Motorola 68000 处理器)毕竟还不错,所以我错过了巨大的指针。IIRC,它们只是远指针,保证段寄存器中的所有重叠位都是 0,如第二个示例中所示。

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

Linus Torvalds 一直坚持到了 80386,它提供了“保护模式”,地址为 32 位,段寄存器是地址的高半部分,不需要添加,并且从一开始就编写 Linux 使用受保护的模式。仅模式,没有奇怪的段内容,这就是为什么 Linux 中没有近指针和远指针支持(也是为什么设计新架构的公司如果需要 Linux 支持就不会再使用它们)。他们吃了罗宾的吟游诗人,非常高兴。(耶...)

远方和巨大指针之间的区别:

默认情况下,指针是 near 例如: int *p 是一个 near 指针。大小 near 指针为2个字节,如果有16位编译器。而且我们已经知道大小很好的编译器与编译器不同。他们仅存储地址的偏移量,它引用了指针。仅包含偏移的地址的范围为0-64K字节。

Farhuge 指针:

Farhuge 指针的大小为4个字节。他们存储了指针引用的片段和地址的偏移。那是什么 不同之处 它们之间?

远方指针的限制:

我们无法通过在其上应用任何算术操作来更改或修改给定地址的段地址。那是通过使用算术运算符,我们不能从一个细分市场跳到另一段。

如果您将超出其偏移地址的最大值而不是增加段地址的最大值,则将以循环顺序重复其偏移地址。这也称为包装,即如果偏移是 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

说明:正如我们看到两者的绝对地址 pp112341 (1234*10+1 或者 1230*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;
     }
    

    输出:

    h=1234:1234
    
    h1=1357:0005
    

    解释:huge 在分配的情况下,指针不标准化。但是,如果在其上执行算术操作,它将进行归一化。 h1234:1234h11357: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

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

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

接近:16位指针,可以解决64K段中的任何字节

FAR:一个32位指针,其中包含一个段和偏移量。请注意,由于段可以重叠,因此两个不同的指针可以指向相同的地址。

巨大:一个32位指针,其中该段“归一化”,因此除非具有相同的值,否则没有两个远方指针指向相同的地址。

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

那会让我们回到doh哦,哦,哦

当使用这些指针时?

在1980年代和90',直到32位窗户变得无处不在,

该术语用于16位体系结构。

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

一个近指针是一个指针,带有16位存储,并在当前模块数据段中引用数据(仅)。

具有超过64KB数据作为要求的16位程序可以访问将返回远距离指针的特殊分配器 - 这是上16位的数据段ID,在下部16位中,该数据段和该数据段的指针。

然而,较大的程序将希望处理超过64KB的连续数据。一个巨大的指针看起来完全像是一个遥远的指针 - 它具有32位存储 - 但是分配器已经注意到了连续ID的一系列数据段,因此,只要简单地将数据段选择器增加一个64KB的数据块就可以是到达。

基础C和C ++语言标准从未真正在其内存模型中正式认识到这些概念 - C或C ++程序中的所有指针都应该是相同的大小。因此,近乎遥远的属性是各种编译器供应商提供的扩展。

在某些体系结构中,可以指向系统中每个对象的指针都会比指向有用事物的一个子集更大,并且使用较慢。许多人给出了与16位X86架构有关的答案。各种类型的指针在16位系统上很常见,尽管近距离/恐惧的区别可能会在64位系统中重新出现,具体取决于它们的实施方式(如果许多开发系统都可以使用64位的指针,我不会感到惊讶一切,尽管在许多情况下会非常浪费)。

在许多程序中,将记忆使用分为两个类别非常容易:总计总数达到相当少量的东西(64K或4GB),但会经常访问,并且更大的东西可能总数达到更大的数量,但不必经常访问。当应用程序需要与“大事物”区域中的一部分对象一起工作时,它将该部分复制到“小东西”区域,与之合作,并在必要时将其写回。

一些程序员不得不区分“接近”和“远”内存,但是在许多情况下,有这样的区别可以使编译器能够生成更好的代码。

(注意:即使在许多32位系统上,也可以直接访问某些内存区域,而无需额外的说明,而其他区域则不能。可以将任何变量直接加载到该寄存器的前32K(68000)或2K(ARM)中。获取存储在其他地方的变量将需要额外的指令来计算地址。将更频繁使用的变量放在优先区域中并让编译器知道将允许更有效的代码生成。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top