C++ 指针加法与减法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3599645/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:14:37  来源:igfitidea点击:

Pointer addition vs subtraction

c++pointersmath

提问by Chubsdad

$5.7 -

5.7 美元 -

"[..]For addition, either both operands shall have arithmetic or enumeration type, or one operand shall be a pointer to a completely defined object type and the other shall have integral or enumeration type.

“[..]对于加法,要么两个操作数都应具有算术或枚举类型,要么一个操作数应是指向完全定义的对象类型的指针,另一个应具有整数或枚举类型。

2 For subtraction, one of the following shall hold: — both operands have arithmetic or enumeration type; or — both operands are pointers to cv-qualified or cv-unqualified versions of the same completely defined object type; or — the left operand is a pointer to a completely defined object type and the right operand has integral or enumeration type.

2 对于减法,应满足以下条件之一: ——两个操作数都具有算术或枚举类型;或 — 两个操作数都是指向完全定义的相同对象类型的 cv 限定或 cv 非限定版本的指针;或 — 左操作数是指向完全定义的对象类型的指针,右操作数具有整数或枚举类型。

int main(){
        int buf[10];
        int *p1 = &buf[0];
        int *p2 = 0;

        p1 + p2;       // Error

        p1 - p2;       // OK
}

So, my question is why'pointer addition' is not supported in C++ but 'pointer subtraction' is?

所以,我的问题是为什么C++ 不支持“指针加法”,而“指针减法”支持?

回答by Brian Hooper

The difference between two pointers means the number of elements of the type that would fit between the targets of the two pointers. The sum of two pointers means...er...nothing, which is why it isn't supported.

两个指针之间的差异意味着适合两个指针的目标之间的类型元素的数量。两个指针的总和意味着......呃......没有,这就是它不受支持的原因。

回答by tenfour

The result of subtraction is distance (useful).

减法的结果是距离(有用)。

The result of adding a pointer and a distance is another meaningful pointer.

添加一个指针和一个距离的结果是另一个有意义的指针。

The result of adding 2 pointers is another pointer, this time meaningless though.

添加 2 个指针的结果是另一个指针,但这次毫无意义。

It's the same reason there are distinct TimeSpan and DateTime objects in most libraries.

这与大多数库中存在不同的 TimeSpan 和 DateTime 对象的原因相同。

回答by Leonid

The first thing that comes to mind is that it doesn't make sense to do pointer addition, so it's not supported. If you have 2 pointers 0x45ff23dd, 0x45ff23ed. What does it mean to add them?? Some memory out-of-bounds. And people in standard comittee have not found good enough reasons to support stuff like that, and rather warn you at compile time about possible problem. While pointer subtraction is fine because it indicates memory distance, which is often useful.

首先想到的是做指针添加没有意义,所以不支持。如果你有2个指针0x45ff23dd, 0x45ff23ed。添加它们是什么意思?一些内存越界。标准委员会中的人还没有找到足够好的理由来支持这样的东西,而是在编译时警告您可能出现的问题。虽然指针减法很好,因为它表示内存距离,这通常很有用。

回答by Alex F

Result of pointer subtraction is number of objects between two memory addresses. Pointer addition doesn't mean anything, this is why it is not allowed.

指针相减的结果是两个内存地址之间的对象数。指针添加没有任何意义,这就是不允许的原因。

回答by Philip Potter

Because adding two pointers doesn't make sense.

因为添加两个指针没有意义。

Consider I have two ints in memory at 0x1234and 0x1240. The difference between these addresses is 0xcand is a distancein memory. The sum is 0x2474and doesn't correspond to anything meaningful.

考虑我int在内存中有两个s0x12340x1240。这些地址之间的区别是0xc并且是内存中的距离。总和0x2474不对应任何有意义的东西。

You canhowever, add a pointer to an integer to get another pointer. This is what you do when you index into an array: p[4] means *(p + 4) which means "the thing stored at the address 4 units past this address."

但是,您可以添加指向整数的指针以获取另一个指针。这就是在对数组进行索引时所做的事情:p[4] 表示 *(p + 4),这表示“存储在该地址后 4 个单位的地址处的东西”。

In general, you can determine the "pointerness" of an arithmetic operation by assigning each pointer a value 1 and each integer a value zero. If the result is 1, you've got a pointer; if it's 0, you've got an integer; if it's any other value, you have something that doesn't make sense. Examples:

通常,您可以通过为每个指针分配值 1 和为每个整数分配值 0 来确定算术运算的“指针性”。如果结果是 1,你就有了一个指针;如果它是 0,你有一个整数;如果它是任何其他值,那么您就有一些没有意义的东西。例子:

/* here p,q,r are pointers, i,j,k are integers */
p + i; /* 1 + 0 == 1 => p+i is a pointer */
p - q; /* 1 - 1 == 0 => p-q is an integer */
p + (q-r); /* 1 + (1-1) == 1 => pointer */

回答by J-a-n-u-s

N.B. No claims on C standards here.

注意这里没有关于 C 标准的声明。

As a quick addendum to @Brian Hooper's answer, "[t]he sum of two pointers means...er...nothing" however the sum of a pointer and an integer allows you to offset from the initial pointer.

作为@Brian Hooper 答案的快速附录,“[t]he 两个指针的总和意味着......呃......没有”但是指针和整数的总和允许您从初始指针偏移。

Subtracting a higher value pointer from a lower value pointer gives you the offset between the two. Note that I am not accounting for memory paging here; I'm assuming the memory values are both within the accessible scope of the process.

从较低值的指针中减去较高值的指针为您提供两者之间的偏移量。请注意,这里我没有考虑内存分页;我假设内存值都在进程的可访问范围内。

So if you have a pointer to a series of consecutive memory locations on the heap, or an array of memory locations on the stack (whose variable name decays to a pointer), these pointers (the real pointer and the one that decays to a pointer) will point to the fist memory location question (i.e. element [0]). Adding an integer value to the pointer is equivalent to incrementing the index in brackets by the same number.

因此,如果您有一个指向堆上一系列连续内存位置的指针,或堆栈上的一组内存位置(其变量名衰减为指针),这些指针(真正的指针和衰减为指针的指针) ) 将指向第一个内存位置问题(即 element [0])。将整数值添加到指针相当于将括号中的索引增加相同的数字。

#include <stdio.h>
#include <stdlib.h>

int main()
{
    // This first declaration does several things (this is conceptual and not an exact list of steps the computer takes):
    //      1) allots space on the stack for a variable of type pointer
    //      2) allocates number of bytes on the heap necessary to fit number of chars in initialisation string
    //         plus the NULL termination '
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E

Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E

Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E

Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
' (i.e. sizeof(char) * <characters in string> + 1 for '##代码##') // 3) changes the value of the variable from step 1 to the memory address of the beginning of the memory // allocated in step 2 // The variable iPointToAMemoryLocationOnTheHeap points to the first address location of the memory that was allocated. char *iPointToAMemoryLocationOnTheHeap = "ABCDE"; // This second declaration does the following: // 1) allots space on the stack for a variable that is not a pointer but is said to decay to a pointer allowing // us to so the following iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap; // 2) allots number of bytes on the stack necessary to fit number of chars in initialisation string // plus the NULL termination '##代码##' (i.e. sizeof(char) * <characters in string> + 1 for '##代码##') // The variable iPointToACharOnTheHeap just points to first address location. // It just so happens that others follow which is why null termination is important in a series of chars you treat char iAmASeriesOfConsecutiveCharsOnTheStack[] = "ABCDE"; // In both these cases it just so happens that other chars follow which is why null termination is important in a series // of chars you treat as though they are a string (which they are not). char *iJustPointToMemoryLocationsYouTellMeToPointTo = NULL; iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap; // If you increment iPointToAMemoryLocationOnTheHeap, you'll lose track of where you started for( ; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '##代码##' ; ) { printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iPointToAMemoryLocationOnTheHeap); printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo); printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo); } printf("\n"); iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap; for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '##代码##' ; i++) { printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iPointToAMemoryLocationOnTheHeap); printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i); printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i); } printf("\n"); iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack; // If you increment iAmASeriesOfConsecutiveCharsOnTheStack, you'll lose track of where you started for( ; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '##代码##' ; ) { printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iAmASeriesOfConsecutiveCharsOnTheStack); printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo); printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo); } printf("\n"); iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack; for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '##代码##' ; i++) { printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iAmASeriesOfConsecutiveCharsOnTheStack); printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i); printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i); } return 1; }

The first notable thing we do in this program is copy the value of the pointer iPointToAMemoryLocationOnTheHeapto iJustPointToMemoryLocationsYouTellMeToPointTo. So now both of these point to the same memory location on the heap. We do this so we don't lose track of the beginning of it.

我们在这个程序中做的第一件事是将指针的值复制iPointToAMemoryLocationOnTheHeapiJustPointToMemoryLocationsYouTellMeToPointTo。所以现在这两个指向堆上的相同内存位置。我们这样做是为了不会忘记它的开始。

In the first forloop we increment the value we just copied into iJustPointToMemoryLocationsYouTellMeToPointTo(increasing it by 1 means it points to one memory location further away from iPointToAMemoryLocationOnTheHeap).

在第一个for循环中,我们增加刚刚复制到的值iJustPointToMemoryLocationsYouTellMeToPointTo(将其增加 1 意味着它指向远离 的一个内存位置iPointToAMemoryLocationOnTheHeap)。

The second loop is similar but I wanted to more clearly show how incrementing the value is related to the offset, and how the arithmetic works.

第二个循环是类似的,但我想更清楚地展示增加值与偏移量的关系,以及算法的工作原理。

The third and fourth loops repeat the process but work on an array on the stack as opposed to allocated memory on the heap.

第三个和第四个循环重复该过程,但在堆栈上的数组上工作,而不是在堆上分配内存。

Note the asterisk *when printing the individual char. This tells printf to output whatever is pointed to by the variable, and not the contents of the variable itself. This is in contrast to the line above where the balance of the string is printed and there is no asterisk before the variable because printf() is looking at the series of memory locations in its entirety until NULL is reached.

*打印个人时请注意星号char。这告诉 printf 输出变量指向​​的任何内容,而不是变量本身的内容。这与上面打印字符串余额且变量前没有星号的行形成对比,因为 printf() 正在查看整个内存位置系列,直到达到 NULL。

Here is the output on ubuntu 15.10 running on an i7 (the first and third loops output starting at an offset of 1 because my loop choice of forincrements at the beginning of the loop as opposed to a do{}while(); I just wanted to keep it simple):

这是在 i7 上运行的 ubuntu 15.10 上的输出(第一个和第三个循环输出从偏移量 1 开始,因为我在循环开始时选择了for增量而不是 a do{}while();我只是想保持简单):

##代码##

回答by celavek

Because the result of that operation is undefined. Where does p1 + p2 point to? How can you make sure it points to a properly initialized memory so that it could be dereferenced later? p1 - p2 gives the offset between those 2 pointers and that result could be used further on.

因为该操作的结果是未定义的。p1 + p2 指向哪里?您如何确保它指向正确初始化的内存,以便以后可以取消引用?p1 -​​ p2 给出了这两个指针之间的偏移量,该结果可以进一步使用。

回答by Michael

subtraction of pointers is only defined if they point into the same array of objects. The resulting subtraction is scaled by the size of object they point to. ie, pointer subtraction gives the number of elements between the two pointers.

只有当它们指向相同的对象数组时才定义指针的减法。产生的减法根据它们指向的对象的大小进行缩放。即,指针减法给出两个指针之间的元素数。