数组名和指针
数组名和指针都可以通过增减偏移量访问元素。但数组名可以理解为常指针,不能自增自减。数组名赋值给其他指针后,可以通过其他指针操作。
分析如下代码:
#include <iostream>
const char* c[] = { "Welcome", "to", "Beautiful", "Beijing" };
const char** cp[] = { c + 3, c + 2, c + 1, c };
const char*** cpp = cp;
int main()
{
printf("%d:%s %d:%s %d:%s %d:%s\n", c[0], c[0], c[1], c[1], c[2], c[2], c[3],c[3]);
printf("%d %d %d %d\n", &c[0], &c[1], &c[2], &c[3]);
printf("%d %d %d %d\n", cp[0], cp[1], cp[2], cp[3]);
printf("%d %d %d %d\n", &cp[0], &cp[1], &cp[2], &cp[3]);
printf("%d %d %d %d\n", cpp, cpp + 1, cpp+2, cpp+3);
printf("%s\n", **++cpp); // Beautiful
printf("%s\n", *-- * ++cpp + 3); // come
printf("%s\n", *cpp[-2] + 3); // jing
printf("%s\n", cpp[-1][-1] + 1); // o
return 0;
}
程序结果如下图:
数组的[]运算就相当于一次解引用
cpp可以理解为一个遍历cp数组的指针,初始为cp数组第一个元素的首地址
对应的地址结构如下:
- 对于**++cpp,相当于cpp=cpp+1,再**cpp。cpp+1后就指向cp数组第二个元素,即此时cpp=&cp[1];*cpp解一重引用后,也是一个指针,即 *cpp=cp[1],而cp[1]存的是c[2]的地址,即cp[1]=&c[2],再解一重引用后,获得c[2]的值,即 *cp[1]=c[2],也就是 **cpp=c[2],而c2就是Beautiful的地址,因此输出Beautiful
- 此时cpp已经指向了cp数组的第二个元素,即cp[1]。再++cpp后指向cp[2],因此*++cpp就是 cp[2],cp[2]存的是c[1]的地址,也是一个指针;再减一后,指向的就是c数组的第一个元素,,即c[0],解引用后就是c[0]。所以 *-- *++cpp就是c[0],c[0]+3输出就是come
- cpp此时指向cp数组的第三个元素,cpp[-2]就是cpp指针减2处的元素(cpp不会变),因此cpp[-2]等价于cpp[0](相当于cpp[2-2])。*cpp[-2]=*cp[0]=c[3],也就是Beijing,c[3]+3就输出jing
- 此时cpp仍指向cp数组的第三个元素,cpp[-1]相当于cp[1],cp[1][-1]相当于c[2]处的指针减一,也就是c[1],即cpp[-1][-1]=cp[1][-1]=c[1],也就是to,c[1]+1就输出o
指针的变化:
数组名代表首元素的地址,+1后访问数组第二个元素。例如a[10]的第二个元素是a[1],a[2][3]的第二个元素是第二行,a[2][3][3]的第二个元素的第二个二维数组。
&数组名代表一个指向整个数组类型的指针,+1会跳过整个数组范围。例如a[2][3],a+1跳过一个2 * 3的数组,a[2][2][3],a+1跳过一个2 * 2 * 3的数组
假设数组
int a[10]; int (*p)[10] = &a;
- a是数组名,是数组首元素地址,+1表示地址值加上一个int类型的大小,如果a的值是0x00000001,加1操作后变为0x00000005。
*(a + 1) = a[1]
。 - &a是数组的指针,其类型为int (*)[10](就是前面提到的数组指针),其加1时,系统会认为是数组首地址加上整个数组的偏移(10个int型变量)l,值为数组a尾元素后一个元素的地址。
- 若(int *)p ,此时输出 *p时,其值为a[0]的值,因为被转为int *类型,解引用时按照int类型大小来读取。
分析如下代码:
int main() {
int a[2][2][3] = { {{1,2,3},{4,5,6}},{{7,8,9},{10,11,12}} };
int* ptr = (int*)(&a + 1);
printf(" %d %d", *(int*)(a + 1), *(ptr - 1)); // 7 12
printf(" %d %d", *(int*)(*a + 1), *(int*)(*a + 2)); // 4 7
// 因为a是三维数组,a是地址,+1跳过一个二维数组;*a仍然是指针,+1跳过一个一维数组
return 0;
}