印度小哥讲解的指针, 真的很好, 这里附上链接:
https://www.bilibili.com/video/BV1bo4y1Z7xf/?spm_id_from=333.999.0.0
1. 使用指针操作一维数组
2. 二维数组和指针
2.1 二维数组在内存中的存储
b 是 b[0], b[1] 数组的数组名
,
b[0] 又同时是 b[0][0], b[0][1], b[0][2] 数组的数组名
b[1] 又同时是 b[1][0], b[1][1], b[1][2] 数组的数组名
都按照一维数组的理解比较好.
实际上, 多维数组的本质就是数组中的数组.
2.2 二维数组和指针
首先明确一个概念:“数组名返回数组首元素的指针”.
指针的类型是很重要的!!!
// int* p = b; // 这样做其实是不对的, 这是编译器帮你优化了, 指向的是“b[0][0]”.
b 返回的是一个指向一维数组(其中包含3个整型元素)的指针,
所以不能拿 int类型
接收.
int main() {
// 需要定义一个指向一维数组的指针(其中的一维数组包括三个整型数).
int b[2][3];
// int* p = b; // 这样做其实是不对的, 这是编译器帮你优化了, 指向的是“b[0][0]”.
int (*p)[3] = b; // 这是一个指向一维数组的指针.
printf("%p\n", b);
printf("%p\n", &b[0]);
printf("%p\n", *b);
printf("%p\n", b[0]);
printf("%p\n", &b[0][0]);
return 0;
}
从下面这张图中也能看出来, “数组名返回数组首元素的指针”这句话对应的是 b[0]
, 不是 b[0][0]
.
2.3 二维数组和指针解引用
b[i][j] == *(b[i] + j)
== *(*(b + i) + j);
2.2.1 详细解释二维数组中内存的管理形式
代码解释
printf("%p\n", b);
printf("%p\n", *b); // 返回一个“int*”类型的指针, 所以“+1, -1”都是;增加减少“一个字节”.
printf("%p\n", b[0]);
printf("%p\n", &b[0]);
printf("%p\n", &b[0][0]);
第一行代码
printf("%p\n", b);
b
是一个二维数组,b
的类型是int (*)[3]
,即指向包含 3 个整型元素的一维数组的指针。- 数组名
b
在大多数情况下会被解释为指向其第一个元素的指针。因此,b
在这里被解释为指向b[0]
的指针。 printf("%p\n", b);
将打印b[0]
的地址。
第二行代码
printf("%p\n", *b);
*b
表示解引用b
,即获取b
指向的一维数组的首元素地址。printf("%p\n", *b);
将打印b[0][0]
的地址。
第三行代码
printf("%p\n", b[0]);
b[0]
表示二维数组b
的第一个一维数组。- 在 C 语言中,数组名在大多数情况下会被解释为指向其第一个元素的指针。因此,
b[0]
在这里被解释为指向b[0][0]
的指针。 printf("%p\n", b[0]);
将打印b[0][0]
的地址。
第四行代码
printf("%p\n", &b[0]);
&b[0]
表示取b[0]
的地址。b[0]
是一个一维数组,&b[0]
将返回这个一维数组的起始地址。printf("%p\n", &b[0]);
将打印b[0]
这个一维数组的起始地址。
第五行代码
printf("%p\n", &b[0][0]);
&b[0][0]
表示取b[0][0]
的地址。b[0][0]
是b[0]
这个一维数组的第一个元素,&b[0][0]
将返回这个元素的地址。printf("%p\n", &b[0][0]);
将打印b[0][0]
的地址。
为什么输出都是一样的
b
、*b
、b[0]
和&b[0][0]
都表示b[0][0]
的地址。&b[0]
也表示b[0]
这个一维数组的起始地址,而b[0]
的起始地址就是b[0][0]
的地址。
因此,这五行代码输出的结果是一样的。
总结
这五行代码输出的结果是一样的,因为它们都表示 b[0][0]
的地址。具体来说:
b
、*b
、b[0]
和&b[0][0]
都表示b[0][0]
的地址。&b[0]
也表示b[0]
这个一维数组的起始地址,而b[0]
的起始地址就是b[0][0]
的地址。
代码解释
printf("%p\n", (b + 1));
printf("%p\n", *(b + 1));
printf("%p\n", b[1]);
printf("%p\n", &b[1]);
printf("%p\n", &b[1][0]);
第一行代码
printf("%p\n", (b + 1));
b
是一个二维数组,b
的类型是int (*)[3]
,即指向包含3个整型元素的一维数组的指针。b + 1
表示将指针b
移动到下一个一维数组的起始地址。printf("%p\n", (b + 1));
将打印b
的下一个一维数组的起始地址。
第二行代码
printf("%p\n", *(b + 1));
*(b + 1)
表示解引用b + 1
,即获取b + 1
指向的一维数组的首元素地址。printf("%p\n", *(b + 1));
将打印b + 1
指向的一维数组的首元素地址。
第三行代码
printf("%p\n", b[1]);
b[1]
表示二维数组b
的第二个一维数组。- 在C语言中,数组名在大多数情况下会被解释为指向其第一个元素的指针。因此,
b[1]
在这里被解释为指向b[1][0]
的指针。 printf("%p\n", b[1]);
将打印b[1][0]
的地址。
第四行代码
printf("%p\n", &b[1]);
&b[1]
表示取b[1]
的地址。b[1]
是一个一维数组,&b[1]
将返回这个一维数组的起始地址。printf("%p\n", &b[1]);
将打印b[1]
这个一维数组的起始地址。
第五行代码
printf("%p\n", &b[1][0]);
&b[1][0]
表示取b[1][0]
的地址。b[1][0]
是b[1]
这个一维数组的第一个元素,&b[1][0]
将返回这个元素的地址。printf("%p\n", &b[1][0]);
将打印b[1][0]
的地址。
为什么输出都是一样的
(b + 1)
和&b[1]
都表示b
的下一个一维数组的起始地址。*(b + 1)
、b[1]
和&b[1][0]
都表示b
的下一个一维数组的首元素地址。
因此,这五行代码输出的结果是一样的。
总结
这五行代码输出的结果是一样的,因为它们都表示 b
的下一个一维数组的起始地址或首元素地址。具体来说:
(b + 1)
和&b[1]
表示b
的下一个一维数组的起始地址。*(b + 1)
、b[1]
和&b[1][0]
表示b
的下一个一维数组的首元素地址。