为什么我们不能比较不指向同一数组中元素的指针?
Why we can't compare pointers which don't point to elements within the same array?
我一直在学习 C 语言并关注 Yashavant P. Kanetkar 的 "Let Us C"。
指针章节中有一行说我们只能比较即小于 (<) 和大于 (>) 指向同一数组中元素的指针。
为什么比较任意指针无效?
因为 C 对主机没有任何假设,并且没有什么能阻止后者在两个完全独立的地址空间中分配两个数组。
It's not just about theoretical exotic architectures either。 x86 机器的 16 位编译器提供了两种指针。近指针有 16 位宽并且表现得像你期望的那样;但是,它们只允许您访问 64k 的 RAM。如果你想访问超过 64k 的 RAM(不是每个块 64K:整个程序 64K!)你必须使用远指针。
远指针为 32 位宽,由两个 16 位半部分组成,segment
和 offset
;例如 1234:0000
是一个具有段 0x1234
和偏移量 0 的指针。实际内存地址是 segment * 16 + offset
。通常,farmalloc
返回一个偏移量为零的指针,而指针运算仅修改了偏移量。所以你可以
char *x = farmalloc(64); // returns 1234:0000 for address 0x12340
char *y = farmalloc(64); // returns 1238:0000 for address 0x12380
现在,如果您计算 x + 128
,结果是 1234:0080
,地址为 0x123C0。它比较 less than
1238:0000(因为 0x1234 < 0x1238)但它指向更高的地址(因为 0x123C0 > 0x1238)。
为什么?因为将 128 加到指向 64 字节对象的 x
是未定义的行为。
memory model 编译器设置定义了指针的默认大小是 near 还是 far。例如,"small" 内存模型有 64K 用于代码,64K 用于所有全局变量、自动变量(堆栈)和 malloc 堆。请注意,代码位于单独的段中,因此您不能只使用 16 位 ("near") 函数指针并取消引用它来读取机器语言!如果必须这样做,则必须要求编译器将代码与其余代码放在同一段中("tiny" 内存模型)。
某些内存模型让编译器始终使用远指针,如果数据+堆栈+堆超过 64K("compact" 或 "large" 内存模型),这会更慢但有必要。
代码和数据的大小也不同,所以你可以有一个内存模型,其中函数指针很近而数据指针很远,反之亦然。上述 "compact" 模型(64K 代码限制但数据远指针)和双重 "medium" 模型(代码远指针,64K 数据限制)就是这种情况。
还有一种方法可以让编译器对所有内容使用平面 32 位指针(所谓的 "huge" 内存模型),但是速度很慢,而且没有人使用它。
Undefined behavior 适用于此处。不能比较两个指针,除非它们都指向同一个对象或指向该对象末尾后的第一个元素。
我一直在学习 C 语言并关注 Yashavant P. Kanetkar 的 "Let Us C"。
指针章节中有一行说我们只能比较即小于 (<) 和大于 (>) 指向同一数组中元素的指针。
为什么比较任意指针无效?
因为 C 对主机没有任何假设,并且没有什么能阻止后者在两个完全独立的地址空间中分配两个数组。
It's not just about theoretical exotic architectures either。 x86 机器的 16 位编译器提供了两种指针。近指针有 16 位宽并且表现得像你期望的那样;但是,它们只允许您访问 64k 的 RAM。如果你想访问超过 64k 的 RAM(不是每个块 64K:整个程序 64K!)你必须使用远指针。
远指针为 32 位宽,由两个 16 位半部分组成,segment
和 offset
;例如 1234:0000
是一个具有段 0x1234
和偏移量 0 的指针。实际内存地址是 segment * 16 + offset
。通常,farmalloc
返回一个偏移量为零的指针,而指针运算仅修改了偏移量。所以你可以
char *x = farmalloc(64); // returns 1234:0000 for address 0x12340
char *y = farmalloc(64); // returns 1238:0000 for address 0x12380
现在,如果您计算 x + 128
,结果是 1234:0080
,地址为 0x123C0。它比较 less than
1238:0000(因为 0x1234 < 0x1238)但它指向更高的地址(因为 0x123C0 > 0x1238)。
为什么?因为将 128 加到指向 64 字节对象的 x
是未定义的行为。
memory model 编译器设置定义了指针的默认大小是 near 还是 far。例如,"small" 内存模型有 64K 用于代码,64K 用于所有全局变量、自动变量(堆栈)和 malloc 堆。请注意,代码位于单独的段中,因此您不能只使用 16 位 ("near") 函数指针并取消引用它来读取机器语言!如果必须这样做,则必须要求编译器将代码与其余代码放在同一段中("tiny" 内存模型)。
某些内存模型让编译器始终使用远指针,如果数据+堆栈+堆超过 64K("compact" 或 "large" 内存模型),这会更慢但有必要。
代码和数据的大小也不同,所以你可以有一个内存模型,其中函数指针很近而数据指针很远,反之亦然。上述 "compact" 模型(64K 代码限制但数据远指针)和双重 "medium" 模型(代码远指针,64K 数据限制)就是这种情况。
还有一种方法可以让编译器对所有内容使用平面 32 位指针(所谓的 "huge" 内存模型),但是速度很慢,而且没有人使用它。
Undefined behavior 适用于此处。不能比较两个指针,除非它们都指向同一个对象或指向该对象末尾后的第一个元素。