zoukankan      html  css  js  c++  java
  • 深入浅出数组指针

    我在实际编码的时候,实质上很少用到数组指针。于是,对于数组指针,总给我一种模糊的感觉。

    花了一上午时间专门去研究数组指针,数组指针终于在我心中终于变的明朗起来。

    何为数组指针?

    数组指针:就是指向数组的指针。说到底还是指针,只不过数组指针指向的对象是数组而已。

    在要搞清楚数组指针之前,必须先要把指针搞清楚。

    何为指针?

    我不想按照书上的解释来解释(太过理论,很多人不是很明白)。我用我的理解给大家讲讲。

    指针就好比一只手,想指哪就指哪。这句话引申到C里,说明C的指针很灵活。

    指针常量:就是这个手一出来,一旦指向某个地方就再也不能指向其他地方了。

     1 #include <stdio.h>
     2 
     3 int main(void)
     4 {
     5     int a=100; 
     6     int *const p=&a;//定义一个指针常量
     7     printf("%d
    ",*p);
     8 
     9     return 0;
    10 }

    输出结果是100.

    接下来我将试图让p改变指向。

     1 #include <stdio.h>
     2 
     3 int main(void)
     4 {
     5     int a=100,b=200; 
     6     int *const p=&a;//定义一个常量指针
     7     printf("%d
    ",*p);
     8     //尝试让p指向b 
     9     p=&b ;
    10     printf("%d
    ",*p);
    11 }

    在C-Free里程序给出了警告: assignment of read-only variable `p'(意思是给只读变量的p赋值)

    在vc6.0里程序给出了错误信息:l-value specifies const object。(意思是左边这个值是常量)

    C-Free和vc6.0用的编译器不一样,一个是minGW,另一个是cl  所以给出的错误提示不一样。

    指针变量:就是指针的指向是可以改变。可以想指谁就指谁,如可以指向整型数据,也可以指向指针,也可以指向结构体。

    一级指针:就是只有一只手。如 int *p;//p就是一级指针

    二级指针:也是一只手,呵呵。很好理解,不管是几级指针,它都是指针,只要是指针,就可以认为是一只手,只不过指向的对象(必须是二级指针)变了。如int **p;

    三级指针:和二级指针类似,指向的对象是三级指针,可以是三级指针变量,可以是三级指针常量。如int ***p;。

    依此类推。。。

    说明:不管是什么类型的指针在内存中都是占4字节。(如果用的很老的系统和编译器,指针所占的空间可能不是4字节)

    接下来进入主题讲数组指针。

    现在有一个一维数组:int b[5];

    我现在想要定义一个指针变量,指向b,该怎么做呢?

    先分析一下数组b, b是数组名,如果我要取数组里的第二个元素,可以这样做 b[1]   ,这样就ok了。实质上编译时b[1]会展开,变成   *(b+1),

    由此可见b是一个指针。实质上是一个指针常量。指向这个数组的第一个元素b[0].也就是说 b的值和&b[0]的值是一样的。

    呵呵,答案已经清晰了,如果我想要指向这个数组,只需要指向数组的第一个元素就可以了。可以这样写 int *p=&b[0];//或者int *p=b;

    我相信学C/C++的人都遇到过这样定义,int (*p)[5];//这个数组指针,这里的p就是一个指针,但是指向的对象是什么还需要研究

    并且很多人(包括本人在内)认为p指向一维数组,并且这个数组的长度是5。

    但是其实并不然,p是指向二维数组的。

    其实int (*p)[5];等价于int[5] *p;  这就不难看出p指向的对象是int[5]类型的数据。

    int b[5];等价于int[5] b;如果我想要让p指向b,必须这么写,p=&b;//因为b是一个指针,对b取地址,也是一个指针,那么p是一个二级指针。

    现在通过代码证实一下:

     1 #include <stdio.h>
     2 
     3 int main(void)
     4 {
     5     int i;
     6     int b[5]={1,2,3,4,5};
     7     int (*p)[5];
     8     p=&b;
     9     for(i=0;i<5;i++)
    10         printf("%d ",p[i]);
    11     printf("
    
    ");
    12 }

    运行的结果:

    看到这样的结果是不是很吃惊。

    从打印出来的结果看,p[i]也是地址。

    如果p是一级指针,那么p[i]打印出的肯定是这个地址内容,应该是1,2,3,4,5。但是实际结果并不是如此,由此断定p是一个二级指针。

    b[0],b[1],b[2],b[3],b[4]的地址是:

     1 #include <stdio.h>
     2 
     3 int main(void)
     4 {
     5     int i;
     6     int b[5]={1,2,3,4,5};
     7     int (*p)[5];
     8     p=&b;
     9     for(i=0;i<5;i++)
    10         printf("%d ",&b[i]);//打印数组b每一个元素的地址 
    11     printf("
    
    ");
    12 }

    运行结果:

    从两次的结果上显示,p[i]打印出的地址并不是b[i]的地址值。为什么呢?

    这得从p的类型讲起。p的定义可以这么写:int[5] *p;

    现在p的值是&b,&b的值其实和b的值一样,所以p的值是2686736

    那么p+1的值是多少呢。实质上偏移的字节数是sizeof(int[5]),  一个int是4个字节,那么5个int就是20个字节。所以第一张运行的结果图中,都是以20的大小在增长。

    呵呵,如果我想要用p打印出1,2,3,4,5.就可以这么写了(*p)[i].  因为(*p)的值和b一样

     1 #include <stdio.h>
     2 
     3 int main(void)
     4 {
     5     int i;
     6     int b[5]={1,2,3,4,5};
     7     int (*p)[5];
     8     p=&b;
     9     for(i=0;i<5;i++)
    10         printf("%d ",(*p)[i]);//打印数组b每一个元素的地址 
    11     printf("
    
    ");
    12 }

    运行结果:

     综合:

    如果有定义int b[5],(*p)[5];要想利用p打印b的所以元素,需要将p指向b .然后再(*p)[i]。

    现在看一下p指向二维数组的情况:

    #include <stdio.h>
    
    int main(void)
    {
        int i;
        int a[3][5]={{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};
        int (*p)[5];
        p=a;//因为p是一个二级指针,所以可以直接把a赋值给p,此时p是指向第一行的 
        for(i=0;i<5;i++)
            printf("%d ",p[0][i]);//打印数组b每一个元素的地址 
        printf("
    
    ");
    }

    运行结果:

    p=a;后相当于a取了一个别名,所以对p操作,实质上是在操作a.

    注意事项:二维数组的第二维的长度必须与数组指针的维度保持一致,如果不是这样,会导致警告或错误(是编译器而定)

    总结:

    如果想要用数组指针指向一维数组,就用形如 int *p;的方式。//虽然可以用int (*p)[n];的方式,但是增加了繁琐。也不便理解,容易出错。

    如果想要用数组指针指向二维数组,就用形如int (*p)[n];的方式。

    如果想要用数组指针指向三维数组,就用形如int (*p)[m][n];的方式。//如 int b[5][m][n];  p=b;

    扩展:现在讲一个动态分配二维数组的程序

    #include <stdio.h>
    #include<malloc.h>
    typedef int datatype;
    
    datatype** fun(int m,int n)
    {
        //先申请二维数组的第二维空间
        datatype** p=(datatype**)malloc(sizeof(datatype*)*m) ;
        int i; 
        for(i=0;i<m;i++)
            p[i]=(datatype*)malloc(sizeof(datatype)*n);
            
        return p; 
    }
    int main(void)
    {
        int i,j,m=4,n=5,k=1;
        datatype **p=fun(m,n);
        for(i=0;i<m;i++)
            for(j=0;j<n;j++)
                p[i][j] = k++;
        
        //打印二维数组p的每一个元素 
        for(i=0;i<m;i++)
        {
            for(j=0;j<n;j++)
                printf("%4d",p[i][j]);
            printf("
    ");        
        }
        
        printf("
    
    ");
    }

    //动态分配二维思路很简单,就是动态申请地址空间,这些空间里存放指向每一行的首地址。然后在申请真正可以用来存放数据的空间。

  • 相关阅读:
    【转发】JS中如何判断null/ undefined/IsNull
    CSS3实现两行或三行文字,然后多出的部分省略号代替
    关于CodeFirst的使用教程
    把一个字符串里符合表情文字标签的地方全部替换为相应的图片的方法
    js 给json添加新的字段,或者添加一组数据,在JS数组指定位置删除、插入、替换元素
    用css3选择器给你要的第几个元素添加不同样式方法【转发】
    WebApi2 知识点总结
    把字符串每隔四个字符使用“-”中横线分隔的方法
    C语言strchr()函数:查找某字符在字符串中首次出现的位置
    linux 下安装开发组件包
  • 原文地址:https://www.cnblogs.com/dzqdzq/p/3243621.html
Copyright © 2011-2022 走看看