一. 回顾
4)三种二级指针内存模型图
二. 二维数组
2.1 数组类型的定义
- 数组类型:由元素个数 和 元素类型对应 int [8]
- 通过typedef定义一个数组类型
- 有typedef是类型,没有typedef是变量
2.2 指针数组
- 指针数组:它是数组,每个元素都是指针
- main函数的参数:
argc:传参数的个数(包含可执行程序)
argv:指针数组,指向输入的参数
2.3 数组指针
< 1 > 定义数组指针的第一种方式
- 数组指针:它是指针,指向一个数组的指针
- 数组指针,指向一维数组的整个数组,而不是首元素地址
- 这里需要注意的是p = a是会警告的,因为p应该是指向一整个一维数组的,而a是首元素的地址,不报错是因为首元素地址和整个数组的首地址是相同的。
- 此时打印p+1的,相比于p加的步长是一整个一维数组的长度,也就是40:
- 如果要给数组指针所指向的数组赋值,操作如下:
打印方式如下:
< 2 > 定义数组指针的第二种方式
- 先定义数组指针类型,再根据类型定义变量
- 第二种定义方式和指针数组写法很类似,多了一个( )
- ( )和[ ]的优先级是一样的,从左往右
- ( )里面有指针,它是一个指针
- 指向数组的指针,但是有了typedef,所以它是一个数组指针类型
定义了一个数组指针类型,然后根据这个类型定义了一个变量q:
现在给这个数组指针一个指向:
int a[10] = {0};
typedef int (*P)[10];
P q;
q = &a;
< 3 > 定义数组指针的第三种方式
< 4 > 总结
2.4 二维数组的使用
定义数组 int a3[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}
- 二维数组数组名代表第0行的首地址
- 区别于第0行首元素地址,它们的值是一样的,但是步长不一样
- 二维数组的数组名,可以理解是一维数组的数组名加上取地址&,这两个代表的意思是一样的
*(a3 + 0)
给二维数组的数组名加*
号,此时表示的是第0行首元素的地址,咱也不知道为啥,记着吧。所以第0行第二个元素的地址是*(a3+0)+1
。第1行首元素的地址为*(a3+1)
,相比于*(a3+0)
偏移的是16个字节- 因为
*
和[ ]
是等价的,所以*(a3+0)
和a3[0]
是等价的,a3[1]
和*(a3+1)
是等价的,a3[0]
到a3[1]
跨越16个字节。a3[0]
表示的是第0行的首元素的地址,a3[0]+1
表示的是第0行第二个元素的地址,a3[0]
到a3[0]+1
跨越4个字节
总结一下:
2.5 数组指针和二维数组相结合
注意:这里 p = &a
又会报警告,因为这里的a变成二维数组了,这里的&a表示的是整个二维数组的首地址,上面定义的指向一维数组的数组指针时这样使用就是对的,具体的为什么自己理解一下。
打印二维数组的值:
2.6 首行首元素地址和首行首地址的区别
- 测量一维数组的长度时,sizeof输入的是首行首元素的地址,如:
sizeof(t)
- 二维数组的a[0],表示的是第0行首元素的地址,所以等于sizeof测出来是40个字节。&a[0]表示的是第0行整个一维数组的地址,它是一个指针类型,所以是4个字节
2.7 二维数组做函数参数
< 1 > 5K写法
< 2 > 7K写法
< 3 > 错误写法
这里二级指针,a+1跳的是4个字节,其它三种写法步长是 a+1 一次跳一行,16个字节