博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
二维数组和指针
阅读量:4520 次
发布时间:2019-06-08

本文共 4091 字,大约阅读时间需要 13 分钟。

先从存储的角度对二维数组作一个全面的了解。二维数组在内存中的存储,是按照先行后列依次存放的。从内存的角度看,可以这样说,二维数组其实就是一个一维数组,在内存中没有二维的概念。如果把二维数组的每一行看成一个整体,即看成一个数组中的一个元素,那么整个二维数组就是一个一维数组,它以每一行作为它的元素,这个应该很好理解。

第一,来详细介绍二维数组与指针的关系。-
首先定义个二维数组 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*,
指针表示法为:*(array+2*) ,
下标表示法为:array[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型指针)有限个类型相同的变量的集合命名,那么这个名称为数组名

根据数组的定义所以可以知道指针数组中,所有的指针都是相同类型的指针。

 

字符数组中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。

 

03.二维:如char string_1[10][10]只要定义了一个二维数组,无论赋不赋值,系统都会给他分配相应空间,而且该空间一定是连续的。其每个元素表示一个字符。我们可以通过指定下标对其元素进行修改。

 

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

 

如果我做这样的定义:

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;这句话对一般,错一半,对的是,数组名确实不能被修改,错的是,不能被修改的原因不是因为数组名是常量指针,而是因为数组名只是一个符号,不是一个变量,因此不能作为一个左值,因此不能被修改,这里又涉及到左值和右值的问题,就不再赘述,网上资料很多。

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

 

转载于:https://www.cnblogs.com/DLL137578736/p/7406553.html

你可能感兴趣的文章
PHP实现四种基本排序算法
查看>>
Poj3414广搜
查看>>
云南南天电子信息产业股份有限公司某站SQL注入漏洞
查看>>
09-C#笔记-循环
查看>>
3.20下午
查看>>
AngularJS之代码风格36条建议【一】(九)
查看>>
linux指令和文件系统
查看>>
java上传xml文件
查看>>
板邓:给网站添加favicon图标
查看>>
Tomcat分析-启动过程
查看>>
IOS开发-基于WebDriverAgent代理服务,实现iOS手机app自动化测试的框架搭建
查看>>
实现JS数组的深拷贝
查看>>
xStream完美转换XML、JSON(转)
查看>>
code::Blocks 汉化经验
查看>>
(2017.10.10) 我对 JavaScript 历史的认识
查看>>
用aiohttp代替requests写异步爬虫
查看>>
ipv6下jdbc的连接数据库方式
查看>>
201521123069 《Java程序设计》第1周学习总结
查看>>
一线咨询师的絮絮叨叨
查看>>
文字分散对齐
查看>>