深入浅出数组指针

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

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

 

何为数组指针?

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

 

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

 

何为指针?

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

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

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

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

运行结果:

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("\n");        
    }
    
    printf("\n\n");
}

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

 

转载于:https://www.cnblogs.com/dzqdzq/p/3243621.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值