zoukankan      html  css  js  c++  java
  • 二维数组指针

         最近复习C语言的时候用到了2维数组的指针做为函数形参传入,网上查了一些方法,觉得颇有深度,做了一番研究,感受颇深,写下来算是做为第一天来博客园的见证。  

      首先网上查了一下,就是传入2维数组指针来作为对参数的形参,如 int (*a)[2],int a[2][2] 等形式;

    void fun(int a[][2])
    {
    	a[1][1]=3;
    }
    void fun1(int (*a)[2])
    {
    	a[1][1]=3;
    }	
    
    
    int main()
    {
    	int a[2][2]={{1,1},{1,1}};
    	fun(a);
    	fun1(a);
    	
    	printf("a[1][1] is %d
    ",a[1][1]);
    }
    

      结果a[1][1] is 3,由此可以发现以上两种形式可以传入的。由此引发了我更深层次的思考.我开始思考这个2维数组的指针到底是以什么样的形式存在的。

      我当时第一印象就是就是指针的指针,简称2级指针,这方面我查阅了一些资料,如果说2维数组指针是一个2级指针,附代码:

      

    int a[2][2]={0,1,2,3}; int **p=(int**)a 则此时p[0]=0;p[1]=1;p[2]=2;p[3]=3; 而p[0][0]=*(*(p+0)+0)=**p; p[0][1]=*(*(p+0)+1); 对于p[0][0]:由于*p=0; ====> **p=*(0);引用地址为零的内存,必然是错误的。 对于p[0][1]=*(*p+1)====>*(4),引用了非法内存同样

      我们知道,对于任何一个指针类型的变量,他会把他内存上连续的四个字节的内容当做地址,如果数组指针是2级指针,那么它的第一次取值就是可能指向非法地址。

     另外指针是可以作为左值的,他是一个变量可以被赋值,至少就我们现在看到的2维数组指针都是固定不变的,由以上两点就可以说明其问题。

     此外数组和指针的关系我就不加以评论。我们都知道char *a="abcd"与char a[]="abcd",是不同的,一个是在编译中确定,一个是在运行时赋值,在以后的存取中,在栈上的数组指针所指向的字符串(例如堆)快。这是题外话,我就不加以赘述了,我只是想说明光是在一维数组我们就该明确的知道指针和数组的不同,由此推断出2维数组的不同也是一种佐证。

     我们已经知道了数组指针和2维指针的区别,那么数组指针到底是怎么样的呢?或者我们明确一点目的,即我们到底如何用指针处理数组。

      说道2维数组,我相信任何一本C语言的书上都说明了一个明确的事实,那就是数组在存储上地址都是连续的,2维数组在存储的时候是按照先行后列的顺序依次存储的,当把每一行看作一个整体,即视为一个大的数组元素时,这个存储的二维数组也就变成了一个一维数组了。而每个大数组元素对应二维数组的一行,我们就称之为行数组元素,显然每个行数组元素都是一个一维数组。

      搞清了这个概念,我们先用一种简单的办法来,贴代码

    int b[3][3]={1,2,3,4,5,6,7,8,9};
    int *p=b;
    int *p2=b[0];
    printf("p[8] is %d
    ",p[8]);
    printf("p2[8] is %d
    ",p2[8]);
    printf(" *(p+2*3+2) is %d
    ",*(p+2*3+2));
    printf(" *(p2+2*3+2) is %d
    ",*(p2+2*3+2));
    

     以上的代码编译的时候会报错,这是因为warning: initialization from incompatible pointer type,这个问题我等下会说,但是我们可以看到我定义了一个int *的指针指向数组的首地址,从而根据数组在内存上的连续排序取值。

     相信看过上述代码的人会纠结下b与b【0】在代码中的意义,我现在先来解释下这两者的区别,从而引发我下一种方法来用指针表示2维数组。

      对上述二维数组b,虽然b[0]、b都是数组首地址,但二者指向的对象不同,b[0]是一维数组的名字,它指向的是b[0]数组的首元素,对其进行“*”运算,得到的是一个数组元素值,即b[0]数组首元素值,因此,*b0]与b[0][0]是同一个值;而b是一个二维数组的名字,它指向的是它所属元素的首元素,它的每一个元素都是一个行数组,因此,它的指针移动单位是“行”,所以b+i指向的是第i个行数组,即指向b[i]。对b进行“*”运算,得到的是一维数组a[0]的首地址,即*a与a[0]是同一个值。当用int *p;定义指针p时,p的指向是一个int型数据,而不是一个地址,因此,用a[0]对p赋值是正确的,而用a对p赋值是错误的。这一点请读者务必注意。尽管这是错误的,但是编译器依旧会优化这个错误,报个警告出来。

      由上述说明,我们还可以得到二维数组元素的一种表示方法:
    对于二维数组b,其b[0]数组由b指向,b[1]数组则由b+1指向,b[2]数组由b+2指向,以此类推。因此,*b与b[0]等价、b(a+1)与b[1]等价、*(b+2)与b[2]等价,┅,即对于b[i]数组,由*(b+i)指向。由此,对于数组元素b[i][j],用数组名b的表示形式为:
    *(*(b+i)+j)
    指向该元素的指针为:
    *(b+i)+j

     是不是感觉有点迷糊,那么我换一种写法来表示

    b=&b[0] b[0].....b[i]   类型 int(*p)[M]
    b[0]=&b[0][0] b[0][0].......b[0][i]  类型int
    

     贴代码验证:

    printf("b  address is %p
    ",b);
    printf("&b[0]  address is %p
    ",&b[0]);
    printf("b[0]  address is %p
    ",b[0]);
    printf("&b[0][0] adddress is %p
    ",&b[0][0]);
    printf("b+1  address is %p
    ",b+1);
    printf("b+1  address is %p
    ",b[0]+1);
    

    b  address is 0xbfb71e48
    &b[0]  address is 0xbfb71e48
    b[0]  address is 0xbfb71e48
    &b[0][0] adddress is 0xbfb71e48
    b+1  address is 0xbfb71e54
    b+1  address is 0xbfb71e4c       一目了然,不用我多说了吧。

    既然我们搞定这种形式,那么我们如何用指针来实现它呢, 首先我知道了a的数组元素是int(*p)[N]的类型,那么我定义一个这种类型的指向它,这时候我们发现编译器的那个waring不报错了,说明我的猜测是对的。

    老规矩,贴代码验证:

    int (*p1)[3]=b;
    printf("p1[0][1] is %d
    ",p1[0][1]);
    printf("*(*(p1+0)+1) is %d
    ",*(*(p1+0)+1));
    printf(" (*p1)[1] is %d
    ",(*p1)[1]);
    printf(" (*p1)[8] is %d
    ",(*p1)[8]);
    

    p1[0][1] is 2
    *(*(p1+0)+1) is 2
     (*p1)[1] is 2
     (*p1)[8] is 9      结果证明我们用int (*p)[3] 类型的指针来实现b+i 与b[i]+j的指针移动并且取值是可以实现的。

    注: (*p1)[8] is 9  我想

    int *p=b;
    int *p2=b[0];
    printf("p[8] is %d ",p[8]);
    printf("p2[8] is %d ",p2[8]);  这段代码已经解释清楚了,但是我建议最好不要这样做。

    好了就说这么多,参考了网上很多大牛的解释,有错误希望指正。

     

        

  • 相关阅读:
    Click和Command事件的区别是什么
    后台取不到repeater里的checkbox选中状态 和 checkbox 值
    asp.net 控件生命周期 内的执行步骤
    采购流程
    matlab练习程序(图像放大/缩小,双立方插值)
    matlab练习程序(获取鼠标坐标)
    matlab练习程序(区域填充算法,队列版)
    matlab练习程序(二值图像连通区域标记法,两步法)
    matlab练习程序(寻找凸包,Graham扫描法)
    matlab练习程序(图像旋转,双线性插值)
  • 原文地址:https://www.cnblogs.com/chongxin/p/chongxin.html
Copyright © 2011-2022 走看看