刚开始学c语言的时候,总是会认为,一级指针可以用来访问一维数组,那么二维数组就要用二级指针来访问啦。。。。
实际上二级指针和二维数组真的没什么关系,而且,切记千万不要用二级指针访问二维数组。。。。。
下面是几个有关的小程序,加深印象。。。。。。。。
实验环境:主机CPU酷睿i5,vs2012
程序1:
int _tmain(int argc, _TCHAR* argv[]) { int **p= NULL; int a[2][3] = {1,2,3,4}; p = a; return 0; }
结果:编译错误,错误提示:无法从“int [2][3]”转换为“int **
可见,二级指针和二维数组名,根本就不是一类东西。
程序2:
int _tmain(int argc, _TCHAR* argv[]) { int **p= NULL; int a[2][3] = {1,2,3,4}; p = (int **)a; printf("指针的长度为:%d ",sizeof(p)); printf("值为:%d ",(int)(*p)); p++; printf("值为:%d ",(int)(*p)); getchar(); return 0; }
输出结果:
为啥可以输出结果呢:纯属巧合!!!!!
什么巧合呢:在我的机器上指针的长度和int类型的长度都刚好为4个字节。
把程序放在虚拟机中跑一下,虚拟机的系统是CentOS7,64位的。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][2]={1,2,3,4}; int **p =(int **)a; printf("值为:%d ",(int)(*p)); p++; printf("值为:%d ",(int)(*p)); char *pointer; printf("指针的长度为:%zd ",sizeof(pointer)); return 0; }
结果:编译错误,提示:
原因是,在我的虚拟机中指针占8个字节吗,而int占四个字节。
但是long是占8个字节,所以改成long也可以实现主机中的效果。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { long a[2][2]={1,2,3,4}; long **p =(long **)a; printf("值为:%ld ",(long)(*p)); p++; printf("值为:%ld ",(long)(*p)); char *pointer; printf("指针的长度为:%zd ",sizeof(pointer)); return 0; }
结果为:
原因和第一个程序一样,纯属巧合:在我的虚拟机中指针和long都占8个字节。
在写这个程序的时候还有个小插曲(小错误),也设计了两个知识点,顺便分享一下,写上面程序的时候。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][2]={1,2,3,4}; int **p =(int **)a; printf("值为:%d ",(long)(*p)); p++; printf("值为:%d ",(long)(*p)); char *pointer; printf("指针的长度为:%zd ",sizeof(pointer)); return 0; }
输出结果为:
在输出long型数据的时候我不小心用了%d,而不是%ld。
看一下用%ld的效果:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][2]={1,2,3,4}; int **p =(int **)a; printf("值为:%ld ",(long)(*p)); p++; printf("值为:%ld ",(long)(*p)); char *pointer; printf("指针的长度为:%zd ",sizeof(pointer)); return 0; }
输出结果为:
为什么会这样? 还是要从内存角度看:
在我的虚拟机中,指针占8个字节,而p是个二级指针,*p就是个int类型的指针,占8个字节,所以其实*p占据了a[0]和a[1]。
至于为什么结果是8589934593.这还要涉及一个字节序的问题(这里就不解释了)
我的cpu是英特尔的,一般x86系列的cpu都是采用低字节序。
所以a[0]和a[1]两个单元在内存中类似于:
高地址------------------------------------------------------------------------------------低地址
00000000 00000000 00000000 00000010 00000000 00000000 00000000 00000001
把这段内存解释成long类型的数据就是8589934593
------------------------------------------------------------------------------------------------------------------------------
还要注意的是我上面,那两个巧合之下能运行的程序,都是用*p访问的,如果用**p会报错,因为**p就是*p指向的内存空间的值,而*p指向的内存,也就是8589934593地址空间,是未知的。
总之记住,二级指针和二维数据没啥关系
问题来了,既然不能用二级指针访问二维数据,那怎样用指针访问二维数组呢?
在我之前的随笔 由typedef和函数指针引起的危机 中,提到了,要声明某种类型的指针变量,只需先声明处该类型的变量,然后加上*即可(注意优先级)
那么怎样用指针访问二维数组呢?
先来回忆一下怎样用指针访问一维数组。在访问一位数组时,我们实际上声明的是一个和数组元素类型相同的指针变量,指向了数组第一个元素的地址,然后在用这个指针访问数组。
比如访问int b[3]= {1,2,3}.
b中的元素为int类型,所以我们要声明一个int类型的指针变量 如int *p ,然后将p指向b的第一个元素的地址,即p = &b[0],数组的首地址和第一个元素的地址是一样的,所以也可以用p=b;
这里需要强调的是,指针类型和数组元素的类型一致,而不是数组类型一致,比如b的类型是有三个int类型元素的数组,而b中元素是int类型,b是数组类型,而元素是int类型,是不一样的。上面之所以能够用p=b,不是说p是数组类型的指针,只是数组的首地址,和数组中第一个元素的地址一样罢了。p是个int类型的指针而不是数组类型的指针,这一点一定要记住。。。。
回到二维数组来,拿int a[2][3]= {1,2,3,4,5,6}来说, 数组a的元素为 一个含有三个int类型数据的数据,即a的元素是一个一维数组,这个数组含3个元素。。。。
那么怎样声明指向a数组中元素的指针变量呢? 在 由typedef和函数指针引起的危机 提到,方法及时先声明一个该类型的变量,然后加上*即可,
a中元素为含有3个int数据的数组,定义这样一个普通变量为: int p[3],然后在变量名前面加上*即可,但是要注意运算符的优先级,因为*的优先级比[]低,所以要加括号,即int (*p)[3]。
这样再将p指向a的第一个元素的地址:p = &a[0],然后就可以用p来访问数组a啦!
给出程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][3] = {1,2,3,4,5,6}; int (*p)[3] = &a[0]; printf("值为:%d ",(*p)[0]); printf("值为:%d ",(*p)[1]); printf("值为:%d ",(*p)[2]); p++; printf("值为:%d ",(*p)[0]); printf("值为:%d ",(*p)[1]); printf("值为:%d ",(*p)[2]); getchar(); return 0; }
结果为:
再来一个程序,加深一下理解
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][3][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}; int (*p)[3][4] = &a[0]; int i = 0; for(;i < 3; i++) { int j = 0; for(; j < 4;j++) printf("值为:%d ",(*p)[i][j]); } p++; i = 0; for(;i < 3; i++) { int j = 0; for(; j < 4;j++) printf("值为:%d ",(*p)[i][j]); } return 0; }
运行结果:
最后,贴上一张,来自csdn的图
希望对你有帮助,有纰漏的地方还请指正,谢谢。。。。