这里有几个答案表明指针是数字。这不是 C 标准指定的指针的准确描述。
在很大程度上,您可以将指针视为数字和内存中的地址,前提是(a)您了解指针减法将差异从字节转换为元素(被减去的指针的类型),并且(b)您了解此模型打破的限制。
以下使用 1999 C 标准 (ISO/IEC 9899, Second edition, 1999-12-01)。我希望以下内容比提问者要求的更详细,但是,鉴于这里的一些错误陈述,我认为应该提供准确和准确的信息。
根据 6.5.6 第 9 段,您可以减去两个指向同一数组元素的指针或指向数组最后一个元素的指针。所以,如果你有int a[8], b[4];
,你可以从指向 a[2] 的指针中减去指向 a[5] 的指针,因为 a[5] 和 a[2] 是同一个数组中的元素。您还可以从指向 a[8] 的指针中减去指向 a[5] 的指针,因为 a[8] 是数组最后一个元素的后一个元素。(a[8] 不在数组中;a[7] 是最后一个元素。)您不能从指向 b[2] 的指针中减去指向 a[5] 的指针,因为 a[5] 不在与 b[2] 相同的数组。或者,更准确地说,如果你做这样的减法,行为是不确定的。请注意,未指定的不仅仅是结果;你不能指望你会得到一些可能是荒谬的数字:行为未定义。根据 C 标准,这意味着 C 标准没有说明任何结果。你的程序可以给你一个合理的答案,或者它可以中止,或者它可以删除文件,所有这些结果都将符合 C 标准。
如果您执行允许的减法,则结果是从第二个指向元素到第一个指向元素的元素数。因此,a[5]-a[2]
是 3,并且a[2]-a[5]
是 -3。不管是什么类型a
都是如此。需要 C 实现将字节(或它使用的任何单位)的距离转换为适当类型的元素。如果a
是一个双精度数组,每个 8 字节,a[5]-a[2]
则为 3,对于 3 个元素。如果a
是一个每个字节的 char 数组,a[5]-a[2]
则为 3,对于 3 个元素。
为什么指针永远不仅仅是数字?在某些计算机上,尤其是较旧的计算机上,寻址内存更为复杂。早期计算机的地址空间很小。当制造商想要制造更大的地址空间时,他们也想保持与旧软件的一些兼容性。由于硬件限制,他们还必须实施各种寻址内存的方案,这些方案可能涉及在内存和磁盘之间移动数据或更改处理器中控制如何将地址转换为物理内存位置的特殊寄存器。对于在这样的机器上工作的指针,它们必须包含更多的信息,而不仅仅是一个简单的地址。因此,C 标准不仅仅将指针定义为地址,还允许您对地址进行算术运算。
即使在现代机器上,也可能存在并发症。在 Digital 的 Alpha 处理器上,指向函数的指针不包含函数的地址。它是函数描述符的地址。该描述符包含函数的地址,并且它包含一些正确调用函数所必需的附加信息。
关于关系运算符,例如>
,C 标准在 6.5.8 第 5 段中说,您可以比较可以减去的相同指针,如上所述,您还可以比较指向聚合对象成员的指针(a结构或联合)。指向数组成员(或其结束地址)的指针以预期的方式进行比较:指向较高索引元素的指针大于指向较低索引元素的指针。指向同一个联合的两个成员的指针比较相等。对于指向结构的两个成员的指针,指向后面声明的成员的指针大于指向前面声明的成员的指针。
只要您保持在上述约束范围内,您就可以将指针视为内存地址的数字。
通常,C 实现很容易提供 C 标准所要求的行为。即使计算机具有复合指针方案,例如基地址和偏移量,通常数组的所有元素都将使用彼此相同的基地址,而结构的所有元素将使用彼此相同的基地址。因此编译器可以简单地减去或比较指针的偏移部分以获得所需的差异或比较。
但是,如果你在这样的计算机上减去指向不同数组的指针,你会得到奇怪的结果。由基地址和偏移量形成的位模式可能看起来比另一个指针更大(当解释为单个整数时),即使它指向内存中的较低地址。这是您必须遵守 C 标准设定的规则的原因之一。