前言:
在上一篇文章里边,我们装模做样的总结了三条结论:
A:一维数组的元素为变量,二维数组的元素为一维数组,以此类推。
B:数组名加&取地址后,就会成为指向该数组本身的类型的数组指针。
C:数组名参与+运算后就会退化为指向该数组元素类型的指针。
文章末位我已经点明了其中C结论是有问题的,其实前边两条我也不敢很自信的说他们是完全正确的。但是我理解的就是这样,我也反复推敲过,感觉暂时已经到了我能力的极限不可能再做出更好的优化和这总结了。总之,现阶段这三条结论我觉得已经表述清楚了我的理解,虽然以后极可能会做修改。这篇文章,我们主要围绕着数组名 a 来探讨指针数组和数组指针,不要纠结为啥还是a,如果你开心,也可以是b。
( 以下代码编译环境均为win32–vs2017 )
1.数组名a到底是什么类型?
首先,数组名是指针吗?答案肯定是的,但是他是一个什么类型的指针?不知道大家有没有思考过这个问题,我们来看一下这一段代码:
int main() {
int a[2] = { 1,2 };
printf("%d\n", *a); //*a ==a[0] ==1
printf("%d\n", *(a + 1)); //*(a+1) ==a[1] == 2
printf("%d\n", sizeof(a)); //sizeof(a) ==8
printf("%d\n", sizeof(a+1)); //sizeof(a+1)==4
puts("----------华丽分割线-----------");
int b[2][2] = { 1,2,3,4 };
printf("%0Xd\n", *b); //*b ==&b[0][0]
printf("%0Xd\n", *(b + 1)); //*(b+1) == &b[1][0]
printf("%d\n", sizeof(b)); //sizeof(b) = 16
printf("%d\n", sizeof(4 + 1));//sizeof(b+1)=4
system("pause");
}
我曾经掉到过 sizeof(a) 的坑里边过:
以 int a[2]={1,2}; 这个数组为例。
sizeof(a)==8,为什么a的步长不是8字节,而是4字节?
为什么sizeof(a+1)==4?
可能有些同学一开始会感觉,这是巧合,和win32平台下指针占用空间大小有关系。但是请看二维数组:
int b[2][2]={1,2,3,4};
sizeof(b)==16,为什么b的步长,不是16字节,而是8字节?
为什么sizeof(b+1)==4?
请注意看一下:
a 的类型 ==> int[2]
a+1的类型 ==> int *
b的类型 ==> int[2][2]
b+1的类型 ==>int[2]*
上一篇文章中提到过,int[2]*是一个数字指针,但是和我们熟知的写法不太一样,int(*p)[2] 这样的写法明显看起来更顺眼一些。现在需要我们记住这种不太常用写法,也就是说数值指针p的类型,也可以用 int[2]*p表示 ,虽然这种写法编译器不允许。
那么数组名到底是什么?大家有答案了吗?
这里我们要回顾以下,sizeof运算符的作用==>计算出系统为某个类型分配的空间大小。除了sizeof运算符,其他情况下,数组名是以一个指针类型参与运算。
可能很多同学看到这里就觉得懂了!对!是数组指针!但是我要告诉你,你的想法没错,但是我们要更严谨一点,因为一维数组的int型数组的数组名是int *指针,所以我们说:数组名的类型是指向该数组元素类型的指针。
回顾以下结论C:
C:数组名参与+运算后就会退化为指向该数组元素类型的指针。
下边我们就需要优化以下结论C让它变得更加精准,我们处理指针和数组的时候,用sizeof的时机很少,一些刁钻的面试题也更喜欢各种 + - * []的综合运算。所以我们可以这样修改结论C:
结论C终极版:
C:数组名参与运算后就会退化为指向该数组元素类型的指针(sizeof运算符除外)。
我们已经对+运算符进行了研究,其实*和[]都可以算作 一个运算符,大家可以自己讨论一下,这两个运算符是否满足结论C。
2.数组指针和指针数组
滥用C指针会让人痛不欲生,如果想秀技术,指针完全可以你的操纵下劝退新手。但是你工作中让指针妙笔生花,你的老板和同事不会削你吗?比如int **(*p)[3][3],哪位大神可以和我解释以下这个数组存在的意义?
所以我们还是老老实实的回归二维数组指针和指针数组:
int(*p)[i],这是一个数组指针,注意是指针!p指向一个含有 i个int型元素的一维数组,他的步长就是i*sizeof(int)。
int *p[2],这是一个指针数组,他本质上是一个一维数组,但是每个元素都是一个
int 。所以说指针数组我更喜欢这样定义,int* p[2],有没有感觉这样看起来更容易理解?
其实指针数组大多数人理解的都没问题,但是数组指针这一块就容易犯迷糊,我想告诉大家如果不理解,那么你就记住他怎么用,总有一天你会理解的。
后边留几个小题目:
1,用一维int*指针在堆区申请一个 sizeof(int) * 16个字节的空间,然后用二维数组的方式遍历这段空间,a[4][4]。
2,定义一个二维数组int a[4][4],定义一个int **p = a,那么可以用p[4][4]的方式遍历这个数组吗?
3,定义一个二维数组int *a[4],定义一个int **p = a,那么可以用p[4][4]的方式遍历这个数组吗?