1.如何通过普通指针间接访问二维数组的每个元素?合理吗?
1、第一步,如何用普通指针保存二维数组的首地址:
- 正确的做法:将二维数组第0行第0列的元素的地址赋值给指针。背后的逻辑:&a[0][0] 、 a[0] 、 *a 三者在逻辑上是等价的,这些“指针”的类型都是int *型的。
#include <stdio.h> int main(int argc, char const *argv[]) { int arr[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int i, j; int *parr = &arr[0][0]; //第0行第0列的元素的地址 return 0; } 或者: int *parr = a[0]; 或者 int *parr = *a
- 被警告的做法(也可以运行):将二维数组的名字arr赋值给指针。背后的逻辑:arr 这个“指针”的类型是 int (*)[4]类型的,与普通指针的int *类型不匹配,语句上虽然合法,但是我们要避免使用。
#include <stdio.h> int main(int argc, char const *argv[]) { int arr[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int i, j; int *parr = arr; return 0; }
2、一种可行的做法:回顾在一维数组中,我们可以通过指针进行间接访问数组元素,它有以下三种可行方式,但是只有一种能用在二维数组身上:
- 指针下标法:行不通,因为二维数组在内存中是连续存储的,用指针保存二维数组的第0行第0列的地址后,就相当于把二维数组看成一维数组,那么下标只能有一个,也就是parr[ 1 ] 是可行的,但是parr[ 1 ][ 0 ]是错的。
- 指针偏移后取星花:行不通,原因同上。
- 指针自加取星花:行得通,因为二维数组在内存中是连续存储的。
#include <stdio.h> int main(int argc, char const *argv[]) { int arr[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}}; int i, j; int *parr = &arr[0][0]; for(i=0; i<3; i++){ for(j=0; j<4; j++){ printf("%d ", *parr++); //通过一维指针自加后取星花能访问二维数组元素 } putchar('\n'); } return 0; }
3、用普通指针访问二维数组不合理:虽然说二维数组名arr是可以赋值给int *类型的指针parr,但是 parr++ 和 arr++ 的跨度不一样,没法用之前的结论:通过二维数组的父子数组名偏移的方式来间接访问二维数组的元素
2.数组指针的引入
1、数组指针的概念:数组指针本身是一个指针变量,它指向一个数组,它不是数组。如图:
2、数组指针的类型:指针变量有四要素,其中指针的类型十分重要。数组指针关心这个指针指向的数组中有几个元素,于是,当你定义数组指针时,中括号里面的数字不一样,代表你定义的数组指针就是不同类型的。如下:
int (*p1)[4];
int (*p2)[5];
p1 和 p2,这两个指针的类型不一样,体现在:
1.它们能访问的内存空间不同;p1一次性能够访问4*4=16字节的内存空间;p2一次性能够访问5*4=20字节的空间
2.它们自加后的跨度不同: p1++后,指针p1往后移动16字节;p2++后,指针p2往后移动20字节。
3、数组指针为啥这样写?(通用的类型判别方法):
- 数组指针首先它得是一个指针,所以用小括号把*p括起来是为了先让修饰符*和p结合,确保p是一个指针;其次,关心的是这个指针指向的数组中有几个元素,也就是中括号里面的数字;最后,关注的是数组指针这个指针指向的数组,数组里面的元素是什么数据类型的。
int (*p)[4]; //数组指针,指针指向的数组中有4个元素,每个元素是int类型的。 int *p[4]; //指针数组,数组中有4个元素,每个元素是int *类型的。
- 适用于变量、指针、函数的类型判别方法
- 找到变量名,若没有变量名,找到最里面的结构
- 向右看,读出看到的东西,不要跳过括号
- 向左看,读出看到的东西,不要跳过括号
- 若有括号,跳出第一层括号
- 重复上述过程,直到读出最终的类型