【问题标题】:What is the difference between far pointers and near pointers?远指针和近指针有什么区别?
【发布时间】:2010-12-17 12:28:38
【问题描述】:

谁能告诉我C语言中far指针和near指针的区别?

【问题讨论】:

    标签: c pointers


    【解决方案1】:

    在 16 位 x86 分段内存架构上,四个寄存器用于引用各自的段:

    • DS → 数据段
    • CS → 代码段
    • SS → 堆栈段
    • ES → 额外段

    此架构上的逻辑地址写为segment:offset。现在回答这个问题:

    • Near 指针(作为偏移量)指向当前段。

    • 远指针使用段信息和偏移量来指向跨段。因此,要使用它们,必须将 DS 或 CS 更改为指定的值,内存将被取消引用,然后 DS/CS 的原始值恢复。请注意,对它们的指针运算不会修改指针的段部分,因此溢出偏移量只会将其环绕。

    • 然后是巨大的指针,它们被规范化为具有给定地址的最高段(与远指针相反)。

    在 32 位和 64 位架构上,内存模型使用不同的段,或者根本不使用。

    【讨论】:

    • 如果你解释什么是段(以及一个偏移量),这会更清楚。 DOS 使用的“段”有点神秘,恕我直言。
    【解决方案2】:

    由于没有人提到 DOS,让我们忘记旧的 DOS PC 计算机,从一般的角度来看这个。然后,非常简化,它是这样的:


    任何 CPU 都有一条数据总线,这是 CPU 在一条指令中可以处理的最大数据量,即等于其寄存器的大小。数据总线宽度以位表示:8位,或16位,或64位等。这就是术语“64位CPU”的来源——它指的是数据总线。

    任何 CPU 都有一条地址总线,也有一定的总线宽度,以位表示。 CPU 可以直接访问的计算机中的任何内存单元都有唯一的地址。地址总线足够大,可以覆盖您拥有的所有可寻址内存。

    例如,如果一台计算机有 65536 字节的可寻址内存,您可以用 16 位地址总线覆盖这些内存,2^16 = 65536。

    大多数情况下,但并非总是如此,数据总线宽度与地址总线宽度一样宽。如果它们的大小相同,那就太好了,因为它使 CPU 指令集和为其编写的程序都更清晰。如果 CPU 需要计算一个地址,如果该地址足够小以适合 CPU 寄存器(在涉及地址时通常称为索引寄存器),则会很方便。

    非标准关键字 farnear 用于描述系统上的指针,您需要在这些系统上寻址超出正常 CPU 地址总线宽度的内存。

    例如,具有 16 位数据总线的 CPU 也具有 16 位地址总线可能会很方便。但同一台计算机可能还需要超过 2^16 = 65536 字节 = 64kB 的可寻址内存。

    然后,CPU 通常会有特殊指令(稍微慢一些),使其能够寻址超过 64kb 的内存。例如,CPU 可以将其大内存划分为npages(有时也称为 bankssegments 和其他此类术语,可以从一个 CPU 到另一个 CPU 的含义不同),其中每页为 64kB。然后它将有一个“页面”寄存器,在寻址该扩展存储器之前必须先设置该寄存器。同样,在扩展内存中的子程序调用/返回时也会有特殊的指令。

    为了让 C 编译器在处理此类扩展内存时生成正确的 CPU 指令,发明了非标准的 nearfar 关键字。非标准,因为它们没有被 C 标准指定,但它们是事实上的行业标准,几乎每个编译器都以某种方式支持它们。

    far 指的是位于扩展内存中的内存,超出了地址总线的宽度。由于它指的是地址,因此您通常在声明指针时使用它。例如:int * far x; 的意思是“给我一个指向扩展内存的指针”。然后编译器就会知道它应该生成访问这些内存所需的特殊指令。同样,使用far 的函数指针将生成特殊指令以跳转到/从扩展内存返回。如果你不使用far,那么你会得到一个指向正常的、可寻址内存的指针,而你最终会指向完全不同的东西。

    near主要是为了和far保持一致而包含的;它指的是可寻址内存中的任何内容,相当于常规指针。所以它主要是一个无用的关键字,除了一些你想确保代码放在标准可寻址内存中的极少数情况。然后,您可以将某些内容显式标记为near。最典型的情况是编写中断服务程序的低级硬件编程。它们由硬件从具有固定宽度的中断向量调用,该宽度与地址总线宽度相同。这意味着中断服务程序必须在标准的可寻址内存中。


    farnear 最著名的用法可能是上面提到的旧 MS DOS PC,它现在被认为是相当古老的,因此没什么兴趣。

    但是这些关键字也存在于更现代的 CPU 上!最值得注意的是在嵌入式系统中,市场上几乎所有 8 位和 16 位微控制器系列都存在这些微控制器,因为这些微控制器通常具有 16 位的地址总线宽度,但有时内存超过 64kB。

    当您有一个 CPU 需要在地址总线宽度之外寻址内存时,您将需要 farnear。通常,此类解决方案是不受欢迎的,因为在它们上编程并始终考虑扩展内存是相当痛苦的。

    推动开发 64 位 PC 的主要原因之一实际上是 32 位 PC 已经到了内存使用量开始达到地址总线限制的地步:它们只能寻址 4GB内存。 2^32 = 42.9 亿字节 = 4GB。为了能够使用更多的 RAM,选择要么求助于像 DOS 时代那样繁重的扩展内存解决方案,要么将计算机(包括它们的地址总线)扩展到 64 位。

    【讨论】:

    • “用于描述系统上的指针,在这些系统中,您需要对超出正常 CPU 地址总线宽度的内存进行寻址”——不,8086 有一个 20 位地址总线。内存分段是关于在不扩大指针大小的情况下生成更宽的地址。 “这就是术语“64 位 CPU”的来源——它指的是数据总线。 — 不,它指的是整数寄存器宽度。数据总线通常更宽,L1 高速缓存能够加载 128 位的 SIMD 寄存器,更高级别的内存层次结构更宽。地址总线通​​常更窄; MMU 可能没有 64 位物理地址空间。
    • 推动64位PC开发,见Linus Torvalds about PAE
    • 时不时你会发现编写的代码非常关注可移植性,以至于它们在 nearfar 仍然是关键字的编译器周围工作。所以你会发现像3D projection matrices with hither and yon clipping planes...
    【解决方案3】:

    Far 和 Near 指针用于 DOS 等旧平台。

    我认为它们与现代平台无关。但是您可以了解它们herehere(正如其他答案所指出的那样)。基本上,far 指针是一种扩展计算机中可寻址内存的方法。即,在 16 位平台上寻址超过 64k 的内存。

    【讨论】:

    • 我相信某些操作系统在 32 位系统上可以访问超过 4GB 的内存。在这种情况下,这种扩展指针类型是必要的。但是,操作系统很少允许用户模式应用程序在这样的环境中访问超过 4GB 的空间。
    • 是的。这是因为 x86 物理地址总线对于 32 位机器来说是 36 位宽的。但是逻辑地址空间仍然限制在 32 位。 PAE 扩展使“额外”内存可用(实际上是通过 bankswitching,IIRC)真正的问题是 MMU 或多或少被排除在外(没有 mmap、COW 等)。但是内存可以用作自己缓冲的应用程序的磁盘缓冲区。或处理银行切换和 MMU 操作的操作系统。
    • PAE 使用 MMU 的 PTE 中的额外位来处理页框信息的四个额外位。据我所知,没有银行转换。
    【解决方案4】:

    指针基本上保存地址。众所周知,Intel内存管理分为4段。 因此,当指针指向的地址在同一段内时,它是一个近指针,因此它只需要 2 个字节的偏移量。 另一方面,当指针指向段外(即在另一个段中)的地址时,该指针就是远指针。它由 4 个字节组成:两个用于段,两个用于偏移。

    【讨论】:

    • 欢迎来到 StackOverflow。当您从几年前看到这样的问题并获得了相当多的赞成票时,当然仍然允许回答该问题,但只有在您的答案写得很好,并提供了其他答案都没有提供的准确的新信息。我不确定您的回答是否符合该标准。
    • 'Intel内存管理分为4段'?可能有 4 个段寄存器(我不确定),但有很多超过 4 个段。您还没有真正解释这对于 16 位系统是如何工作的,其中段和偏移量是必要的。然后,您应该涵盖 32 位系统,并提及 64 位系统是否有任何显着不同(而不仅仅是更大)的特征。
    【解决方案5】:

    四个寄存器用于引用 16 位 x86 分段内存架构上的四个段。 DS(数据段)、CS(代码段)、SS(堆栈段)和ES(额外段)。这个平台上的一个逻辑地址是segment:offset,十六进制。

    Near 指针(作为偏移量)指向当前段。

    远指针使用段信息和偏移量来指向跨段。因此,要使用它们,必须将 DS 或 CS 更改为指定的值,内存将被取消引用,然后 DS/CS 的原始值恢复。请注意,对它们的指针运算不会修改指针的段部分,因此溢出偏移量只会将其环绕。

    然后是巨大的指针,它们被规范化为具有给定地址的最高段(与远指针相反)。

    在 32 位和 64 位架构上,内存模型使用不同的段,或者根本不使用。

    【讨论】:

    • 这些寄存器实际上用于不同的目的。正如您提到自己的代码,数据,堆栈,额外寄存器。当然也有 AX,BX,DX,CX。但是 CS 用于 CODE,DS 用于 DATA,SS 用于 Stack,ES 用于扩展现有数据和代码变量。 DOS 时代的 AX/BX/CX/DX 非常重要。与任何其他寄存器一样。当调用中断函数 (INT) 时,其中很多都依赖于实际的注册表值。
    【解决方案6】:

    在 DOS 中处理寄存器有点有趣。和细分市场。所有关于 RAM 的最大计数容量。

    今天它几乎无关紧要。您需要阅读的只是关于虚拟/用户空间和内核的区别。

    自从 win nt4(当他们从 *nix 窃取想法时)微软程序员开始使用所谓的用户/内核内存空间。 并从那时起避免直接访问物理控制器。从那时起,也解决了处理直接访问内存段的问题。 - 一切都通过操作系统变成了 R/W。

    但是,如果您坚持理解和操作远/近指针,请查看 linux 内核源代码及其工作原理 - 我猜您会更新回来。

    如果你还需要在 DOS 中使用 CS(代码段)/DS(数据段)。看看这些:

    https://en.wikipedia.org/wiki/Intel_Memory_Model http://www.digitalmars.com/ctg/ctgMemoryModel.html

    我想在下面指出完美的答案..来自伦丁。我懒得正确回答。 Lundin给出了非常详细和明智的解释“竖起大拇指”!

    【讨论】:

      猜你喜欢
      • 2011-04-04
      • 2013-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多