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