2012-07-29 84 views
8

我读指针运算了一下,我来到两件事我不明白也不知道这是用C/C++:指针运算

address_expression - address_expression 

address_expression > address_expression 

灿有人请向我解释他们,他们是如何工作以及何时使用。

编辑:

我的意思是说在做,他们生产的东西,如果我只是把两个地址并减去他们

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

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

我明白1 < 2,但如何是地址比另一个更大的一个,并且比较它们在什么

+1

标题宣布C++,但标签包括C,C++。如果目标是使用双语言解决方案的双语言,则标题应该删除C++。 – chux 2017-10-02 16:28:20

回答

16

指针减法得出相同类型的两个指针之间的数组元素的数量。

例如,

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 
+0

谢谢。虽然其他答案提供了更多的细节,并向我解释了很多我不知道的东西。这是最直接的答案,回答我的主要问题 – 2012-07-30 00:23:03

+0

@MohamedAhmedNabil欢迎您! – ouah 2012-07-30 00:24:33

+0

'-'和'>'只适用于指向同一个数组的指针。在其他方面使用它们是未定义的行为。 – fredoverflow 2012-07-30 05:06:39

-1

第一个表达式从另一个中减去一个指针。作为一个简单的例子说明这可能是有用的,考虑一个C字符串。该字符串是在连续内存中,所以如果你有字符串的第一个字符,最后一个字符的地址的地址,你可以通过执行查找的字符串的长度:

int strLength = (last_char_address - first_char_address) + 1; 

这样的指针运算是类型意识,这意味着算术的结果表示两个指针之间的特定类型的元素的数量。在使用char的上述示例中,不同之处在于字符数。这对于例如指向两个structs

类似地,第二个表达式简单地比较指针和结果将是1或0作为一个非常简单的例子,元素的地址阵列5总是>元件的地址:&string[4] > &string[5]是真的。

+0

我的意思是说,如果我只取两个地址并将它们相减,它们会产生什么 而且如果我取两个地址并比较它们的结果或根据 – 2012-07-29 23:57:53

+0

进行比较,则减法产生一个数字。在我的示例中,数字表示地址之间的_distance_或之间的内存地址数。基于参数的比较是真或假 – pb2q 2012-07-29 23:58:52

+0

比较怎么样,两个地址比较的基础是什么? – 2012-07-30 00:00:57

4

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

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

+3

小细节:使用'char'类型,总是减去计数字节,因为'char'被C标准定义为一个字节。 – 2012-07-30 00:07:50

+3

此外,重要的是要注意,如果两个指针不指向同一个超级对象内的子对象(同一个数组内的元素),则“p1 - p2”和“p1 2012-07-30 00:19:23

+1

@DietrichEpp这是有点误导,因为一个字节的C定义不一定是一个八位字节。 – 2016-05-26 13:35:21

-1

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

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

指针算术通常用于指向数据块的指针,而您需要访问不在数据块开头的内容。例如:

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

这将输出:

hello world! 
world! 

在这里,我们的前6个字符后得到的字符串,或"world!" “世界,你好!”。请记住,如果可能的话,应使用std::string。与指针算法非常相似的概念是随机访问迭代器。

减指针可以帮助您找到这两个指针之间的距离。如果有一个指向数组的第一个元素的指针,并且指向通过数组最后一个元素的一个元素,则减去这两个指针可以帮助您查找数组的大小。

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

+3

指针不仅仅是数字。在某些平台上,指针是基地址和偏移量,基地址和偏移量的不同组合可指向相同的位置。 – 2012-07-30 00:16:34

+0

在您评论上述帖子后,我在编辑此内容。这些帮助有用? – vedosity 2012-07-30 00:23:11

-2

您可以在很多方面看待像int这样的地址。唯一的区别是int代表该地址中的大小数。例如,如果int * p碰巧具有例如234(来自某个安全指令,例如p = new int[12];)的值,则它代表地址234.如果我们做p += 1;,则它只是按照int大小来加入。现在p是(假设此例为4字节int)238,又名p[1]。实际上p[x]相当于*(p+x)。你可以比较,就像int一样。在某些情况下,这是有用的,例如在给出的示例中p[0]现在指的是p[1]。这避免了必须执行诸如p = &p[1]之类的不必要的解除引用。

+0

int * p = 234是非常错误的,如果被误导的编译器允许,它是危险的。用g ++ 5.3.0的话来说,它是从'int'到'int *'的无效转换。假设sizeof(int)== 4同样是错误的 – 2016-06-08 15:49:06

+0

当然可以。我的意思是,如果int * p的内部值在某些指令(例如'p = new int [12];')后碰巧是234,我们可以安全地进行指针运算。 – Cosine 2016-06-14 22:24:24

19

这里的几个答案都指出,指针是数字。这不是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标准设定的规则之内的原因之一。

+0

真的很棒的答案:我可以说我读完这些之后学到了一些东西。你真的成功地提供了具体和有启发性的推理,说明为什么它不像“地址只是数字”那么简单,以及为什么规范是特定的(或者说是实现)。我将编辑我自己的答案,使其更好,或完全删除它。谢谢 – pb2q 2017-10-04 00:23:03