首先,您打印了一个地址这一事实并不意味着在该地址分配了内存。您只是简单地添加了数字并产生了其他数字。
其次,通过加二得到的编号是比基地址大 8 而不是比基地址大 2,因为当你在 C 中将整数添加到指针时,算术是根据指向的元素完成的,而不是内存中的字节(除非指向的元素是字节)。假设你有一个 int 数组,比如说int x[8]
,并且你有一个指向 的指针x[3]
。向该指针添加两个会产生一个指向 的指针x[5]
,而不是指向超出开头的两个字节的指针x[3]
. 重要的是要记住 C 是一种抽象,而 C 标准指定了该抽象内部发生的情况。在 C 抽象内部,指针运算适用于元素数量,而不是原始内存地址。C 实现(编译器和将 C 代码转换为程序执行的工具)需要对原始内存地址执行任何操作,以实现 C 标准指定的抽象。通常,这意味着编译器在将整数添加到指针时将其乘以元素的大小。所以 2 乘以 4(在 anint
是 4 个字节的机器上),得到的 8 加到基地址上。
第三,你不能依赖这种行为。C 标准仅为指向数组内对象的指针定义指针算法,包括数组末尾的一个虚构对象。此外,指向单个对象的指针就像一个元素的数组。因此,如果您有一个指向p
int 的指针,则可以计算p+0
or p+1
,因为它们指向数组中的唯一对象 ( p+0
) 和数组中最后一个元素之外的虚构对象 ( p+1
)。不允许计算or ,因为它们在数组之外。请注意,这不是取消引用指针的问题(尝试在计算的地址读取或写入内存):即使只是计算p-1
p+2
该地址会导致 C 标准未定义的行为:您的程序可能会崩溃,它可能会给您“正确”的结果,或者它可能会删除您帐户中的所有文件,并且所有这些行为都符合 C 标准.
仅仅计算越界地址不太可能产生这种奇怪的行为。但是,该标准允许这样做,因为某些计算机处理器具有不寻常的地址方案,需要比简单的算术更多的工作。可能仅次于平面地址空间的第二常见地址方案是基地址和偏移量方案。在这种方案中,四字节指针的高 16 位可能包含一个基地址,而低 16 位可能包含一个偏移量。对于给定的基地址 b 和偏移量 o,对应的虚拟地址可能是 4096*b+o。(这样的方案只能解决 2 20字节,并且基数和偏移量的许多不同值可以引用相同的地址。例如,基数 0 和偏移量 4096 与基数 1 和偏移量 0 引用相同的地址。)使用基数和偏移量方案,编译器可以通过仅添加到偏移量并忽略基数来实现指针运算。(这样的 C 实现最多只能支持 65536 字节的数组,该范围仅可通过偏移量寻址。)在这样的实现中,如果您有指向 intp
且编码为 0x0000fffc(基数 0,偏移量 65532)的指针,并且int
是四个字节,那么p+2
将具有值 0x00000004,而不是大于 8 的值 (0x00010004)。
这是一个示例,其中指针运算会产生您不会从平面地址机器中获得的值。很难想象根据 C 标准无效的指针算法会导致崩溃的实现。但是,考虑一个内存必须由进程手动交换的实现,因为处理器没有支持虚拟内存的硬件。在这样的实现中,指针可能包含内存中结构的地址,这些地址描述了磁盘位置和用于管理内存交换的其他信息。在这样的实现中,执行指针运算可能需要读取内存中的结构,因此执行无效指针运算可能会读取无效地址。