图解c/c++多级指针与多维数组

声明:本文转自 chenyang_yao ,欢迎阅读原文

指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的。其中,多级指针与“多维”数组更是让很多人云里雾里,其实,只要掌握一定的方法,理解多级指针和“多维”数组完全可以像理解一级指针和一维数组那样简单。

首先,先声明一些常识,如果你对这些常识还不理解,请先去弥补一下基础知识:

1、实际上并不存在多维数组,所谓的多维数组本质上是用一维数组模拟的。

2、数组名是一个常量(意味着不允许对其进行赋值操作),其代表数组首元素的首地址。

3、数组与指针的关系是因为数组下标操作符[],比如,int a[3][2]相当于*(*(a+3)+2) 。

4、指针是一种变量,也具有类型,其占用内存空间大小和系统有关,一般32位系统下,sizeof(指针变量)=4。

5、指针可以进行加减算术运算,加减的基本单位是sizeof(指针所指向的数据类型)。

6、对数组的数组名进行取地址(&)操作,其类型为整个数组类型。

7、对数组的数组名进行sizeof运算符操作,其值为整个数组的大小(以字节为单位)。

8、数组作为函数形参时会退化为指针。

 一、一维数组与数组指针

假如有一维数组如下:

  char a[3];

该数组一共有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)[3] = &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[3][2];

由于实际上并不存在多维数组,因此,可以将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)[2]  = a;//a为第一维数组的数组名,类型为char (*)[2]

  char * p = a[0];//a[0]维第二维数组的数组名,类型为char *

同样,对a取地址操作代表整个数组的首地址,类型为数组类型(请允许我暂且这么称呼),也就是char (*)[3][2],所以如下赋值是正确的:

  char (*p)[3][2] = &a;

三、三维数组与数组指针

假设有三维数组:

 char a[3][2][2];

同样,为了便于理解,特意画了如下的逻辑内存图。分析方法和二维数组类似,首先,从第一维角度看过去,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)[3][2][2] = &a;//对数组名取地址类型为整个数组
      char (*p)[2][2]  = a;
      char (*p) [2]  = a[0];//或者a[1]、a[2]
      char *p = a[0][0];//或者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 -1;
      else
          return 0;
    }
     int *p = NULL;
     get_memery(&p,10);

关于多级指针的用法很多,尤其以二级指针应用最为广泛,后续的有时间再进行补充。

原文地址:https://www.cnblogs.com/iwangzhengchao/p/10049733.html

时间: 2024-10-10 17:35:11

图解c/c++多级指针与多维数组的相关文章

图解多级指针与“多维”数组

指针与数组是C/C++编程中非常重要的元素,同时也是较难以理解的.其中,多级指针与“多维”数组更是让很多人云里雾里,其实,只要掌握一定的方法,理解多级指针和“多维”数组完全可以像理解一级指针和一维数组那样简单. 首先,先声明一些常识,如果你对这些常识还不理解,那么你先去弥补一下基础知识: 1.实际上并不存在多维数组,所谓的多维数组本质上是用一维数组模拟的. 2.数组名是一个常量(意味着不允许对其进行赋值操作),其代表数组首元素的首地址. 3.数组与指针的关系是因为数组下标操作符[],比如,int

C语言数组篇(五)多级指针和二维数组指针的区别

多级指针 以二级指针为例 二级指针的由来是 指针数组 的指针形式. int *p[10] 读取的顺序是 p[10] --> 10个空间的数组 * p[10] --> 这10个空间的数组里面存放的数据都是 指针型的数据 int *p[10] --> 数组里面每个指针指向的空间存放的是int型的数据 int *p[10] --> int **p; p: 指针数组的数组名,也是数组的首地址. *p 数组里面存放的指针 **p 数组里面存放的指针 指向的空间 的内容 二维数组指针: 二维数

二级指针与二维数组

最近看<Linux C程序设计大全>这本书,虽然书中有一些错误,但整体来说,书写得还算可以. 当看到网络编程[第23.2.4小节 获得主机信息]时,遇到了一段代码,原文如下: “一台主机有许多和网络相关的信息,例如,主机名称.IP地址.主机提供的服务等.这些信息一般都保存在系统中的某个文件里(例如/etc/hosts等),用户程序可以通过系统提供的函数读取这些文件上的内容.Linux环境下使用gethostent函数读取和主机有关的信息,该函数的原型如下: 1 #include <net

指针和二维数组的关系

指针引用多维数组 int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};                    二维数组 a[0]  == 1 2 5 7 a[1]  == 9 11 13 15 a[2]  == 17 19 21 23 OK,二维数组在我们眼中,相当于三个一维数组组成:a代表着 二维数组的首地址. 而在二维数组中需要我们特别注意的是 a+1 指向其实是a[1]的地址. 我们在例子中设置的是 “INT” 类型的,在32位 vs中

指针与二维数组间的关系

1.四种表示a[i][j]的形式是等价的: a[i][j]==*(a[i]+j)==*(*(a+i)+j)==(*(a+i))[j] 2.通过行指针p引用二维数组a的元素a[i][j]的方法可用以下4种等价形式: p[i][j]==*(p[i]+j)==*(*(p+i)+j)==(*(p+i))[j] 3.对指向二维数组的行指针p进行初始化的方法: p=a 或p=&a[0] 4.对指向二维数组的列指针进行初始化的方法(以下三种方法等价): p=a[0] 或 p=*a 或 p=&a[0][0

例看二维数组,指针,二维数组指针

例程: /****************************************************** * * 文件名:例程 * * 文件描述:例看二维数组,指针,二维数组指针 * * 创建人:Jesse * * 版本号: * * 修改记录: * ******************************************************/ #include <stdio.h> #define ROW 3 #define LINE 3 void main(voi

指针与多维数组深度剖析

源码 [[email protected] ch10]# cat zippo11.c /* zippo1.c --  zippo info */ #include <stdio.h> int main(void) { int b[3]={100,200,300}; int *p;p=b; printf("b=%p,*b=%d,p=%p,*p=%d,p+1=%p,*p+1=%d \n",b,*b,p,*p,p+1,*p+1); printf("-----------

指针和二维数组

指针和二维数组 首先定义一个数组: 1 int a[5][3] = { {1,6,11}, 2 {2,7,12}, 3 { 3,8,13 }, 4 { 4,9,14 }, 5 { 5,10,15 } 6 }; 随便定义的一个二维数组i = 5, j = 3 然后看下面3种方式: 1 int *b = a[0]; 2 int *c = *a; 3 int &d = **a; 其实上面的三种方式的效果是一样的,大牛一看就知道,这不是废话么.我们还是输出下 1 //value 2 std::cout

typedef 与指针、多维数组

1.在typedef中使用指针往往会带来意外的结果.如下: typedef string *pstring; const pstring cstr; 绝大数人刚开始都会认为cstr是一种指针,它指向const对象,即const pstring cstr 等价于const string *cstr,其实,这是不对的,错误的原因在于单纯地将typedef当做文本扩展了. 首先,我们要认识到pstring它是一个类型,表示的是指向string的指针.声明const pstring时,const修饰的是