zoukankan      html  css  js  c++  java
  • C/C++中二维数组和指针关系分析

     在C/c++中,数组和指针有着密切的关系,有很多地方说数组就是指针式错误的一种说法。这两者是不同的数据结构。其实,在C/c++中没有所谓的二维数组,书面表达就是数组的数组。我猜想是为了表述方便才叫它二维数组。

           在本文中,我也就叫它二维数组。在C/C++中,二维数组是数组的数组。数组的每一个元素是一个数组。说起来有点绕,大家都知道,一维数组也和指针那关比较密切,在本文中不重点阐述,下面就来阐述二维数组和指针之间到底存在着什么样的关系。

          一、二维数组一维化

               其实我这里也只是表述的方便才叫这么一个题目,我们怎么利用一个数组的访问方式来访问二维数组呢?下面来看一个具体的例子。

               首先,定义一个二维数组。

             

    1. int iArr[2][3]={0,1,2,3,4,5};  


    我们可以用一个指向int型的指针变量来访问这个数组,下面的代码是将数组一维化:

    1. int* p = iArr[0];  

    上面的iArr[0]就是代表第一个数组的首地址,由于二维数组在内存中的存储也是先行后列的方式,所以第二行也紧跟第一行之后,这样就可以用p来访问数组的元素值了,访问的方式有下标和指针方式。

    1. printf("%d,",p[3]);  
    2. printf("%d ",*(p+3));  

    最后输出的结果都是3。讲完了一维化之后,下面来继续看二维数组的函数名到底是什么意思?

            二、关于二维数组名的探索

          可能想当然的话,二维数组不就是一个二级指针吗?真是这样吗?下面用代码来验证下:

        

    1. int **pp = iArr;  

           不出意外,会出现下面的编译错误:

          error C2440: “初始化”: 无法从“int [2][3]”转换为“int **”

          其实二维数组名是一个数组指针,那什么是数组指针?数组指针是指向一个数组首地址的指针,它实际上也是一种指针类型,类似于函数指针。它声明如下: 

    1. int (*pArr)[3]  

          它说明pArr是一个数组指针,它指向的是一个数组元素为int类型并且数组元素的个数为3的一个数组指针,奇怪,中间的怎么还有一个括号是啥玩意?呵呵,这个括号还真是不可少的。少了它就变为另外一种类型了:指针数组。指针数组是数组类型,代表数组的每一个元素是指针类型,它声明如下:int  *pArr[3]。

          既然二维数组的数组名是指向第一行数组的首地址,我们也叫它行指针。那么我们可以用这种数组名或者指针来访问二维数组的元素。

        

    1. int (*pArr)[3] = iArr;  

    下面,我要访问第一行第二列的元素,我可以用下面的代码来访问

    1. *(*(pArr+1) + 2)  

    也可以用数组名来访问:

    1. *(*(iArr+1) + 2)  

    这种方式是不是一下很难看懂,为什么两个星号啊?下面就我的理解来作一下解释。仅以pArr做说明

    首先,pArr是一个指向数组的指针,在这个指针上加减一个整数都是移动整行,而不是一个元素。比如说,pArr+1代表的现在指针已经指向第一行元素了,也就是实际中的第二行,而要取得指针所指的对象,就要用到解引用运算符*,所以*(pArr+1)就代表第一行数组,是整个这一行元素就取到了,那现在要取这一行的第二个元素,只须将指针再移动两个元素,即*(iArr+1) + 2,这样就指向了这个元素的地址,再解引用取得元素的值即可。说的有点啰嗦,或许有错误,望高手别喷就是了。

            三、作为函数参数

           一维数组名作为函数参数实际上是退化为指针,二维数组作为函数参数又有什么不同呢?下面举个例子说明。

     声明了如下函数:

    1. void TestFun(int *pArr,int nlength)  

    假设,我用数组名和指向首个元素地址的指针作为传递参数,看看有什么效果?

    1. TestFun(iArr,6);//“TestFun”: 不能将参数 1 从“int [2][3]”转换为“int *”  
    2. TestFun(&iArr[0][0],6);  

    直接传递数组名是编译通不过的。因为数组名是数组指针,而函数的参数是int*,两者的类型化完全不一样,所以不能转换。

    而数组首元素的地址显然是int*类型,所以就能编译通过。

    假设,我现在把这个函数的声明换一下,看看这两种传参的方法会出现什么情况?

    现在的声明是:

    1. void TestFun(void *pArr,int nlength)  

    还是这样传参,

    1. TestFun(iArr,6);  
    2. TestFun(&iArr[0][0],6);  

    编译一下,居然都能通过了。在这里,第二种方式显然是没问题的,因为int*可以转化为void*。而第一种方式怎么就可以了呢?因为iArr是数组指针,当然也可以转换为void*啦。

  • 相关阅读:
    GSON -JSON 反序列化-多节点名称支持
    Jedis 分片原理
    闭锁-CountDownLatch
    XML序列化及反序列化
    用GIT操作SVN
    报表worker-CPU使用率过高原因排查
    二.PlantUML 之活动图
    一.PlantUML 与 IDEA 集成
    ArrayList
    VI常用命令
  • 原文地址:https://www.cnblogs.com/ywsoftware/p/4527241.html
Copyright © 2011-2022 走看看