Bootstrap

10. 指针与二维数组

印度小哥讲解的指针, 真的很好, 这里附上链接:
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*bb[0]&b[0][0] 都表示 b[0][0] 的地址。
  • &b[0] 也表示 b[0] 这个一维数组的起始地址,而 b[0] 的起始地址就是 b[0][0] 的地址。

因此,这五行代码输出的结果是一样的。

总结

这五行代码输出的结果是一样的,因为它们都表示 b[0][0] 的地址。具体来说:

  • b*bb[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 的下一个一维数组的首元素地址。
;