图解c/c++多级指针与“多维”数组
声明:本文为原创博文,如有转载,请注明出处。若本文有编辑错误、概念错误或者逻辑错误,请予以指正,谢谢。
指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的。其中,多级指针与“多维”数组更是让很多人云里雾里,其实,只要掌握一定的方法,理解多级指针和“多维”数组完全可以像理解一级指针和一维数组那样简单。
首先,先声明一些常识,如果你对这些常识还不理解,请先去弥补一下基础知识:
、实际上并不存在多维数组,所谓的多维数组本质上是用一维数组模拟的。 、数组名是一个常量(意味着不允许对其进行赋值操作),其代表数组首元素的首地址。 、数组与指针的关系是因为数组下标操作符[],比如,int a[][]相当于*(*(a+)+) 。 、指针是一种变量,也具有类型,其占用内存空间大小和系统有关,一般32位系统下,sizeof(指针变量)=。 、指针可以进行加减算术运算,加减的基本单位是sizeof(指针所指向的数据类型)。 、对数组的数组名进行取地址(&)操作,其类型为整个数组类型。 、对数组的数组名进行sizeof运算符操作,其值为整个数组的大小(以字节为单位)。 、数组作为函数形参时会退化为指针。
一、一维数组与数组指针
假如有一维数组如下:
char a[];
该数组一共有3个元素,元素的类型为char,如果想定义一个指针指向该数组,也就是如果想把数组名a赋值给一个指针变量,那么该指针变量的类型应该是什么呢?前文说过,一个数组的数组名代表其首元素的首地址,也就是相当于&a[0],而a[0]的类型为char,因此&a[0]类型为char *,因此,可以定义如下的指针变量:
char * p = a;//相当于char * p = &a[0]
以上文字可用如下内存模型图表示。
大家都应该知道,a和&a[0]代表的都是数组首元素的首地址,而如果你将&a的值打印出来,会发现该值也等于数组首元素的首地址。请注意我这里的措辞,也就是说,&a虽然在数值上也等于数组首元素首地址的值,但是其类型并不是数组首元素首地址类型,也就是char *p = &a是错误的。
前文第6条常识已经说过,对数组名进行取地址操作,其类型为整个数组,因此,&a的类型是char (*)[3],所以正确的赋值方式如下:
char (*p)[] = &a;
注:很多人对类似于a+1,&a+1,&a[0]+1,sizeof(a),sizeof(&a)等感到迷惑,其实只要搞清楚指针的类型就可以迎刃而解。比如在面对a+1和&a+1的区别时,由于a表示数组首元素首地址,其类型为char *,因此a+1相当于数组首地址值+sizeof(char);而&a的类型为char (*)[3],代表整个数组,因此&a+1相当于数组首地址值+sizeof(a)。(sizeof(a)代表整个数组大小,前文第7条说明,但是无论数组大小如何,sizeof(&a)永远等于一个指针变量占用空间的大小,具体与系统平台有关)
二、二维数组与数组指针
假如有如下二维数组:
char a[][];
由于实际上并不存在多维数组,因此,可以将a[3][2]看成是一个具有3个元素的一维数组,只是这三个元素分别又是一个一维数组。实际上,在内存中,该数组的确是按照一维数组的形式存储的,存储顺序为(低地址在前):a[0][0]、a[0][1]、a[1][0]、a[1][1]、a[2][0]、a[2][1]。(此种方式也不是绝对,也有按列优先存储的模式)
为了方便理解,我画了一张逻辑上的内存图,之所以说是逻辑上的,是因为该图只是便于理解,并不是数组在内存中实际的存储模型(实际模型为前文所述)。
如上图所示,我们可以将数组分成两个维度来看,首先是第一维,将a[3][2]看成一个具有三个元素的一维数组,元素分别为:a[0]、a[1]、a[2],其中,a[0]、a[1]、a[2]又分别是一个具有两个元素的一维数组(元素类型为char)。从第二个维度看,此处可以将a[0]、a[1]、a[2]看成自己代表”第二维”数组的数组名,以a[0]为例,a[0](数组名)代表的一维数组是一个具有两个char类型元素的数组,而a[0]是这个数组的数组名(代表数组首元素首地址),因此a[0]类型为char *,同理a[1]和a[2]类型都是char *。而a是第一维数组的数组名,代表首元素首地址,而首元素是一个具有两个char类型元素的一维数组,因此a就是一个指向具有两个char类型元素数组的数组指针,也就是char(*)[2]。
也就是说,如下的赋值是正确的:
char (*p)[] = a;//a为第一维数组的数组名,类型为char (*)[2] char * p = a[];//a[0]维第二维数组的数组名,类型为char *
同样,对a取地址操作代表整个数组的首地址,类型为数组类型(请允许我暂且这么称呼),也就是char (*)[3][2],所以如下赋值是正确的:
char (*p)[][] = &a;
三、三维数组与数组指针
假设有三维数组:
char a[][][];
同样,为了便于理解,特意画了如下的逻辑内存图。分析方法和二维数组类似,首先,从第一维角度看过去,a[3][2][2]是一个具有三个元素a[0]、a[1]、a[2]的一维数组,只是这三个元素分别又是一个"二维"数组,a作为第一维数组的数组名,代表数组首元素的首地址,也就是一个指向一个二维数组的数组指针,其类型为char (*)[2][2]。从第二维角度看过去,a[0]、a[1]、a[2]分别是第二维数组的数组名,代表第二维数组的首元素的首地址,也就是一个指向一维数组的数组指针,类型为char(*)[2];同理,从第三维角度看过去,a[0][0]、a[0][1]、a[1][0]、a[1][1]、a[2][0]、a[2][1]又分别是第三维数组的数组名,代表第三维数组的首元素的首地址,也就是一个指向char类型的指针,类型为char *。
由上可知,以下的赋值是正确的:
char (*p)[][][] = &a;//对数组名取地址类型为整个数组
char (*p)[][] = a;
char (*p) [] = a[];//或者a[1]、a[2]
char *p = a[][];//或者a[0][1]、a[1][0]...
四:多级指针
所谓的多级指针,就是一个指向指针的指针,比如:
char *p = "my name is chenyang."; char **pp = &p;//二级指针 char ***ppp = &pp;//三级指针
假设以上语句都位于函数体内,则可以使用下面的简化图来表达多级指针之间的指向关系。
多级指针通常用来作为函数的形参,比如常见的main函数声明如下:
int main(int argc,char ** argv)
因为当数组用作函数的形参的时候,会退化为指针来处理,所以上面的形式和下面是一样的。
int mian(int argc,char* argv[])
argv用于接收用户输入的命令参数,这些参数会以字符串数组的形式传入,类似于:
char * parm[] = {"parm1","parm2","parm3","parm4"};//模拟用户传入的参数 main(sizeof(parm)/sizeof(char *),parm);//模拟调用main函数,实际中main函数是由入口函数调用的(glibc中的入口函数默认为_start)
多级指针的另一种常见用法是,假设用户想调用一个函数分配一段内存,那么分配的内存地址可以有两种方式拿到:第一种是通过函数的返回值,该种方式的函数声明如下:
void * get_memery(int size)
{
void *p = malloc(size);
return p;
}
第二种获取地址的方法是使用二级指针,代码如下:
int get_memery(int** buf,int size)
{
*buf = (int *)malloc(size);
if(*buf == NULL)
return -;
else
return ;
}
int *p = NULL;
get_memery(&p,);
关于多级指针的用法很多,尤其以二级指针应用最为广泛,后续的有时间再进行补充。
图解c/c++多级指针与“多维”数组的更多相关文章
- 图解C/C++多级指针与多维数组
声明:本文转自 chenyang_yao ,欢迎阅读原文. 指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的.其中,多级指针与“多维”数组更是让很多人云里雾里,其实,只要掌握一定的方法 ...
- C++多级指针与多维数组详细介绍
多级指针的概念 多级指针可对应于多维数组,这种指针变量中存的是另一个指针变量的地址,其说明如下: int val=10; int *ptr=&val; int **pptr= ...
- C语言数组篇(五)多级指针和二维数组指针的区别
多级指针 以二级指针为例 二级指针的由来是 指针数组 的指针形式. int *p[10] 读取的顺序是 p[] --> 10个空间的数组 * p[] --> 这10个空间的数组里面存放 ...
- C:指针遍历二维数组
C 指针遍历二维数组 http://blog.csdn.net/lcxandsfy/article/details/55000033 C++ 字符串指针与字符串数组 https://www.cnblo ...
- 论C语言中二级指针和二维数组之间的区别
刚开始学习C语言的时候,觉得一个数组可以定义一个一级指针去访问,想当然的就觉得可以定义一个二级指针去访问二维数组.很显然这是错误的. 我们来看看C语言的数组在内存中的存储方式. 实际上C语言中的数组, ...
- 20130330 printf数组改变 数组指针便利二维数组 二级指针遍历二维数组 ZigZag
1.为什么printf之后数组的值会改变? #include<stdio.h> ; int * Zigzag() { ,j=,limit=; ; ; int a[N][N]; int (* ...
- C语言中指针和多维数组
指针和多维数组 数组名是特殊的指针 数组是一个特殊的指针,多维数组也是更为复杂的数组,它们的关系是什么样的呢? 我们通过一个简单的例子来比较形象的了解指针和多维数组: int a[2][3]; 这是一 ...
- typedef 与指针、多维数组
1.在typedef中使用指针往往会带来意外的结果.如下: typedef string *pstring; const pstring cstr; 绝大数人刚开始都会认为cstr是一种指针,它指向c ...
- c语言,指针与数组--指针与二维数组2
指向一维数组的指针 char (*p)[10] ;指向一维数组的指针类型 typedef char(*TYPE_P2ARRAY)[10] ; 该指针可以指向数组 ,且使用起来效果节本相同, ...
随机推荐
- x01.os.20: compile linux-0.11 on the ubuntu
为什么学习 linux 正如不能依靠美国的 GPS 为我们的导弹指示目标一样,很难想像用运行 windows 的电脑去同美国进行信息战.而朝鲜的网络崩溃,再次警示国人,信息战.网络战离我们并不遥远.l ...
- CSS3动画事件
CSS3 的动画效果强大,在移动端使用广泛,动画执行开始和结束都可以使用JS来监听其事件. animationstart animationend 以下是一个示例 <!DOCTYPE html& ...
- 联发科发布全球首款搭载Android TV的智能电视系统芯片MT5595
联发科发布全球首款搭载Android TV的智能电视系统芯片MT5595 admin 资讯 01-07 1 1月7日消息,联发科宣布与Google共同开发出全世界第一个搭载Android TV操作系统 ...
- java HashMap
HashMap 的性能因子 1. 容量:表示桶位的数量. 2. 初始容量: 表在创建是所拥有的桶位数. 如果你知道将要在HashMap存储多少项,创建一个初始容量合适的HashMap将可以避免自动 ...
- git 中关于LF 和 CRLF 的问题
git 中关于LF 和 CRLF 的转换问题注意: Windows下编辑器设置中,建议调整设置为Unix风格.(具体设置位置各种编辑器上不同,需要找找) 使用Git Bash进行命令行操作时,运行一下 ...
- ajax请求成功后新开窗口window.open()被拦截解决方法
ajax 异步请求成功后需要新开窗口打开 url,使用的是 window.open() 方法,但是很可惜被浏览器给拦截了,怎么解决这个问题呢 问题: 前面开发项目时碰到一个问题,ajax 异步请求 ...
- FineReport中如何进行Informix数据库连接
报表开发工具Finereport中,对于Informix数据库,定义数据连接处进行如下配置: 数据库:Others 驱动器:com.informix.jdbc.IfxDriver URL: jdbc: ...
- jQuery超链接提示,提示跟随鼠标动
功能:实现鼠标移动到一个超链接时,鼠标右下角产生一个提示,并跟谁鼠标移动,知道鼠标离开超链接. 效果: 源码: <!--本案例是鼠标放在超链接上时,鼠标旁边有提示这是个超练级,以及放在图片上时图 ...
- [No00006D]下载离线版的github for windows【以Github for Windows 3.0.110.为例】
目录 先上地址后讲原理: 原理: 11个目录的文件怎么一口气下载呢? 最后,把下好的文件批量名,同时将GitHub.exe.manifest也放到软件根目录下(与GitHub.exe同级): 今后的猜 ...
- git教程链接
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000