区别二维数组中的 a 与 a[0]

C·语法 2019-10-19 1841 字 516 浏览 点赞

起步

在一个二维数组 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 可以视为一个含有两个元素的一维数组,只不过这两个元素又恰好是一个包含三个元素的的一维数组罢了。
Alt text

由于 *b[i] 是指针数组,*b[0] 的意思是取出数组中的第一根指针(这里对应 E1),然后取出该指针指向数组的第一个元素(e1);*b[1] 取出数组中的第二根指针(对应E2),然后取出该指针指向数组的第一个元素(e4)…… 由于不存在第三根、第四根指针,因而后续操作是指针越界,打印出内存里的随机数据。

参考

  • 《C语言程序设计现代方法 2nd》


本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论