注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

饥民2011

一直在搬砖

 
 
 

日志

 
 
 
 

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

2013-02-14 14:50:30|  分类: c/c++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
        
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个小测试函数:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 


执行结果如下:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 
 
 
 其实, 好明显可以看出只要给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]的地址, 不过在编译时会有如下的警告信息.
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011

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


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

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个函数:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 
执行结果如下图:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011


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个函数:
          

关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 
执行结果:

关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 


  


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个函数如下:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011

执行结果:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011




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];哦

关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011

关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 
 
   
=======================================================================
    
最后也写了1个函数
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011

执行结果:
关于一维数组和二位数组的数组指针 - 饥民 - 饥民2011
 
 

   

  评论这张
 
阅读(338)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017