1. 一维数组
           c语言中, 数组占用的空间是一组连续的物理内存空间.

所以对于1维数组来讲,只需要知道数组头1个元素的地址, 就可以逐个地得出各个元素的值了.

例子:
          首先定义1个1维数字型数组, 并且赋值.
          int A[10]={10,1,2,3,4,5,6,7,8,9};

这里说明一下, c语言中大部分情况下如上面这个语句这样, 定义1个数组的同时就对它赋值了, 这是最方便的.
          也可以先定义再赋值.
          如
          int A[10];         A[0]=10; A[1]=2;........
          但是不能用A={10,1,2,3,4,5,6,7,8,9}; 会编译失败~

跟住定义1个指针.
          int *p;

这时这个指针p只是1个空指针, 所以下一步就是给这个指针赋值.
        p=A;            //意思就是将数组A的头部地址赋值给指针p

          注意 : 对于一般的变量, 要把它的地址赋值给指针,必须使用寻址符"&", 例如p=&a, 但是对于数组变量来讲, 是不需要寻址符的, 直接p=A就ok了.

原因是A这个数组名字本身是1个指针. 它指向数组A第1个元素A[0]的地址, 下面会详细讲解的啦~

这时指针p的值是A的头1个元素的地址.
         printf("A[0] is %d\n", *p);   //这时去访问p指针指向的值, 就是指向数组A的第1个元素啊, 所以就是A[0], *p==10了

如果想访问下1个元素的值A[1],  则指针必须指向一下元素的地址:
         p+=1;      //意思并不是指针的值加1, 而是指指针所指向的地址指向下数组1个元素的地址.同理p+i, 就是指向下i个元素的地址了.
                        //实际上, 假如p是1个指针, 那么p+i 等价与p+ (i 乘以 p所指向元素的字节长度)
      
这时再对指针p取值, 就得到A[1]的地值了.
         printf("A[1] is %d\n", *p); //同理再执行p+1;, 就得到A[2]的地址...... 一直类推

这时要返回具体某个元素的地址, 则先执行
         p=A;  //重置p的地址指向A第1个元素.
       p=p+i-1;    //指向数组A第i个元素A[i]

       或者执行p=&A[i] ,因为用取址符号能将数字型变量的地址取出来, 这时p存放的同样是A[i]的地址.      
         而不能直接p=A[i] 因为A[i]已经是1个数字型变量了.

如果当p指向数组A最后1个地址时, 例如p当前的值已经是&A[9], 如果再执行p+1, 那么p继续会已相同幅度指向下1个内存地址, 但是那个地址是不属于数组A的, 要注意啊.

写了1个小测试函数:

执行结果如下:

 其实, 好明显可以看出只要给p赋值,如上图红色框框
 p=A;    
要获取任何1个元素A[i]的值,
只要取值*(p+i)就ok
也就是
*(p+i) = A[i]

这个特性会在下面二维数组上讲解的更加深刻

2. 二维数组
       二维数组跟1维数组小许区别, 指针的用法也是不同的.

其实二维数组也可以看成1个特殊的一维数组. 只不过1一般的一位数组里面的元素是数字,或者字符串...等一般的类型值. 而这个特殊的一维数组里面的元素是若干个长度相当的一维数组.
 
       也就是说二维数组是1个由相同长度的若干个一维数组组成的数组.

例如1个2维数组B[3][4] 可以看成是由如下3个长度为4的一维数组组成.

B0[0]    B0[1]   B0[2]   B0[3]                                       //1维数组B0
       B1[0]    B1[1]   B1[2]   B1[3]                                       //1维数组B1
       B2[0]    B2[1]   B2[2]   B2[3]                                      //1为数组B2

2.1 一般的指针
也做个例子:
      首先定义1个二位数组并赋值:
      int B[3][4] = {{ 0, 1, 2, 3},
                          {10,11,12,13},
                          {20,21,22,23}};

跟住定义1个指针:
     int *p2;

同样,将指针指向整个数组的第1个元素的地址.
      p2=B[0];  //或者p2=&B[0][0]

===================================================================================================
    这里有点奇怪, 为何不像之前一位数组那样直接用B来赋值给指针(p2=B;), 而用B[0]呢?

下面解析一下:
   a1)     对于一维数组A[4]来讲, 有4个元素, 他们分别是A[0],A[1],A[2],A[3], 数组每1个元素都存放着对应的值.
   a1-1)  对于二位数组B[3][4]来讲, 我们可以将它看成1个特殊的1维数组, 这个数组有3个元素, 他们分别是 B[0],B[1],B[2].  这3个元素分别是3个长度为4的子1维数组 .
    a2-2) 对于每1个子数组B[x]来讲, 它包含4个元素, 分别是B[x][0],B[x][1],B[x][2],B[x][3], 同样, 这个4个孙元素都存放这对应的值.
    a2-3) 但是! 对于父数组B来讲, 他说包含的3个数组,只是存放了3个子数组的头部地址. 并不是这3个子数组的值啊.

b1)  所以对于一维数组A来讲, p=A; 指针p就指向A的首个元素的地址, 也就是A[0]地址啊. 对p进行取值*p 就是后A[0]的值了.
           A的头部地址p 取值后*p与A[0]是等价的
    b2)  对于二维数组B来讲, p=B;指针p同样就是指向B的首个元素的地址, 也就是B[0]的地址. 而B[0]的值并不是第1个元素B[0][0]的值, 而是B[0]这个子1维数组的首个元素的地址, 也就是B[0][0]的地址.
          
   也就是说p=B; 的话 ===> *p=B[0]   而*B[0]才是和B[0][0]等价的
   所以二位数组, 指针p想获得第1个元素的地址的话, 要执行
   P=B[0],  这样*p==*B[0]==B[0][0]

但是个人亲测, 在64位linux系统上, 用P=B; 语句(B是二维数组), 一样可以获得B[0][0]的地址, 不过在编译时会有如下的警告信息.


===================================================================================================


获得二维数组首个元素地址后,就可以取得对应元素的值了.

printf("B[0][0] is %d\n", *p2);


获得下1个元素的地址, 就是B[0][1]的地址

p++;

获得下B[1][2]的地址,当前就地址是B[0][1], 对于B[i][j]来讲, 则i+1, j+1,而这个二维数组有4个列, 则j+4相当于i+1,所以:
p+=1*4+1;  //p+=5;

下面以这个例子编写了1个函数:

执行结果如下图:

2.2 二维数组名作地址表示数组元素
        对于2维数组B[3][4]来讲, 数组名字B其实算是1个指针,  它指向了子一维数组B[0]的地址, 也就是说*B 与B[0]是等价的, 都是1个地址,指向元素B[0][0]的地址.
        B---->  B[0] ----->B[0][0]

既然B是1个指针, 指向B[0]的地址,  那么指针(B+1) 就指向B[1]的地址了, *(B+1) !!注意不是*B+1哦, 与B[1]是等价的 ,都是指向元素B[0][1] 的地址.

也就是说,  (B+i )是1个行数组指针, 指向 子一维数组B[i]的地址,  每当这个指针+1, 那么它就指向下1个子一维数组. 
       而*(B+i) 就是B[i]的值的了. 而B[i]的值仍然是1个地址, 指向B[i][0]的地址, 所以*(B+i) 与 B[i]一样 仍然是1个指针啊. 它指向B[i][0].
        (B+i) --> B[i] -->B[i][0];   
         
          *(B+i)
       也可以容易看出, 既然B[i] 是1个指针, 那么B[i]也可与进行指针移动运算, 即B[i]+j 也是1个指针, 它指向B[i][j]的地址.
       即:
       B[i]+j --> B[i][j]

而上面提过 B[i] 就是 *(B+i)啊
       so:
       *(B+i)+j --> B[i][j] 

也就是说  *(B+1)+j 是1个指针, 它指向二位数组B的1个具体元素B[i][j]的地址
         所以对这个指针取值*(*(B+1)+j) (也就是 *(B[i]+j)  因为*(B+i) == B[i]嘛),     就是元素B[i][j]

亦即系讲加如我们想获取二位数组B里面的1个具体的元素B[i][j]的值, 只需要利用指针*(B+i)+j 就ok啦.

下面就是例子:
        首先定义1个二位数组并赋值:
          int B[3][4] = {{ 0, 1, 2, 3},
                          {10,11,12,13},
                          {20,21,22,23}};

跟住定义1个指针:
         int *p3;

这时我们想获取B[1][2]的值, 只需要给指针p3 赋值成 指针*(B+1)+2
           p3 = *(B+1)+2
         
           接下来就很方便地获取B[1][2]的值啦~
           printf("B[1][2] is %d\n", *p3);     //B[1][2] = 12

这时我们又想获取B[2][1]的值, 只需要给指针p3 赋值成 指针*(B+2)+1
            p3 = *(B+2)+1
           printf("B[2][1] is %d\n", *p3);

感觉很方便啦, 避免了无谓的行列换算啦.

也写了1个函数:

执行结果:

2.3  行数组指针.
       上面已经提过,对于二维数组B来讲,  数组名(B+i)是1个指针, 但是(B+i)指向的B[i](也就是*(B+i))  也是1个指针.   
         而当你定义1个普通指针p时, p指向的*p 一般是一个值

也就是说指针(B+i) 与 普通指针p 所指向的对象性质不同, 1个是指针, 而另1个是值.
          也可以说它们是两个级别不同的指针.

所以说一般我们定义1个指针p 后.
         不能直接用(B+i) 给p赋值
         例如:
         p=B;    //B是个二位数组,    如果B是1维数组的话 p=B;似乎正确的, 因为他们是同级别的指针.
         p=(B+1);
        都是错误的.

      ======================================================================================
     
而有一种指针, 它的的级别比一般的指针高一级.  这就是行数组指针.
     
       定义1个行数组指针:
       int (*p)[4];   // []里面是4啊

********************************************************************************************
       特别说明:
          这里的int (*p)[4],  并不是这个行数组指针有4个元素, 这个4代表的是行数组指针p指向的行数组(也就是B的子一维数组里有4个元素)

也就是说这个行指针数组p 是专门for有4个列的二位数组的指针

如果有个二维数组B[3][5],   则对应定义的行指针数组为  int (*p)[5]

就如一维数组的指针*p 一样,  这个(*p)[4] 不是1个数组, 而是1个指针 而这个指针是指向二位数组B的1个子一维数组的.
 

 
   ********************************************************************************************
   
       所以这个高一级别的指针p 跟二位数组B 名字指针是同级别的/
       也就是说可以直接用B对其赋值.
        p=B;    是正确的.
 
        那么:
        p==B                 -->   *B         == B[0]
      *p                -->   *B[0] == B[0][0]
      *(*p)== *(*(p+0))       == *p[0]       ==
B[0][0]     // *(p+i) = p[i] 见文2.2节
                    *(*(p+0) +1)  == *(p[0]+1) == B[0][1]
     
      p+1 = B+1   -->     *(B+1) == B[1]                 //同上 见此文2.2 节
      *(p+1)      -->     *B[1]         == B[1][0] 

      *(*(p+1))     ==    *p[1]        == B[1][0]
      *(*(p+1)+1)  ==    *(p[1]+1)  ==  
B[1][1]

通用化:
      p+i             == B+i              -->   *(B+i)                        ==  B[i]
      *(p+i)        == *(B+i)         -->  *B[i]                           ==  B[i][0] 
      *(*(p+i))   == *(*(B+i))     ==  *p[i]        ==    *B[i]      ==  B[i][0]
      *(*(p+i)+j) == *(*(B+i)+j)  ==    *(p[i]+j) ==   *(B[i]+j) == B[i][j]

由上面红色语句得出
     
     *(p+i)+j  == *(B+i)+j --> B[i][j]
     就是1个指向 1个具体元素B[i][j] 的指针

     而 *(B+i)+j 就是2.2 节那个以行数组名作的指针吗?
     而且可以看出p 与 B是等价的   (p=B;)嘛

没错,  二位数组B的名字B 本身就是i个行数组指针...
    
    
艹.  我承认自己也很难理解,  我快吐血了!

   总之就是:
      p[i]--> B[i][0]
      p[i]+j --> B[i][j]

所以:  *(p[i]+j) = B[i][j]                  //注意这里的P是1个行数组指针啊

下面又是一个例子:
        首先定义1个二位数组并赋值:
          int B[3][4] = {{ 0, 1, 2, 3},
                          {10,11,12,13},
                          {20,21,22,23}};

跟住定义1个行数组指针:
         int (*p4)[3];   //p4是1个指针名字啦,数字4毫无其他意义 注意不要写成 int *p4[3]啊

直接用数组名字给指针赋值, 就如一维数组一样:
         p4=B;

接下来就很方便地获取B[1][2]的值啦~
           printf("B[1][2] is %d\n", *(p4[1]+2);     //B[1][2] = 12

这时我们又想获取B[2][1]的值,
            printf("B[2][1] is %d\n", *(p4[2]+1);   //B[2][1] = 21

国际惯例又写了1个函数如下:

执行结果:

2.4 指针数组
     上面2.2 2.3 节讲的实质上都是行数组指针,   但是c 还有一种叫指针数组的数组, 可以用于二维数组,  真的要吐了!

假如有个二维数组B[3][4]
     定义1个指针数组:
     int *p[3]   //注意跟 行数组指针 int(*p)[3]的区别  定义的语句只差1个括号, 意义就大大不同了.

这就定义了1个指针数组了,   它是1个数组, 成员是3个指针(p[0],p[1],p[2]), 他们可以分别用于指向二维数组B的3个子一维数组.
 
     p=B;   这个赋值是错误的, 因为p是1个指针数组, 而数组名B是1个行数组指针啊, 完全不同性质的对象.

p[0] = B[0];  
     那么p[0] 就指向了B[0]这个子一维数组的首元素地址(B[0][0])

所以
     *p[0] = *B[0] = B[0][0];
     *(p[0]+1) = *(B[0]+1) = B[0][1];

也就是:
      *(p[i]+j) = *(B[i]+j) = B[i][j];
          p[i]+j --> B[i][j]

又吐一口, 这不是跟行指针数组一样吗?
    
========================================================================================
    而因为我们上面刚刚对指针数组p执行了赋值 令
     p[i] = B[i];    //i=0

首先p[i] 是1个指针, 而B[0]是1个子一维数组,实际上也是1个指针, 它指向B[i][0]的地址
     也就是说对于数组指针p来讲:
     p[i]是1个实质上的对象, 他是p数组的1个成员. 他是1个指针

而对于行数组指针P来讲,
     当执行P=B;时
     得P+i == B+i  ==>   *(P+i) = *(B+i)

因为对于行指针数组(B+i)来讲
     (B+i)--> B[i](子一维数组)的地址    所以*(B+i)== B[i]的

而又因为p与B是等价的,都是行数组指针
    所以 *(P+i)== P[i]
    也就是说 P[i] 其实就是 B[i], 也是1个子一维数组,也是1个指针, 指向B[i][0]的地址,

又因为 p[i] = B[i]; //见前15行
   所以

   P[i]  = p[i]

也即是说 对于行数组指针p 和 指针数组P来讲,
   虽然它么你的定义方法不同,

   但是他们的p[i](P[i])是等价的.

对于指针数组p来讲 p[i] 数组p内的一个元素,是1个普通指针.

而对于行指针数组P来将, P[i] 是通过对行数组指针P+i 执行*(P+i) 取值得出的.也就是P+i 指向地址的内容. 也就是B[i]啦, 而B[i]是1个数组名(子一维数组B[i], 也相当与1个普通指针啦) 所以B[i]与P[i]与p[i]都是等价的. 前提是已经赋值P=B; p[i]=B[i];哦

=======================================================================
    
最后也写了1个函数

执行结果:

关于一维数组和二位数组的数组指针相关推荐

  1. Java学习 第三章 数组(二)多维数组

    多维数组的使用 由数组构成的数组 二维数组: ① 二维数组的声明和初始化 ② 如何调用数组的指定位置的元素 ③ 如何获取数组的长度 ④ 如何遍历数组 ⑤ 数组元素的默认初始化值 :见ArrayTest ...

  2. [转载] pythonjson构建二维数组_python二维键值数组生成转json的例子

    参考链接: python json 10: Datetime与json的相互转化 python二维键值数组生成转json的例子 今天出于需要,要将爬虫爬取的一些数据整理成二维数组,再编码成json字符 ...

  3. java ArrayList 套数组,二维不等长数组

    ArrayList 除了装填普通类型外,还能为数组类型.用法是: ArrayList<ArrayList<Double>> arr = new ArrayList<> ...

  4. JAVA数组首位末位互换_数组元素前移,第一个元素放置数组末位

    看起来so sasy,slice截取,concat拼接到末尾. 就像这样: var arr = [1,2,3];var newArr = arr.slice(1).concat(arr.slice(0 ...

  5. 一维数组和二位数组作为函数参数进行传递的方式

    数组作为参数形式进行传递其实是指针作为函数参数的一种具体表现形式, 才得以在函数中改变原来的数值,函数结束后,任然生效 普通变量作为函数参数实现是数值传递,而指针变量作为函数参数传递实现的是地址的传递 ...

  6. CF869 E. The Untended Antiquity 二位树状数组+hash

    题意 一个地图,然后三种操作  1.一个矩阵四周加上障碍  2.一个矩阵四周的障碍消除  3.问你两个点之间是否纯在一条路径不经过障碍 题解 我们可以给每一个矩阵一个hash值  然后将矩阵里面的点都 ...

  7. 一维码和二位码主要原理

    1.条码主要分类: Code39码(标准39码).Codabar码(库德巴码).Code25码(标准25码).ITF25码(交叉25码).Matrix25码(矩阵 25码).UPC-A码.UPC-E码 ...

  8. pythonjson构建二维数组_python二维键值数组生成转json的例子

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  9. java二位数组biaoda_1 面向对象前部分

    1.类名称 : ①最好是英文 (企业根本没有中文类名称) ② 最好是名词 3. 类名称一般首字母需要大5,并且应该使用"驼峰模式" helloword ---> HelloW ...

最新文章

  1. JAVA基础7-封装(1)
  2. Kubernetes - - k8s - v1.12.3 动态存储管理GlusterFS及使用Heketi扩容GlusterFS集群
  3. 两个Long类型真的不能直接用或比较么?其实可以
  4. C++ 泛型编程模板 之 函数模板初步01
  5. esp8266单片机透传_基于WeMos D1(ESP8266)的校园卡门禁系统
  6. 2017.4.16 麦森数 思考记录
  7. Visual C# 资源文件编程--创建资源文件
  8. 在国外当程序员是一种什么样的体验
  9. 数据库索引类型介绍及其优缺点、区别、适用场景
  10. java粒子群算法_Java多线程技术实现的粒子群优化算法
  11. Node.js 读取图片
  12. Minio客户端操作
  13. c语言计算平时成绩30%和期末成绩,C语言程序设计C
  14. Python:谁能赢得最后的糖块?
  15. USB UVC协议分析
  16. 【超详细】MMLab分类任务mmclassification:环境配置说明、训练、预测及模型结果可视化展示
  17. python百钱买百鸡及优化
  18. 软件测试周刊(第83期):当你感觉忙得没时间休息,就是你最需要找时间休息的时候。 ​​​
  19. LeetCode-5411、摘樱桃 II-困难
  20. 《梦华录》配色太可了!配色这样玩古风十足!

热门文章

  1. 算法—详细讲解单向循环链表的实现(python)
  2. 【MySQL】求每门科目成绩排名前二的学生信息
  3. 12- Library at ROM
  4. 用数组存储二进制数据
  5. c++ new[] delete[]底层分析
  6. 5、(字符串类型)CHAR、VARCHAR、TEXT、ENUM、SET
  7. 1063 Set Similarity (25 分)【难度: 一般 / 知识点: STL中set的应用】
  8. 使用next_permutation()的坑,你中招了么?
  9. docker之centos7安装docker
  10. JVM在JDK1.8的变化