超易懂-指针

  个人博客网站文章地址:http://blog.mclink.xyz/index/article/index/id/41.html

  指针,一直都是c语言中的难点,刚开始大一刚学C的时候就感觉懵逼,一直都是简简单单去理解,简简单单的使用,从没有说去深刻研究一下,尤其是现在决定搞网站开发了,以后估计更少用到,所以趁现在有点时间赶紧好好看一看。好好的搞懂它。

 一、什么是指针?

  这里就有个很多人容易搞混的概念,什么是指针,什么是指针变量,不懂吗?那就仔细听我说明吧,指针实际上就是地址,例如我们平常所说的某个函数返回一个指针,其实返回的是一个地址,而指针变量则是存储地址的变量。正常来说,我们使用c语言定义一个类型的变量时,就给这个变量名赋予了一个内存空间,如果单纯只定义变量而不给其赋值,那么它的值是不确定,在不同的编译器不同的计算机都可能会有不同的值,因为操作系统回收内存单元后并不会清除其内容,当程序运行时需要为变量分配空间时就从这些回收的单元中进行分配,例如:

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int a,b,c;
    printf("%d,%d,%d",a,b,c);
    return 0;

}

打印出来的结果并不是没有值,而是:

当你为其赋值时才会重写该地址的内容。我们经常会听到书上说,如果你定义一个指针变量而不给予赋值,是件很恐怖的事情,那是为什么呢?其实这是为了避免野指针的出现。

什么是野指针?

  野指针是指向一个已删除的对象或未申请访问受限内存区域的指针。与空指针不同,野指针无法通过简单地判断是否为 NULL避免,而只能通过养成良好的编程习惯来尽力减少。对野指针进行操作很容易造成程序错误。

野指针有什么危害呢?

我在这里引用知乎的一句话:设想,你家里有个物体,不知什么时候突然出现,也不知什么时候突然消失。会把你的东西乱挪位置,还时不时打碎个瓶子。这个物体,在计算机的世界叫野指针。在现实世界,叫猫。

所以为了避免野指针的出现,我们需要有良好的编程习惯,例如:

1.指针变量一定要初始化为NULL,因为任何指针变量(除了static修饰的指针变量)刚被创建时不会自动成为NULL指针,它的缺省值是随机的。

2.释放时置 NULL,当指针p指向的内存空间释放时,没有设置指针p的值为NULL。delete和free只是把内存空间释放了,但是并没有将指针p的值赋为NULL。通常判断一个指针是否合法,都是使用if语句测试该指针是否为NULL。

指针变量与其地址

  我们知道,定义一个指针变量通常都是type *变量名的格式。type是数据类型,例如int,char等等。我们前面有说,指针变量实际上是存储地址的变量,而这个地址往往是另一个变量的地址。例如:

// 指针变量与其地址,普通变量与其地址的区别
 int main()
 {
     int a=2;
     int *b=a;
     int *c=&a;
     printf("%d,%p,%d,%p,%p,%p,%d\n",a,&a,b,&b,c,&c,*c);
     a=3;
     printf("%d,%p,%d,%p",a,&a,b,&b);
     return 0;
 }

打印的结果是:

我们对其大致分析一下,首先我们定义的一个变量a,a的值为2,所以打印的a为2,&a是a的内存地址,我这里地址统一用%p(指针)来输出,然后又定义了一个整型指针b,把a的值赋予了它,这种写法是有的,虽然我们前面说了指针变量是用来存指针(地址)的变量,但是给值不给地址这样也是可以的,这样的话b就指向了a的内容。打印b实际上就是打印a,实际上这个不等于一种引用,因为b也是有自己的内存空间的,类似于一种副本,所以我在后面又将a的值改为3,尝试打印a,b的值是否还相同,从结果可看出,其实b的值并不会随着a的变化而变化,说明这种用法并不是一种引用。

 然后我们看真正的用法,为定义的指针赋予地址的值,在上面的代码中,我将a的地址赋值给了指针变量c,所以c内存地址的内容应为a的内存地址,我们可以看上面的输出,可以看出,c的值刚好是a的地址,&c的值的c自己的地址,而*c则为a的值,关于*c,我们可以这么理解。Int *c=&a,那么a就是c的一个对象,则*c就是指针变量c指向的那个对象的值。

 现在你应该十分了解指针了,那我们在来看看,

二、数组作为参数和指针有什么关系?

首先我们先来了解一下什么叫数组指针,什么又是指针数组。

我们常会听到说这里有个整型数组,是用来放整型的元素的。其实指针数组跟它是一样的,不过它是用来放指针(地址)的而已,而数组指针又是什么呢?其实数组指针就是一个指向了数组的指针变量。在这里引用一下大佬的解析:

数组指针(也称行指针
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
 p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针
指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值a的首地址的值
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间

这是引用网上大神的解释,重点我都加粗了,应该不难理解。

知道了这个我们再来看一个例子,数组作为参数到底是怎么回事。

上代码:

//区别数组参数,数组指针,数组间的区别
int getsize(int data[])
 {
     printf("%p\n",data);
     return sizeof(data);


 }
 int main()
 {
     int data[]={1,2,3,4};
     int size1=getsize(data);
     int size2=sizeof(data);
     int *da=data;
     int size3=sizeof(da);
     printf("%d,%d,%d\n",size1,size2,size3);
     printf("%p,%p,%p",data,da,&data[0]);
     return 0;


 }

打印结果是这样的:

简答解释一下代码,sizeof()是用来计算字节数的。我定义了一个getsize()来获取传入的数组的字节数,然后来区分不同情况下数组的字节数情况。我们都知道整型是4个字节的。先来看第一个size1,这里我将一个含有四个元素作为参数传给了自定义的getsize函数,让它返回参数的字节数,这里我们可以知道打印出的结果是4,而第一个直接获取定义的数组的结果却是16,这是为什么呢?实际上,当一个数组作为参数传递给函数时,它是以数组首元素的指针(地址)的形式传过去的,即是数组data[0]的地址,所以它的字节长度结果为4,接下来我们看下size3,我使用一个指针变量da来将data直接赋值给他,实际上赋的值也是数组data的首元素,所以使用sizeof()获得的也是4个字节。

接下来我们来看看地址。从打印结果可以知道,数组作为参数传进函数后打印出的地址和data,da,&data[0]的首字节地址都是一样的。这里我为什么说是首字节呢。因为在32位以上的系统,int是4个字节,一个字节又是8比特,因为内存单位是字节,所以我说这个指针实际上是首字节的地址。

我讲了那么多,相信你对指针有一定了解了吧。不要问我为啥要写这篇文章,我闲的蛋疼。

  • 17
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MClink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值