起步
在一个二维数组 a[2][3]
中,a 与 a[0] 拥有相同的地址,但它们各自存在的意义却不同。
正文
现在,我们创建一个二维数组,再打印它们的各自地址:
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
printf("a address is %p\n", a);
printf("a[0] address is %p\n", a[0]);
OutPut:
a address is 0x7ffee3da8710
a[0] address is 0x7ffee3da8710
显然,它们的地址是相同的,事实上,数组中的第一个元素 a[0][0]
也是这个地址。
尽管地址相同,但对于编译器来说,二者仍存在差别。这个差别就是它们的类型。
对于数组名 a 来说,它的类型是 int [][3],意味着 a 是一个二维数组,且该数组的每一行有 3 个元素;而 a[0] 的类型是 int [],也就是说 a[0] 是一个一维数组的首地址,在这里,被 a[0] 代表的一维数组就是:a[0][0]、a[0][1]、a[0][2]
。
当需要用指针变量指向 a 和 a[0] 时,由于类型不同,指针变量的类型也需要随之改变:
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*b)[3] = a; /* 指针变量 b 指向二维数组 a */
for (int i=0; i<2; ++i)
for (int j=0; j<3; ++j)
printf("%d%s", *(*(b+i)+j), j==3-1 ? "\n": " ");
puts("-------");
int *c = a[0]; /* 指针变量 c 指向一维数组 a[0] */
for (int j=0; j<3; ++j)
printf("%d%s", *(c+j), j==3-1 ? "\n": " ");
OutPut:
1 2 3
4 5 6
-------
1 2 3
需要注意,int (*b)[3]
中 (*b)
小括号不能漏掉,否则编译器会认为 b 是一个一维数组,数组里边的元素都是 int * 指针。
int *l, *m, *n;
int *arr_ptr[3] = {l, m, n};
在使用过程中,通过偏移量操作指针时(类似:p + i
),小括号是否存在已然不重要了,但是对指针做下标操作就不一样了。
for (int i=0; i<2*3; ++i)
printf("%d%s", (*b)[i], i==2*3-1 ? "\n": " "); /* 正确用法 */
for (int i=0; i<2*3; ++i)
printf("%d%s", *b[i], i==2*3-1 ? "\n": " "); /* 错误用法 */
OutPut:
1 2 3 4 5 6
1 4 -709492496 32766 1642521557 0
为什么 *b[i] 这样使用时会先打印 1 和 4 呢?
这是因为二维数组 a2 可以视为一个含有两个元素的一维数组,只不过这两个元素又恰好是一个包含三个元素的的一维数组罢了。
由于 *b[i] 是指针数组,*b[0] 的意思是取出数组中的第一根指针(这里对应 E1),然后取出该指针指向数组的第一个元素(e1);*b[1] 取出数组中的第二根指针(对应E2),然后取出该指针指向数组的第一个元素(e4)…… 由于不存在第三根、第四根指针,因而后续操作是指针越界,打印出内存里的随机数据。
参考
- 《C语言程序设计现代方法 2nd》
还不快抢沙发