20

使用了哪些形式的内存地址空间?

今天,一个大的平面虚拟地址空间很常见。从历史上看,已经使用了更复杂的地址空间,例如基地址和偏移量的对、段号和偏移量的对、字地址加上字节或其他子对象的一些索引等等.

不时地,各种答案和评论断言 C(或 C++)指针本质上是整数。这对于 C(或 C++)来说是一个不正确的模型,因为地址空间的多样性无疑是 C(或 C++)中一些关于指针操作的规则的原因。例如,不在数组之外定义指针算术简化了对基本和偏移模型中指针的支持。指针转换的限制简化了对地址加额外数据模型的支持。

这个反复出现的断言引发了这个问题。我正在寻找有关各种地址空间的信息,以说明 C 指针不一定是简单的整数,并且鉴于要支持的机器种类繁多,C 对指针操作的限制是明智的。

有用的信息可能包括:

  • 具有各种地址空间和这些空间描述的计算机体系结构示例。
  • 当前制造的机器中仍在使用的各种地址空间的示例。
  • 对文档或解释的引用,尤其是 URL。
  • 详细说明地址空间如何激发 C 指针规则。

这是一个广泛的问题,因此我愿意接受有关管理它的建议。我很高兴看到对一个普遍包容的答案进行协作编辑。但是,这可能无法获得应得的声誉。我建议对多个有用的贡献进行投票。

4

4 回答 4

17

几乎任何你能想象到的东西都可能被使用过。第一个主要划分是字节寻址(所有现代架构)和字寻址(IBM 360/PDP-11 之前的版本,但我认为现代 Unisys 大型机仍然是字寻址)。在字寻址中,char*通常void*会大于int*; 即使它们不是更大,“字节选择器”也将位于高位,要求为 0,或者对于字节以外的任何内容都将被忽略。(例如,在 PDP-10 上,如果p是 a char*(int)p < (int)(p+1)通常会是错误的,即使它int具有char*相同的大小。)

在字节寻址机器中,主要的变体是分段和非分段架构。尽管在英特尔 32 位(具有 48 位地址的分段架构)的情况下,两者今天仍然广泛传播,但一些更广泛使用的操作系统(Windows 和 Linux)人为地将用户进程限制为单个段,模拟平面寻址。

尽管我没有最近的经验,但我希望嵌入式处理器的种类更多。特别是在过去,嵌入式处理器经常使用哈佛架构,其中代码和数据位于独立的地址空间中(以便函数指针和数据指针,转换为足够大的整数类型,可以比较相等)。

于 2012-12-30T15:34:07.643 回答
6

我会说你问错了问题,除了历史好奇心。

即使你的系统碰巧使用了一个平面地址空间——事实上,即使从现在到时间结束的每个系统都使用一个平面地址空间——你仍然不能将指针视为整数。

C 和 C++ 标准使各种指针算术“未定义”。这可能会影响您现在,在任何系统上,因为编译器会假设您避免未定义的行为并相应地进行优化。

举个具体的例子,三个月前 Valgrind 中出现了一个非常有趣的错误:

https://sourceforge.net/p/valgrind/mailman/message/29730736/

(单击“查看整个线程”,然后搜索“未定义行为”。)

基本上,Valgrind 使用小于和大于指针来尝试确定自动变量是否在特定范围内。因为不同聚合中的指针之间的比较是“未定义的”,所以 Clang 只是优化了所有比较以返回一个常量 true(或 false;我忘记了)。

这个错误本身产生了一个有趣的 StackOverflow 问题

因此,虽然最初的指针算术定义可能迎合了真实的机器,而且这本身可能很有趣,但它实际上与今天的编程无关。今天相关的是,无论您碰巧使用的是什么系统,您都不能简单地假设指针的行为类似于整数、句点。“未定义的行为”并不意味着“发生了一些有趣的事情”;这意味着编译器可以假设您不参与其中。当你这样做时,你在编译器的推理中引入了一个矛盾;从矛盾中,任何事情都会发生......这仅取决于您的编译器有多聪明。

他们总是变得更聪明。

于 2012-12-31T22:53:44.513 回答
2

有多种形式的组交换存储器。

我在一个总内存为 128 KB 的嵌入式系统上工作:64 KB 的 RAM 和 64 KB 的 EPROM。指针只有 16 位,因此指向 RAM 的指针可以具有与 EPROM 中的指针相同的值,即使它们指向不同的内存位置。

编译器跟踪指针的类型,以便它可以在取消引用指针之前生成指令以选择正确的组。

您可能会说这就像段 + 偏移量,在硬件级别上,它本质上是。但是段(或更准确地说,银行)是指针类型隐含的,而不是存储为指针的值。如果您在调试器中检查了一个指针,您只会看到一个 16 位的值。要知道它是对 RAM 还是 ROM 的偏移,您必须知道类型。

例如,Foo *只能在 RAM 中,const Bar *也只能在 ROM 中。如果您必须将 a 复制Bar到 RAM 中,则该副本实际上将是不同的类型。(它不像 const/non-const 那样简单:ROM 中的所有内容都是 const,但并非所有 const 都在 ROM 中。)

这一切都在 C 中,我知道我们使用非标准扩展来完成这项工作。我怀疑一个 100% 兼容的 C 编译器可能无法处理这个问题。

于 2019-06-25T18:05:47.163 回答
-2

从 C 程序员的角度来看,需要担心三种主要的实现方式:

  1. 那些以具有线性内存模型的机器为目标,并且被设计和/或配置为可用作“高级汇编程序”的那些——标准的作者明确表示他们不希望排除. 当优化被禁用时,大多数实现都会以这种方式运行。

  2. 那些可用作具有不寻常内存架构的机器的“高级汇编程序”。

  3. 那些其设计和/或配置使其仅适用于不涉及低级编程的任务,包括启用优化时的 clang 和 gcc。

针对第一种实现类型的内存管理代码通常与该类型的所有实现兼容,其目标使用相同的指针和整数表示。第二种实现的内存管理代码通常需要针对特定​​的硬件架构进行专门定制。不使用线性寻址的平台非常少见,而且种类繁多,除非需要为某些特定的不寻常硬件编写或维护代码(例如,因为它驱动昂贵的工业设备,而更现代的控制器却不是这样) t 可用)任何特定架构的知识都不太可能有太大用处。

第三种类型的实现应该只用于不需要执行任何内存管理或系统编程任务的程序。因为该标准不要求所有实现都能够支持此类任务,所以一些编译器编写者——即使是针对线性地址机器——也不会尝试支持其中的任何有用语义。甚至某些原则,例如“两个有效指针之间的相等比较将 - 在最坏的情况下 - 以可能未指定的方式选择产生 0 或 1 不适用于此类实现。

于 2019-06-25T16:06:05.813 回答