1. 二维数组的定义
1.1 首先要知道数组是什么东西,这里不再赘述.
1.2 所谓二维数组,就是在一个数组里面嵌套另外一个数组,形式如下:
int a[2][1]={{1},{2}};
这里定义了一个含有两个子数组每个子数组内含有一个元素的整形的二维数组
怎样理这里的定义呢? 首先,我们定义了一个数组,他的第一个[]意义为想要包括的数组数量,其次参数为2,也就是包含数组数量为2。再看第二个[],意义为每个子数组中想要包括的元素数量,这里参数为1,也就是说每个子数组中包含一个元素
1.3 综上所述:二维数组[ ][ ]的定义规则为:包含数组个数+数组元素个数
1.4 下面是二维数组的一些宏观图解
2.二维数组名的指针表现
说完了二维数组的定义,我们再来看看二维数组名与指针的关系
2.1 根据一维数组的知识我们知道, 数组名代表的是数组中第一个元素的地址,假设有 int b[2]={1,2} , 那我们访问 *b 将获得b[0]的值, 同样的推论转移到二维数组上, 那么上面的二维数组名 a 代表的即是 a中的第一个元素a[0]这个数组的地址 , 所以我们如果 *a 和 a[0]得到的答案是一样的,结果都是a中的第一个数组
cout<<*a<<endl; //a中第一个数组
cout<<a[0]<<endl; //等价
2.2 然后来看*a[0], a[0][0] 代表的是a中第一个数组的第一个元素值,那么以一维数组的结论继续推 ,a[0]这个数组名也就是a[0]这个数组的第一个元素的地址,如果我们对这个元素解引用, 即*a[0] 值就是 a[0][0]的值 , 也就是a中第一个数组的第一个元素的值
cout<<*a[0]<<endl; //a中第一个数组的第一个元素的值
cout<<a[0][0]<<endl; //等价
2.3 同样的,如果我们用&来反过来取地址,那么 &a[0][0] == a[0] , &a[0] == a , 这也不难理解,只是把上面的过程倒了倒
2.4 那么有趣的来了 ,看结果:
为什么输出都为地址而且一样呢?
我们再回到一开始的定义来看,a为二维数组中第一个子数组的地址,*a为二维数组中第一个子数组
其实聪明的人已经知道答案了,因为*a是一个数组而不是一个确定的值, 所以输出的是地址 , 那又有人要问了, *a就是a[0] ,也就是a中第一个子数组的第一个元素的地址 , 为什么和包含他的数组的地址一样呢,如果真的这么定义不会混淆吗?
答案是确实第一个数组的地址和他包含的第一个元素的的地址相同, 先看一张图:
我们只看第a[0]部分 , 你会发现a[0] 和 a 的地址竟然是在同一块地方 , 可能有人到这就要犯糊涂了
你再看看 ?再看 ? a+1 等于多少? a[0]或者说 *a + 1 又是多少 ?
啊哈, 想必你已经明白了 ,原因是步长不一样, 虽然他们的首地址一样 ,但是 a+1的步长为一个数组 , 而 *a+1 的步长为一个元素 , 这一点也可以根据你取其他元素的地址得到证实
3.浅谈一下二维数组指针
3.1因为涉及到行指针和列指针,所以…
当然要讲了!
其实很简单, 一共两类 , 一类指针指向子数组 , 一类指针指向数组中的元素,就这么多
int a[2][1]={{1},{2}};
int (*p) [1] = a ; // p == a
我上面就定义了一个二维指针 , 然后我们看看他哪里难了 , p为指向数组的指针 ,不就是二维数组名吗? 是不是? a===p(三个等号表示: 完 全 一 致 ) , 不信你去试试, 和a一样的用法 , 而且不涉及动态分配内存只有这一种定义方法
下面说一下定义原则, 首先 (*p) 表示的是声明的变量为指针 , 后面的[1] 表示这是一个指向所有含一个元素的数组的指针 , 然后把a赋值给他, 因为a也是指向数组的指针 , 其中的数组包含的元素数量也为1 , 所以能成功
为了避免看完云里雾里 , 这里在强调一下数组名的双重身份:
背就完事了
那些所谓的行指针就是子数组的地址 如:a , a+1 ,列指针就是子数组中元素 *a *a +1 ,你定义一个p只是换个皮 ,好了不在赘述
4.二维数组进阶篇
看完上面的差不多考试没问题了,那些题目只是变着法子绕弯子 , 本质都一个吊样 , 这里的考试不单指wl的期末考试,一些等级和面试题也基本没问题的,涉及new当我没说,别打我我也不想讲,有文章自己可以看
“伪二维指针”:
先看代码以及运行结果
int a[2][1]={{1},{2}};
int **p = (int**)a;
cout<<p<<'\n'<<*p<< endl;
//cout<<**p; //报错
我们来看,这里我想通过**的方式定义一个指针来指向一个二维数组, 然后尝试解引用输出 , 第一个输出的值是和a的值一模一样的,参照一开始的图,也就是p的二维指针定义貌似成功了
手动空格以表示给你点时间想想…其实大不相同,这里的第二个值已经很明确的表现出来了,没有和一开始a的输出一样,这里是0x1, 0x1 是十六进制, 值为
值为0? 哪里来的0? 这里我们留意一下我的定义语句 int ##p = (int ##)a; (*被ljcsdn错误排掉了用#代替) 这里不是a而是int的强制转换(**表示二维指针的强制转换) , 啊, 其实我已经说出原因了 , 这里是二维指针的强制转换 , 也就是说原来的a不是彻底的二维指针, 再联系一下这个0 , 会不会是因为需要"强制"而被迫丢出的 0?
答案是肯定的,这里直接开解释: 这里的a是二维数组的指针而不是 二维指针 , 这两个完全不是一个东西, 你以为**就可以表示一个二维数组的地址了吗?
手 动 滑 稽
这里带回顾一下指针的内容 , *表示定义指针或者解引用, 在定义的时候表示定义指针 ,这里 ** 表示的就是定义指针的指针
所以这一切都讲的通了 , 因为类型错误导致转换时只能成功把a的地址给p , 而不能把声明也给p , 所以p只有一个有效值 ,当尝试第一次解引用的时候会返回0的二进制数字 , 当我们再次尝试**p解引用的时候(注释部分)程序直接崩溃, 因为尝试解右值0的引用
不强制转换的错误说明也证明了这一点:
“特殊的char”
老规矩先代码运行
char a[2][1]={{'a'},{'b'}};
char **p = (char**)a;
cout<<p<<'\n' ; //<<*p<< endl;
//cout<<**p;
可以看到这里我只输出了p , 和刚刚讲的一样, 这里是伪二维指针 , 只能输出二维数组的首地址, 然而我把刚刚int输出成功的*p注释掉了 , 为什么 ?
原因很简单,因为程序崩溃了
先来看:
这里我想输出二维数组中第一个子数组的第一个元素的地址,或者说第一个子数组的地址 (故意混淆,看看你还就不记得数组名的双含义)
然而输出的却是ab(( ,为什么会这样 ? 其实这里就像你猜测的那样 , 是类似乱码 … 为什么会出现这样的错误 , 因为对于char*特殊的类型c++有特殊的规定 , char * 类型如果直接被cout输出的不是地址, 而是地址所对应的字符串值 ,这里系统尝试直接解析字符串的值但是解析对象却是一个数组导致的错误 , (输出涉及C++I/O输出标准,参考C++ primer plus第14章) .
好,再回到问题
不用我说相比你已经清楚了,因为是char* 所以系统尝试解析0所对应的字符串值,所以直接崩溃 , 这样**就更不要谈了
//能看到这里的大佬们想必有了各种各样的想法 , 欢迎私聊,我会及时回复并改正
dalao点个赞再走呗 ?