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