#include <stdio.h>
int main()
{
int a[5]={1,2,3,4,5};
int *p=a; printf("p = %p\n",p);
int *ptr = (int *)(&a+1);
printf("ptr=%p\n",ptr);
printf("%d %d\n",*(a+1),*(ptr-1));
printf("sizeof(ptr-1) = %d\n",sizeof(ptr-1));
printf("sizeof(*ptr-1) = %d\n",sizeof(*ptr-1));
}
结果:
分析
p = 0061FF04
:这是数组a
的首地址,即数组名即为数组首元素的地址。ptr = 0061FF18;
(&a+1)
会将&a
的地址加上整个数组a
所占的字节数(即sizeof(a)
),指向的是数组a
的末地址加上整个数组大小之后的位置,而不是直接增加整个数组a
的大小。这是因为数组a
的类型是已知的,编译器可以根据类型确定数组所占的字节数。在代码中,
(int *)(&a+1)
的作用是将数组a
的地址增加了一个数组大小的偏移量,并将其强制转换为int*
类型的指针。这样得到的指针ptr
指向了数组a
之后的内存位置。具体来说,
&a
取得数组a
的地址,它等价于&a[0]
,即数组首元素的地址。由于a
是一个具有 5 个元素的整型数组,而每个整型元素占据 4 个字节的空间(取决于系统平台),所以(&a + 1)
得到的地址值向后增加了sizeof(int) * 5
个字节的偏移量。例如,假设数组
a
的首地址是0x7ffd9aee3e40
,那么根据sizeof(int) * 5
的偏移量计算,可以得到(&a + 1)
的地址为0x7ffd9aee3e40 + sizeof(int) * 5 = 0x7ffd9aee3e40 + 20 = 0x7ffd9aee3e54
。而ptr
被强制转换为int*
类型后,就指向了这个地址。*(a+1)
:这是数组a
的第二个元素的值,即2
。*(ptr-1)
:这是ptr
的前一个位置的值,即数组a
的最后一个元素的值,即5
。sizeof(ptr-1)
:这是ptr-1
表达式的大小,由于ptr-1
是一个指针类型,所以大小为 4 字节(32位系统)。- 在32位系统中,整数类型通常占用4个字节的空间,所以
sizeof(*ptr-1)
的结果为4
。
二级指针,&arr代表整个数组,加一跳过数组。一级解引用获得指向尾后元素的指针(这里我的理解是指向整个数组首地址指针退化为指向数组首元素地址的指针),减一(也就是往前偏移一个数组元素地址的大小)再解引用得到5。