二维数组和指针

先从存储的角度对二维数组做一个全面的了解。二维数组在内存中的存储,是按照先行后列依次存放的。从内存的角度看,能够这样说,二维数组其实就是一个一维数组,在内存中没有二维的概念。若是把二维数组的每一行当作一个总体,即当作一个数组中的一个元素,那么整个二维数组就是一个一维数组,它以每一行做为它的元素,这个应该很好理解。
第一,来详细介绍二维数组与指针的关系。-
首先定义个二维数组 array[3][4],p 为指向数组的指针。
若p=array[0],此时p指向的是二维数组第一行的首地址,则 p+i 将指向array[0]数组中的元素array[0][i]。由以上所介绍的二维数组在内存中的存储方式可知,对数组中的任一元素array[i][j] ,其指针的形式为:p+i*N+j (N为每一行的长度)。 元素相应的指针表示法为:*(p+i*N+j) ,下标表示法为:p[i*N+j] 。
For Example:
array[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int * p=array[0];
数组array有四个元素,分别为array[0],array[1],array[2],array[3],每一个元素为包含3个元素的一维数组,
如array[0]的3个元素为 array[0][0],array[0][1],array[0][2]。
元素array[2][2]对应指针为:array+2*3+2
指针表示法为:*(array+2*3+2) ,
下标表示法为:array[2*3+2] 。数组

特别注意:虽然 array[0] 与 array 都是数组首地址,但二者指向的对象不一样,这点要很是明确。
array是一个数组名,右值退化为指针,指向的是一个一维数组a[0]总体,也就是指向{1,2,3}这个数组总体的地址。
array[0]是一个数组名,右值退化为指针,指向的是{1,2,3}这个数组中第一个元素的地址,也就是arrar[0][0]=1的地址.
array做为数组名,左值表明数组,有四个元素,分别为array[0],array[1],array[2],array[3],每一个元素为包含3个元素的一维数组,
array[0]做为数组名,左值表明数组,有3个元素为 ,分别为array[0][0],array[0][1],array[0][2]。它做为右值转化为指针,指向的是一维数组array[0]的首地址。(array[0]是一维数组的名字,array[0]做为左值表明数组,做为右值表明的是指针。*array[0]这个表达式中,array[0]做为右值,数据类型由数组退化为指针(该指针指向了array[0]这个数组首元素的地址),因此*array[0]的结果就是第一行第一个元素的值,*array[0]==array[0][0]。
由上面可知:array做为数组名(左值)表明的是二维数组总体,arr[0]做为数组名(左值)表明的是行数组的总体。位运算符sizeof()中,数组名表明左值,因此:
二维数组的总字节长度为:sizeof(arr)
二维数组中每行的字节长度表示为:sizeof(arr[0])
而 array 是二维数组的名字,它指向的是所属元素的首地址,其每一个元素为一个行数组。它是以‘行’来做为指针移动单位的,如array+i 指向的是第 i 行。对 array 进行 * 运算,获得的是一维数组 array[0] 的首地址,因此 *array 与 array[0]  为同个值(做为左值都是一个行数组)。【此时array先退化为指针,该指针指向的是第一行这个一维数组(array是一个数组指针,这个指针中的值就是array中第一行这个数组的地址),*array就是对指针取值,结果就是第一行这个数组。此时若是用printf函数打印*array,*array做为第一行这个数组,会退化为指针,打印结果就是第一行首元素的地址】
若是定义 int* p,p为指int类型的指针,指向int 类型,而不是地址。故如下操做 :p=array[0] (正确) ,p=array (错误) 。这点要很是注意。(错误缘由是p=array这个表达式中,array做为指针,其类型是一个数组指针,指向了{1,2,3}这个数组,因此p和array两个指针的类型不匹配。)
C语言中不一样类型的指针之间如何自动转换的??。指针存储的是地址,地址的位宽根据不一样的系统有区别,64位是8字节。  

int (*p)[3]; 它表示,数组 *p 具备三个int类型元素,分别为 (*p)[0] , (*p)[1] , (*p)[2] ,即 p指向的是具备三个int类型的一维数组,也就是说,p为行指针。此时,如下运算 p=array 是正确的。函数

 

01.数组指针是指向数组首元素的地址的指针,其本质为指针(这个指针存放的是数组首地址的地址,至关于2级指针,这个指针不可移动)。int (*p)[10]; p即为指向数组的指针,又称数组指针。(行数组指针)指针

 

02.指针数组是数组元素为指针的数组,其本质为数组。对象

int*p[2]是指针数组,实质是一个数组,里面的两个元素都是指针, []的优先级比*的优先级高,p先与[]结合,造成数组p[2],有两个元素的数组,再与*结合,表示此数组是指针类型的,每一个数组元素至关于一个指针变量递归

int *pointer_array[3]; //指针数组,便是一个存放指针元素的数组,定义后即会有含有三个指针元素的数组,可是每一个指针元素并无初始化(相对而言,char *n[3]={"gain","much","strong"};是一个初始化了的指针数组,其数组中的元素是char型指针)有限个类型相同的变量的集合命名,那么这个名称为数组名内存

根据数组的定义因此能够知道指针数组中,全部的指针都是相同类型的指针。字符串

 

字符数组中存放的是字符串的首地址,不是字符串自己,字符串自己位于其余的内存区域,和字符数组是分开的。get

 

03.二维数组:如char string_1[10][10]只要定义了一个二维数组,不管赋不赋值,系统都会给他分配相应空间,并且该空间必定是连续的。其每一个元素表示一个字符。咱们能够经过指定下标对其元素进行修改。编译器

 

指针数组:如char *str_B[5] 系统至少会分配5个连续的空间用来存储5个元素,表示str_B是一个5个元素的数组,每一个元素是一个指向字符型数据的一个指针。string

 

若是我作这样的定义:

char a[3][8]={"gain","much","strong"};

char *n[3]={"gain","much","strong"};

他们在内存的存储方式分别如右图所示,可见,系统给数组a分配了3×8的空间,而给n分配的空间则取决于具体字符串的长度。

此外,系统分配给a的空间是连续的,而给n分配的空间则不必定连续。

因而可知,相比于比二维字符数组,指针数组有明显的优势:一是指针数组中每一个元素所指的字符串没必要限制在相同的字符长度;二是访问指针数组中的一个元素是用指针间接进行的,效率比下标方式要高。 可是二维字符数组却能够经过下标很方便的修改某一元素的值,而指针数组却没法这么作。

 

二维数组做为函数参数。

二维数组做为函数参数通常有两种方式:(1) void func(int **array){...}   (2) void func(int array[ ][N])

注意第二种方式必定要指明二维数组的列数

当二维数组名做为函数实参时,对应的形参必须是一个行指针变量。

 

“数组名被改写成一个指针参数”规则并非递归定义的。

数组的数组会被改写成“数组的指针”,而不是“指针的指针”:(二维数组传递参数后退化位,数组指针。)

指针数组,做为实参传递给函数会退化位二级指针。
  实参                                                所匹配的形参
数组的数组             char c[8][10];                char (*c)[10];        数组指针
指针数组               char *c[10];                  char **c;              指针的指针
数组指针(行指针)      char (*c)[10];                char (*c)[10];        不改变
指针的指针             char **c;                      char **c;              不改变        

 

对二维数组作以下总结:

1.二维数组和二维指针不是等价的,不能相互赋值

2.指针数组*[] 能够转换为二级指针** 他们相互等价

3.二维数组名和数组指针虽然是一个指针,但编译器并不理解,对他来讲是数组类型的指针,但能够类型强制转换

pointer = (int *)array_pointer;    array_pointer = (int(*)[3])pointer;  pointer = (int *)bi_array;

4.若是想用函数传递二维数组,通常形参用二级指针**p或指针数组*[],能够支持二级指针和指针数组的实参传递,特殊的还能够用(*p)[N]

 

 

数组名绝对不等于指针,并且不是指针

数组名仅仅是一个符号,不是变量,它没有本身的存储空间,而指针实实在在的是个变量,有本身的空间:

数组名不是指针,它就是一个符号。

这个是重点分析的地方,很对多人对于数组名就是指针持赞同观点的一个堂而皇之的证据是“数组名不能被修改,由于数组名是一个常量指针”,也就是不能执行 a = a+1;这句话对通常,错一半,对的是,数组名确实不能被修改,错的是,不能被修改的缘由不是由于数组名是常量指针,而是由于数组名只是一个符号,不是一个变量,所以不能做为一个左值,所以不能被修改,这里又涉及到左值和右值的问题,就再也不赘述,网上资料不少。

数组名和指针的本质区别:指针是一个变量,有本身对应的存储空间,而数组名仅仅是一个符号,不是变量,于是没有本身对应的存储空间,到此已经能够得出结论,“数组名永远不等于指针”。

相关文章
相关标签/搜索