两个指针之间的差为 1 意味着指针指向相邻的内存单元,其大小与所指向的对象的大小相同。
因此,给定:
int i[2];
int *ip0 = &i[0];
int *ip1 = &i[1];
double d[2];
double *dp0 = &d[0];
double *dp1 = &d[1];
我们可以安全地写:
assert((ip1 - ip0) == (dp1 - dp0));
assert(ip1 - ip0 == 1);
assert(dp1 - dp0 == 1);
但是,您也可以安全地编写:
assert((char *)ip1 - (char *)ip0 == sizeof(int));
assert((char *)dp1 - (char *)dp0 == sizeof(double));
通常你会发现这样写是安全的:
assert(sizeof(double) != sizeof(int));
尽管标准不能保证这一点。
此外,正如Filipe Gonçalves在他的评论中正确指出的那样,两个指针之间的区别仅在指针具有相同类型并指向同一数组的两个元素或指向数组末尾之外的一个元素时才正式定义. 请注意,标准 C 要求给出:
int a[100];
生成地址是安全的int *ip = &array[100];
,即使读取或写入指向的位置也不安全ip
。存储的值ip
可用于比较。
您也正式不能减去两个void *
值,因为类型没有大小void
(这就是我的示例使用强制转换为char *
,而不是的原因void *
)。void *
注意:除非您包含-pedantic
在选项中,否则GCC 不会反对减去两个值。
你知道为什么doubPtr2 - doubPtr1
(在我的第二种方法中)的值与(在我的第一种方法中)不同x = ptr - a
吗?
假设这intArray
是a
,那么这段代码:
#include <stdio.h>
static void withinArray(int *a, int *ptr)
{
int x;
printf("ptr is %p\n", (void *)ptr);
printf("a is %p\n", (void *)a);
printf("difference in pointers is: %td\n", ptr - a);
x = ptr - a;
printf("x is %d\n", x);
}
static void doubleSize(void)
{
double doubArray[10];
double *doubPtr1 = doubArray;
double *doubPtr2 = doubArray+1;
int p2 = doubPtr2;
int p1 = doubPtr1;
printf("p1 = 0x%.8X\n", p1);
printf("p2 = 0x%.8X\n", p2);
printf("p2-p1 is %d\n", p2-p1);
printf("doubPtr1 = %p\n", (void *)doubPtr1);
printf("doubPtr1 = %p\n", (void *)doubPtr2);
printf("doubPtr2-doubPtr1 is %td\n", doubPtr2-doubPtr1);
}
int main(void)
{
int a[10];
int *intarray = a;
int *p = intarray + 9;
withinArray(a, p);
doubleSize();
return 0;
}
编译时会出现我通常会修复的警告(将p1
and的类型更改p2
为uintptr_t
、 include<inttypes.h>
和 format using"p1 = 0x%.8" PRIXPTR "\n"
作为格式字符串),并生成输出:
ptr is 0x7fff5c5684a4
a is 0x7fff5c568480
difference in pointers is: 9
x is 9
p1 = 0x5C5684B0
p2 = 0x5C5684B8
p2-p1 is 8
doubPtr1 = 0x7fff5c5684b0
doubPtr1 = 0x7fff5c5684b8
doubPtr2-doubPtr1 is 1
固定代码生成:
ptr is 0x7fff5594f4a4
a is 0x7fff5594f480
difference in pointers is: 9
x is 9
p1 = 0x7FFF5594F4B0
p2 = 0x7FFF5594F4B8
p2-p1 is 8
doubPtr1 = 0x7fff5594f4b0
doubPtr1 = 0x7fff5594f4b8
doubPtr2-doubPtr1 is 1
(区别在于p1
和打印的十六进制数字的数量p2
。)
我假设您的困惑是为什么int
代码打印 9 而不是 36,而double
代码打印 8 而不是 1。
答案是当你减去两个指针时,结果以指向的对象的大小为单位给出(我似乎记得在我的开场白中说过)。
执行doubPtr2-doubPtr1
时,返回的距离double
以两个地址之间的值个数为单位。
但是,转换为整数会丢失类型信息,因此您实际上拥有整数中两个指针的char *
(或void *
)地址,并且字节地址确实相距 8。
如果我们做两个对称的套路,信息就更清楚了:
#include <stdio.h>
#include <inttypes.h>
static void intSize(void)
{
int intArray[10];
int *intPtr1 = intArray;
int *intPtr2 = intArray+1;
uintptr_t p2 = (uintptr_t)intPtr2;
uintptr_t p1 = (uintptr_t)intPtr1;
printf("p1 = 0x%.8" PRIXPTR "\n", p1);
printf("p2 = 0x%.8" PRIXPTR "\n", p2);
printf("p2-p1 is %" PRIdPTR "\n", p2-p1);
printf("intPtr1 = %p\n", (void *)intPtr1);
printf("intPtr1 = %p\n", (void *)intPtr2);
printf("intPtr2-intPtr1 is %td\n", intPtr2-intPtr1);
}
static void doubleSize(void)
{
double doubArray[10];
double *doubPtr1 = doubArray;
double *doubPtr2 = doubArray+1;
uintptr_t p2 = (uintptr_t)doubPtr2;
uintptr_t p1 = (uintptr_t)doubPtr1;
printf("p1 = 0x%.8" PRIXPTR "\n", p1);
printf("p2 = 0x%.8" PRIXPTR "\n", p2);
printf("p2-p1 is %" PRIdPTR "\n", p2-p1);
printf("doubPtr1 = %p\n", (void *)doubPtr1);
printf("doubPtr1 = %p\n", (void *)doubPtr2);
printf("doubPtr2-doubPtr1 is %td\n", doubPtr2-doubPtr1);
}
int main(void)
{
doubleSize();
intSize();
return 0;
}
输出:
p1 = 0x7FFF5C93D4B0
p2 = 0x7FFF5C93D4B8
p2-p1 is 8
doubPtr1 = 0x7fff5c93d4b0
doubPtr1 = 0x7fff5c93d4b8
doubPtr2-doubPtr1 is 1
p1 = 0x7FFF5C93D4B0
p2 = 0x7FFF5C93D4B4
p2-p1 is 4
intPtr1 = 0x7fff5c93d4b0
intPtr1 = 0x7fff5c93d4b4
intPtr2-intPtr1 is 1
记住 Polya 在How to Solve It中的建议: