【发布时间】:2010-12-17 12:28:38
【问题描述】:
谁能告诉我C语言中far指针和near指针的区别?
【问题讨论】:
谁能告诉我C语言中far指针和near指针的区别?
【问题讨论】:
在 16 位 x86 分段内存架构上,四个寄存器用于引用各自的段:
此架构上的逻辑地址写为segment:offset。现在回答这个问题:
Near 指针(作为偏移量)指向当前段。
远指针使用段信息和偏移量来指向跨段。因此,要使用它们,必须将 DS 或 CS 更改为指定的值,内存将被取消引用,然后 DS/CS 的原始值恢复。请注意,对它们的指针运算不会修改指针的段部分,因此溢出偏移量只会将其环绕。
然后是巨大的指针,它们被规范化为具有给定地址的最高段(与远指针相反)。
在 32 位和 64 位架构上,内存模型使用不同的段,或者根本不使用。
【讨论】:
由于没有人提到 DOS,让我们忘记旧的 DOS PC 计算机,从一般的角度来看这个。然后,非常简化,它是这样的:
任何 CPU 都有一条数据总线,这是 CPU 在一条指令中可以处理的最大数据量,即等于其寄存器的大小。数据总线宽度以位表示:8位,或16位,或64位等。这就是术语“64位CPU”的来源——它指的是数据总线。
任何 CPU 都有一条地址总线,也有一定的总线宽度,以位表示。 CPU 可以直接访问的计算机中的任何内存单元都有唯一的地址。地址总线足够大,可以覆盖您拥有的所有可寻址内存。
例如,如果一台计算机有 65536 字节的可寻址内存,您可以用 16 位地址总线覆盖这些内存,2^16 = 65536。
大多数情况下,但并非总是如此,数据总线宽度与地址总线宽度一样宽。如果它们的大小相同,那就太好了,因为它使 CPU 指令集和为其编写的程序都更清晰。如果 CPU 需要计算一个地址,如果该地址足够小以适合 CPU 寄存器(在涉及地址时通常称为索引寄存器),则会很方便。
非标准关键字 far 和 near 用于描述系统上的指针,您需要在这些系统上寻址超出正常 CPU 地址总线宽度的内存。
例如,具有 16 位数据总线的 CPU 也具有 16 位地址总线可能会很方便。但同一台计算机可能还需要超过 2^16 = 65536 字节 = 64kB 的可寻址内存。
然后,CPU 通常会有特殊指令(稍微慢一些),使其能够寻址超过 64kb 的内存。例如,CPU 可以将其大内存划分为npages(有时也称为 banks、segments 和其他此类术语,可以从一个 CPU 到另一个 CPU 的含义不同),其中每页为 64kB。然后它将有一个“页面”寄存器,在寻址该扩展存储器之前必须先设置该寄存器。同样,在扩展内存中的子程序调用/返回时也会有特殊的指令。
为了让 C 编译器在处理此类扩展内存时生成正确的 CPU 指令,发明了非标准的 near 和 far 关键字。非标准,因为它们没有被 C 标准指定,但它们是事实上的行业标准,几乎每个编译器都以某种方式支持它们。
far 指的是位于扩展内存中的内存,超出了地址总线的宽度。由于它指的是地址,因此您通常在声明指针时使用它。例如:int * far x; 的意思是“给我一个指向扩展内存的指针”。然后编译器就会知道它应该生成访问这些内存所需的特殊指令。同样,使用far 的函数指针将生成特殊指令以跳转到/从扩展内存返回。如果你不使用far,那么你会得到一个指向正常的、可寻址内存的指针,而你最终会指向完全不同的东西。
near主要是为了和far保持一致而包含的;它指的是可寻址内存中的任何内容,相当于常规指针。所以它主要是一个无用的关键字,除了一些你想确保代码放在标准可寻址内存中的极少数情况。然后,您可以将某些内容显式标记为near。最典型的情况是编写中断服务程序的低级硬件编程。它们由硬件从具有固定宽度的中断向量调用,该宽度与地址总线宽度相同。这意味着中断服务程序必须在标准的可寻址内存中。
far 和 near 最著名的用法可能是上面提到的旧 MS DOS PC,它现在被认为是相当古老的,因此没什么兴趣。
但是这些关键字也存在于更现代的 CPU 上!最值得注意的是在嵌入式系统中,市场上几乎所有 8 位和 16 位微控制器系列都存在这些微控制器,因为这些微控制器通常具有 16 位的地址总线宽度,但有时内存超过 64kB。
当您有一个 CPU 需要在地址总线宽度之外寻址内存时,您将需要 far 和 near。通常,此类解决方案是不受欢迎的,因为在它们上编程并始终考虑扩展内存是相当痛苦的。
推动开发 64 位 PC 的主要原因之一实际上是 32 位 PC 已经到了内存使用量开始达到地址总线限制的地步:它们只能寻址 4GB内存。 2^32 = 42.9 亿字节 = 4GB。为了能够使用更多的 RAM,选择要么求助于像 DOS 时代那样繁重的扩展内存解决方案,要么将计算机(包括它们的地址总线)扩展到 64 位。
【讨论】:
near 和 far 仍然是关键字的编译器周围工作。所以你会发现像3D projection matrices with hither and yon clipping planes...
【讨论】:
指针基本上保存地址。众所周知,Intel内存管理分为4段。 因此,当指针指向的地址在同一段内时,它是一个近指针,因此它只需要 2 个字节的偏移量。 另一方面,当指针指向段外(即在另一个段中)的地址时,该指针就是远指针。它由 4 个字节组成:两个用于段,两个用于偏移。
【讨论】:
四个寄存器用于引用 16 位 x86 分段内存架构上的四个段。 DS(数据段)、CS(代码段)、SS(堆栈段)和ES(额外段)。这个平台上的一个逻辑地址是segment:offset,十六进制。
Near 指针(作为偏移量)指向当前段。
远指针使用段信息和偏移量来指向跨段。因此,要使用它们,必须将 DS 或 CS 更改为指定的值,内存将被取消引用,然后 DS/CS 的原始值恢复。请注意,对它们的指针运算不会修改指针的段部分,因此溢出偏移量只会将其环绕。
然后是巨大的指针,它们被规范化为具有给定地址的最高段(与远指针相反)。
在 32 位和 64 位架构上,内存模型使用不同的段,或者根本不使用。
【讨论】:
在 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给出了非常详细和明智的解释“竖起大拇指”!
【讨论】: