使用了哪种形式的内存地址空间?
今天,大型平面虚拟地址空间很常见。从历史上看,已经使用了更复杂的地址空间,例如一对基地址和偏移量,一对段号和一个偏移量,一个字地址加上一个字节或其他子对象的索引,依此类推
有时,各种答案和评论断言C / C ++指针本质上是整数。这是一个不正确的C / C ++模型,因为各种地址空间无疑是一些关于指针操作的C规则的原因。例如,不在数组之外定义指针算法简化了对基本和偏移模型中指针的支持。指针转换的限制简化了对地址加额外数据模型的支持。
这种反复出现的断言激发了这个问题。我正在寻找有关各种地址空间的信息,以说明C / C ++指针不一定是一个简单的整数,并且考虑到要支持的各种机器,C / C ++对指针操作的限制是明智的。
有用的信息可能包括:
这是一个广泛的问题,所以我愿意接受有关管理它的建议。我很高兴看到一个通用包容性答案的协作编辑。然而,这可能无法授予应得的声誉。我建议对多个有用的贡献进行投票。
答案 0 :(得分:16)
您可以想象的任何事情都可能被使用过。该
第一个主要划分是字节寻址(所有现代的
架构)和字寻址(IBM 360 / PDP-11之前,但是
我认为现代Unisys大型机仍然是字处理的)。在
单词寻址,char*
和void*
通常会大于。int*
和p
char*
;即使它们不是更大,“字节选择器”
将是高阶位,需要为0,或
除了字节之外的任何东西都会被忽略。 (在PDP-10上,
例如,如果(int)p < (int)(p+1)
是int
,char*
就是{{1}}
虽然{{1}}和{{1}}具有相同的效果,但通常都是错误的
大小。)
在字节寻址机器中,主要变体是分段的 和非分段的体系结构。两者仍然广泛传播 今天,虽然在英特尔32位(分段 具有48位地址的架构),其中一些更广泛 二手操作系统(Windows和Linux)人为限制用户 处理到单个段,模拟平面寻址。
虽然我最近没有经验,但我还是会期待更多 各种嵌入式处理器。特别是在过去,它 嵌入式处理器经常使用哈佛 架构,代码和数据在独立地址 空格(以便将函数指针和数据指针强制转换为 足够大的整数类型,可以比较相等)。
答案 1 :(得分:5)
我会说你问的是错误的问题,除了历史的好奇心。
即使你的系统碰巧使用了一个扁平的地址空间 - 事实上,即使从现在到结束的每个系统都使用一个扁平的地址空间 - 你仍然无法将指针视为整数。
C和C ++标准使各种指针算术“未定义”。在任何系统上,这都会对您产生影响,因为编译器会假设您避免未定义的行为并相应地进行优化。
一个具体的例子,三个月前Valgrind出现了一个非常有趣的错误:
http://comments.gmane.org/gmane.comp.debugging.valgrind.devel/19698
(搜索“未定义的行为”。)
基本上,Valgrind使用小于和大于指针来尝试确定自动变量是否在一定范围内。因为不同聚合中的指针之间的比较是“未定义的”,所以Clang只是优化了比较的所有以返回常量true(或者假,我忘了)。
这个错误本身产生了an interesting StackOverflow question。
因此,虽然原始指针算术定义可能已经迎合了真正的机器,并且这可能是有趣的,但它实际上与今天的编程无关。今天相关的是你不能假设指针的行为像整数,句点,无论你碰巧使用什么系统。 “未定义的行为”并不意味着“有趣的事情发生”;这意味着编译器可以假设您不参与其中。当你这样做时,你会在编译器的推理中引入矛盾;从一个矛盾来看,任何事情都遵循......它只取决于你的编译器有多聪明。
他们一直都变得更聪明。
答案 2 :(得分:0)
存储区切换存储器的形式多种多样。
我在一个具有128 KB总内存的嵌入式系统上工作:64KB RAM和64KB EPROM。指针只有16位,因此即使指向不同的内存位置,进入RAM的指针也可能具有与EPROM中的指针相同的值。
编译器会跟踪指针的类型,以便它可以在取消引用指针之前生成指令以选择正确的存储体。
您可能会争辩说,这就像是段+偏移量,在硬件级别上,它实际上是。但是该段(或更正确的说,存储体)是从指针的类型隐式的,而不是存储为指针的值。如果您在调试器中检查了一个指针,则只会看到一个16位的值。要知道它是到RAM还是ROM的偏移量,您必须知道类型。
例如,Foo *
只能在RAM中,const Bar *
只能在ROM中。如果必须将Bar
复制到RAM,则该复制实际上是另一种类型。 (它不像const / non-const那样简单:
ROM中的所有内容都是const,但并非所有const都位于ROM中。)
所有这些都是用C语言编写的,我知道我们使用了非标准扩展名来完成这项工作。我怀疑100%兼容的C编译器可能无法解决这个问题。
答案 3 :(得分:-1)
从C程序员的角度来看,需要担心三种主要的实现方式:
那些针对具有线性内存模型的计算机,以及设计和/或配置为可用作“高级汇编程序”的计算机的人-标准作者明确表示他们不想排除。禁用优化后,大多数实现都会以这种方式运行。
可用于具有异常内存体系结构的计算机的“高级汇编程序”。
其设计和/或配置使其仅适用于不涉及底层编程的任务的对象,包括启用优化后的clang和gcc。
针对第一种实现方式的内存管理代码通常将与该类型的所有实现方式兼容,该类型的所有实现方式的目标都使用相同的指针和整数表示形式。第二种实现方式的内存管理代码通常需要针对特定的硬件体系结构进行专门定制。不使用线性寻址的平台非常稀少,而且变化很大,除非有人需要为某些特殊的不寻常硬件编写或维护代码(例如,因为它驱动了昂贵的工业设备,而现代控制器却没有) t可用)对任何特定架构的了解都不太有用。
第三种类型的实现仅应用于不需要执行任何内存管理或系统编程任务的程序。因为该标准并不要求所有实现都能够支持此类任务,所以某些编译器编写器(即使是针对线性地址机器)也没有尝试支持其任何有用的语义。甚至有些原则,例如“两个有效指针之间的相等性比较-最糟糕的是-以可能未指定的方式选择的结果0或1都不适用于此类实现。