$ 5.7 -
“[..]另外,两个操作数都应具有算术或枚举类型,或者一个操作数应是指向完全定义的对象类型的指针,另一个操作数应具有整数或枚举类型。
2对于减法,以下之一应包含: - 两个操作数都有算术或枚举类型;要么 - 两个操作数都指向同一个完全定义的对象类型的cv-qualified或cv-nonqualified版本;要么 - 左操作数是指向完全定义的对象类型的指针,右操作数具有整数或枚举类型。
int main(){
int buf[10];
int *p1 = &buf[0];
int *p2 = 0;
p1 + p2; // Error
p1 - p2; // OK
}
所以,我的问题是为什么'指针添加'在C ++中不受支持,但'指针减法'是?
答案 0 :(得分:29)
两个指针之间的差异意味着适合两个指针的目标之间的类型元素的数量。两个指针的总和意味着......呃...没有,这就是为什么不支持它。
答案 1 :(得分:18)
减法的结果是距离(有用)。
添加指针和距离的结果是另一个有意义的指针。
添加2个指针的结果是另一个指针,但这次没有意义。
这与大多数库中存在不同的TimeSpan和DateTime对象的原因相同。
答案 2 :(得分:4)
首先想到的是,添加指针没有意义,所以不支持它。如果你有2个指针0x45ff23dd, 0x45ff23ed
。添加它们意味着什么?一些记忆越界。标准委员会的人们没有找到足够的理由支持这样的事情,而是在编译时警告你可能存在的问题。虽然指针减法很好,因为它表示记忆距离,这通常很有用。
答案 3 :(得分:3)
指针减法的结果是两个存储器地址之间的对象数。指针添加并不意味着什么,这就是为什么不允许它。
答案 4 :(得分:2)
因为添加两个指针没有意义。
考虑我在int
和0x1234
的内存中有两个0x1240
。这些地址之间的差异为0xc
,并且在内存中是距离。总和为0x2474
,与任何有意义的东西都不对应。
你可以然而,添加指向整数的指针以获得另一个指针。这是你索引数组时所做的事情:p [4]表示*(p + 4)表示“存储在地址4个单位之后的东西。”
通常,您可以通过为每个指针赋值1和每个整数值为零来确定算术运算的“指针性”。如果结果是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 */
答案 5 :(得分:1)
N.B。这里没有关于C标准的声明。
作为@Brian Hooper答案的快速补遗,“[t]两个指针的总和意味着......呃......没什么”但是指针和整数之和允许你偏离初始指针。
从较低值指针中减去较高值的指针,可以得到两者之间的偏移量。请注意,我在这里没有考虑内存分页;我假设内存值都在进程的可访问范围内。
因此,如果您有一个指向堆上的一系列连续内存位置的指针,或堆栈上的内存位置数组(其变量名称衰减为指针),这些指针(真实指针和衰减的指针)到指针)将指向第一个内存位置问题(即元素[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 '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// 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 '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// 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) != '\0' ; ) {
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) != '\0' ; 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) != '\0' ; ) {
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) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
return 1;
}
我们在此程序中首先要注意的是将指针iPointToAMemoryLocationOnTheHeap
的值复制到iJustPointToMemoryLocationsYouTellMeToPointTo
。所以现在这两个指向堆上的相同内存位置。我们这样做,所以我们不会忘记它的开始。
在第一个for
循环中,我们将刚刚复制的值增加到iJustPointToMemoryLocationsYouTellMeToPointTo
(将其增加1表示它指向远离iPointToAMemoryLocationOnTheHeap
的一个内存位置。)
第二个循环类似,但我想更清楚地说明值的增量与偏移量的关系,以及算法的工作原理。
第三个和第四个循环重复该过程,但是在堆栈上的数组上工作,而不是在堆上分配内存。
打印个人*
时,请注意星号char
。这告诉printf输出变量指向的内容,而不是变量本身的内容。这与上面的行相反,其中打印字符串的余额并且变量之前没有星号,因为printf()完整地查看了一系列内存位置,直到达到NULL。
这是在i7上运行的ubuntu 15.10上的输出(第一个和第三个循环输出从偏移量1开始,因为我的循环选择for
在循环开始时递增而不是{{ 1}};我只是想保持简单):
do{}while()
答案 6 :(得分:0)
因为该操作的结果未定义。 p1 + p2指向哪里?如何确保它指向正确初始化的内存,以便以后可以取消引用? p1 - p2给出了这两个指针之间的偏移量,结果可以进一步使用。
答案 7 :(得分:0)
指针的减法。得到的减法由它们指向的对象的大小来缩放。即,指针减法给出两个指针之间的元素数量。