C / C ++:指针算术

我正在读指针算术,我遇到了两个我不明白的东西,都知道它的用处

address_expression - address_expression 

并且

 address_expression > address_expression 

有人可以向我解释他们,他们是如何工作,什么时候使用。

编辑:

我的意思是说,如果我只取两个地址并减去它们,它们会产生什么

如果我拿两个地址,并比较它们是什么结果或基于比较

编辑:我现在明白减去地址的结果,但比较地址我仍然没有得到它。

我知道1 <2,但是地址怎么比另一个更大,他们比较的是什么

指针减法产生相同types的两个指针之间的数组元素的数目。

例如,

 int buf[10] = /* initializer here */; &buf[10] - &buf[0]; // yields 10, the difference is 10 elements 

指针比较。 例如,对于>关系运算符:如果左侧的指向数组元素或结构成员位于右侧的指向数组元素或结构成员之后,则操作会产生1 ,否则返回0 。 记得数组和结构是有序的序列。

  &buf[10] > &buf[0]; // 1, &buf[10] element is after &buf[0] element 

这里有几个答案指出,指针是数字。 这不是C标准指定的指针的准确描述。

在很大程度上,您可以将指针看作数字,并将其看作是内存中的地址,前提是(a)您明白指针减法将差异从字节转换为要删除的指针types的元素,(b)了解这个模型破坏的限制。

以下使用1999 C标准(ISO / IEC 9899,第二版,1999-12-01)。 我希望以下内容比提问者要求的更详细,但是鉴于这里的一些错误陈述,我认为应该给出准确和准确的信息。

按照6.5.6的第9段,可以减去指向同一数组的元素的两个指针或减去数组中最后一个元素的指针。 所以,如果你有int a[8], b[4]; ,因为[5]和[2]是同一数组中的元素,所以可以从指向[2]的指针减去指向[5]的指针。 你也可以从一个指向[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。 不pipetypes是什么,情况都是如此。 C实现需要将字节(或其使用的任何单位)的距离转换为适当types的元素。 如果a是每个8字节的双倍数组,则3个元素a[5]-a[2]是3。 如果a是一个字节数组,每个字节的数组, a[5]-a[2]为3,3个元素。

为什么指针不会只是数字? 在一些计算机上,特别是在较旧的计算机上,寻址内存更复杂。 早期的电脑有小的地址空间。 当制造商想要制造更大的地址空间时,他们也想保持与旧软件的一些兼容性。 由于硬件限制,他们还必须实现各种用于寻址存储器的scheme,并且这些scheme可能涉及在存储器和磁盘之间移动数据或者改变处理器中控制地址如何转换为物理存储器位置的特殊寄存器。 对于像这样的机器上的指针,他们必须包含更多的信息,而不仅仅是一个简单的地址。 正因为如此,C标准不仅仅把指针定义为地址,而且让你用它们进行算术运算。 只定义了一个合理数量的算术,并且需要C实现来提供必要的操作来完成这个算术运算,但是没有更多。

即使在现代机器上,也可能会有复杂性。 在Digital的Alpha处理器上,指向函数的指针不包含函数的地址。 它是函数描述符的地址。 该描述符包含函数的地址,并且包含一些正确调用函数所需的附加信息。

对于关系运算符,例如> ,C标准在6.5.8的第5段中说,如上所述,你可以比较相同的指针,你也可以比较指向聚合对象成员的指针(一个结构或联合)。 指向数组成员的指针(或其结束地址)以预期的方式进行比较:指向较高索引元素的指针大于指向较低索引元素的指针。 指向同一个工会的两个成员比较相等。 对于指向结构的两个成员的指针,指向稍后声明的成员的指针大于指向之前声明的成员的指针。

只要你保持在上面的约束之内,那么你可以把指针看作是内存地址的数字。

通常,C实现很容易提供C标准所要求的行为。 即使计算机有复合指针scheme(如基址和偏移量),通常数组中的所有元素都将使用相同的基地址,并且结构中的所有元素将使用相同的基地址。 因此,编译器可以简单地减去或比较指针的偏移部分,以获得所需的差异或比较。

但是,如果您在这样的计算机上减去指向不同arrays的指针,则可能会得到奇怪的结果。 即使指向内存中的较低地址,由基地址和偏移形成的位模式也有可能比另一个指针显得更大(当解释为单个整数时)。 这是您必须保持在C标准设定的规则之内的原因之一。

减去两个指针地址将返回该types的元素数量。

所以如果你有一个整数和两个指针的数组,减去这些指针将返回int值的数量, 而不是字节数。 与chartypes相同。 所以你需要小心这个,特别是如果你正在使用字节缓冲区或宽字符,你的expression式正在计算正确的值。 如果您需要基于字节的缓冲区偏移量来存储不使用单个字节的内容(int,short等),则需要先将指针转换为char *。

指针通常可以被认为是代表内存地址的数字,比如0x0A31FCF20(或者十进制的2736770848),或者0xCAFEDEAD(有时系统使用这个来表示错误,我不记得细节)。

指针比较通常用于对指针数组进行sorting。 当您需要检查指针是否在指针列表中时,对指针进行sorting是有帮助的; 如果列表已sorting,则不必查看列表中的每个元素来确定指针是否在列表中。 您需要使用比较来对列表进行sorting。

当你有一个指向数据块的指针时,经常使用指针算术,而你需要访问那些不在数据块开头的东西。 例如:

 const char *string = "hello world!" const char *substring = string+6; std::cout << string << "\n"; std::cout << substring << std::endl; 

这将输出:

 hello world! world! 

我们在“hello world!”或"world!"的前6个字符之后得到了string"world!" 。 请记住,如果可能的话,您应该在可用的地方使用std::string 。 随机访问迭代器与指针algorithm非常相似。

减指针可以帮助您find这两个指针之间的距离。 如果你有一个指向数组的第一个元素的指针和一个指向数组最后一个元素的指针,减去这两个指针可以帮助你find数组的大小。

另一种可能将指针视为整数的情况是链接列表的优化版本,称为XOR链接列表。 你可以在这里find更多的细节。 如果你愿意的话,我可以扩展它。 让我知道在评论中。

第一个expression式从另一个指针中减去一个指针。 作为一个简单的例子,为什么这可能是有用的,考虑一个Cstring。 该string是在连续的内存中,所以如果你有string的第一个字符的地址和最后一个字符的地址,你可以通过执行findstring的长度:

 int strLength = (last_char_address - first_char_address) + 1; 

这种指针运算是types感知的 ,意味着算术结果表示两个指针之间的特定types的元素的数量。 在上面使用char例子中,不同的是字符数。 这类似的工作,例如指向两个structs指针。

同样的,你的第二个expression式只是比较指针,结果将是1或者0。作为一个非常简单的例子,数组元素5的地址总是>元素4的地址: &string[4] > &string[5]是真的。

你可以在很多方面像int一样处理一个地址。 唯一的区别是int表示该地址中的大小数量。 例如,如果int * p恰好具有234的值(来自某些安全指令,例如p = new int[12]; ),则它表示地址234.如果我们做p += 1; ,它只是增加,在整数大小方面。 现在p是(假设这个例子是4字节的int)238,又叫p[1] 。 实际上, p[x]相当于*(p+x) 。 你可以比较,就像一个int。 在一些情况下,这是有用的,例如在给出的例子中, p[0]现在指的是p[1] 。 这避免了必须做一些像p = &p[1]不必要的解引用。