我们在之前讲到过指针的本质是变量,当然指针也就会占用一定的内存空间。我们便可以定义指针的指针来保存指针变量的地址值啦,这便是我们所说的二维指针啦。

那么为什么需要指向指针的指针呢?指针的本质便是变量,对于指针也同样存在传值调用和传址调用。我们来看看一个示例代码,代码如下

#include<stdio.h>#include<malloc.h>intreset(char**p,intsize,intnew_size){intret=1;inti=0;intlen=0;char*pt=NULL;char*tmp=NULL;char*pp=*p;if((p!=NULL)&&(new_size>0)){pt=malloc(sizeof(int)*new_size);tmp=pt;len=(size<new_size?size:new_size);for(i=0;i<len;i++){*tmp++=*pp++;}free(*p);*p=pt;}else{ret=0;}returnret;}intmain(){char*p=(char*)malloc(5);printf("%p\n",p);if(reset(&p,5,3)){printf("%p\n",p);}free(p);return0;}

我们看到这个函数的功能是重置申请内存空间的大小,在最后打印的是重置前后的指针的地址。如果重置成功,地址便会改变,结果如下

那么二维数组究竟是怎样的呢?二维数组在内存中是以一维数组的方式排布的,它的第一维是一维数组。第二维才是具体的值,二维数组的数组名也可看做常量指针。下图更形象的说明了

那么我们接下来以代码为例进行分析

#include<stdio.h>voidPrintArray(inta[],intsize){inti=0;printf("PrintArray:%d\n",sizeof(a));for(i=0;i<size;i++){printf("%d\n",a[i]);}}intmain(){inta[3][3]={{0,1,2},{3,4,5},{6,7,8}};int*p=&a[0][0];inti=0;intj=0;for(i=0;i<3;i++){for(j=0;j<3;j++){printf("%d",*(*(a+i)+j));//*(a+i)==>a[i];*(a[i]+j)==>a[i][j]}printf("\n");}printf("\n");PrintArray(p,9);return0;}

我们看到在程序的第17行定义了二维数组 a,在第18行则定义了指针 p ,指向二维数组的首元素的地址。接下来我们便打印了这个数组,在第35行我们调用 PrintArray 函数打印数组。结果如下

我们看到二维数组在内存中也是呈一维分布的。那么一维数组名代表数组首元素的地址,二维数组名同样也代表数组首元素的地址。如:int a[5] a的类型为 int*; int m[2][5] m的类型为 int(*)[5];

二维数组名可看做是指向数组的常量指针,它也可以看做是一维数组,只不过数组中的每个元素同样也是同类型的一个数组。我们下来看看如何申请二维数组,代码如下

#include<stdio.h>#include<malloc.h>int**malloc2d(introw,intcol){int**ret=NULL;if((row>0)&&(col>0)){int*p=NULL;ret=(int**)malloc(row*sizeof(int*));p=(int*)malloc(row*col*sizeof(int));if((ret!=NULL)&&(p!=NULL)){inti=0;for(i=0;i<row;i++){ret[i]=p+i*col;}}else{free(ret);free(p);ret=NULL;}}returnret;}voidfree2d(int**p){if(*p!=NULL){free(*p);}free(p);}intmain(){int**a=malloc2d(3,3);inti=0;intj=0;for(i=0;i<3;i++){for(j=0;j<3;j++){printf("%d,",a[i][j]);}printf("\n");}free2d(a);return0;}

我们在主函数里申请了二维数组 a,打印并释放它。我们来看看结果

如我们所愿,二维数组已经申请好了。通过对多维数组和多维指针的学习,总结如下:1、C 与应用中只支持一维数组,并且它的大小必须在编译期就作为常数确定;2、数组里的元素可以是任何类型的数据,甚至可以是另一个数组,这也就是多维数组的本质了。


欢迎大家一起来学习 C 语言,可以加我QQ:243343083。