手撕指针第一页

1.  理解内存和地址

1.1  内存

内存,顾名思义就是电脑用来存储数据的,当CPU(中央处理器)在工作时,不仅需要从内存中拿取数据也需要将数据放入内存当中,当把内存引入到现实当中,就像学校里面的宿舍楼一样,每个宿舍楼有许多间宿舍,每一间宿舍住着8个人,内存里面有许多的内存单元,每一个内存单元就像一间宿舍一样,而这个内存单元叫做字节,每个字节又相当于8个比特位,每一个比特位对应8人间的每一个人。

1.2  地址

宿舍有宿舍的地址,每一个内存单元也有自己的地址,宿管阿姨可以通过寝室号找的你的宿舍,而程序员也就可以通过地址找到内存里面存储的数据,在C语言中地址也叫作指针。

                                            内存单元的编号==地址==指针

2.  指针变量

2.1  取地址操作符(&)

&:取地址操作符是将一个变量的地址取出来

当我们创建变量时,就会向内存申请一块内存空间,就像我们在外住宿时就会去酒店开一间房间,但我们要去房间住宿时,前台会给我们房间号找到这个房间,同样的,当我们需要将一个数据放到一块内存空间里面时,就要通过&(取地址操作符)找到这个变量的地址

我们可以通过监视窗口看见&a的值,再通过内存窗口发现a的地址和&a的值是一样的

所以&(取地址操作符)的作用就是获得一个变量的地址,然后我们通过这个地址将数据存储到变量里面

2.2  如何创建一个指针变量

简单介绍一下指针变量是什么:

指针变量就是一个存放指针(也就是地址)的变量

创建指针变量的基本格式:

解释:

                左边分别是整形变量,字符变量,浮点型变量。右边是他们对应的指针变量。

这⾥p1,p2,p3左边写的是 int*,char*,double* , * 是在说明p1,p2,p3是指针变量,⽽前⾯的 int ,char,double是在说明p1,p2,p3指向的是整型(int),字符型(char),浮点型(double)类型的对象

2.3  解引用操作符(*)

我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢?
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。
C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)

            1.(*)解引用操作符对比生活,他就像一把钥匙,当我们知道一个房间号(地址),想要拿到里面的东西,就需要一把钥匙把房间打开才能拿到里面的东西,当然出来从里面拿东西也可以将里面的东西腾出来,放入新的东西。

            2.printf("%d",*p);p里面存放着a的地址,我们用钥匙(*)打开这个地址,就可以看见里面存放的东西也就是100

            3.*p=10;就是将10放入到p所指向的地址里面去,从而改变了a的值

2.4  指针变量的大小

我们知道,32位机器下有32根地址总线,每一根脉冲信号为0或1,而这些脉冲信号组成的二进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储,如果用指针变量来存储一个地址,至少需要4个字节才能存储,同理在64位机器下需要8个字节才能存储这些地址

小结:

• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是       相同的。

3.  指针变量的意义

既然各种各样的指针在相同平台下的大小都是相同的,为什么还要弄这么多的指针类型,用一个指针不就行了吗?那么接着往下看,你就会知道设计师为什么这么设计。

3.1  指针的解引用

用例1:

当我们创建一个整形变量,并且给他赋0x11223344(因为8位的16进制数刚好可以占满4个字节),当用整形指针来接收a的地址,再将p解引用再赋值为0时,通过内存窗口可以看见,4个字节全部被修改为0.

用例2:

但我们用字符指针来接收a的地址时,再将p解引用再赋值为0时,通过内存窗口可以看见,发现只有第一个字节被修改为0

结论:

         指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节

3.2  指针加减整数

通过代码我们可以发现,char*类型的指针+1跳过一个字节,而int*类型的指针+1跳过4个字节,指针的类型决定了指针向前或者向后走一步能跳过几个字节

3.3  void*类型的指针

3.3.1  void类型

1.void类型和char,int,double等类型的区别是:void类型不能用来声明变量

   下面这种声明变量是错的

void a=10;

2.void类型也常常出现在函数返回值,表示该函数无返回值

void compar(参数1,参数2);

3.void类型也可以出现在函数参数里

int fun(void);          int fun();

这两种写法是等价的,都表示不需要给函数传递参数

3.3.2  void*指针

void*指针也叫做泛型指针(无具体类型的指针),他可以用来接收各种类型的指针,但是他不能直接进行指针加减整数和解引用的操作,需要进行强制类型转换才能进行指针加减整数和解引用的操作

两段代码对比,用void*类型的指针来接收int*类型的指针是可以的,但是直接对p解引用是错误的,我们需要将void*类型强制转换为int*类型才能使用

⼀般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。使得一个函数来处理多种类型的数据,在下面讲到qsort函数时,我们会对void*的使用有更深的了解

4.  指针的运算

4.1 指针加减整数

指针加减整数表示指针可以跳过几个字节

数组在内存中是连续存放的,我们将3的地址给到p,当我们对p进行加一操作时,可以发现在3地址的基础上加上了4个字节,得到了4的地址,从而访问到了4,同理,1,2,5也是同样的原理

4.2 指针减指针

指针加减整数我们知道得到的还是指针,那么指针减指针得到的就是整数,这个整数也就是两个指针之间相差多少个元素

arr[0]和arr[5]之间相差20个直接,而int*类型加1跳过4个直接,所以p1-p为5

有指针减指针,有指针加指针吗?

看看下面这两段代码

两个指针相加的到的是指针还是整数?答案是都不是,这种写法是错误的。

4.3 指针的关系运算

指针的关系运算简而言之就是指针比大小,指针不都是地址吗?地址也有大小吗?答案是地址也有大小

我们可以看出p1的值比p的大,而这两个值分别是a[3]和a[0]的地址,所以指针是可以比较大小的

5.  野指针

5.1 常见的三种野指针

  1.未进行初始化的指针

创建了int*类型的指针,但是并没有给p赋值,所以不知道p指向何处,故p为野指针

2.越界访问的指针

第11个数打印的是随机值,指针指向的地址超出arr,p就是野指针

3.指针指向的空间被释放掉

在test()里面创建了一个n变量,返回n变量的值,用p来接收,但是当&n返回时,也就是出test()函数是,n变量就已经被销毁了,虽然还能打印出n的值,但是p是一个野指针。

5.2 如何规避野指针

1.创建指针变量时,对指针进行初始化,如果指针没有明确的指向时,可以将指针初始化为NULL,NULL是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错

2.防止指针越界访问,我们向内存申请了多少空间,那就让指针在申请的空间内进行使用,不要超出范围访问

3.当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问,同时使⽤指针之前可以判断指针是否为NULL

4.避免返回局部变量的地址

6.传值调用和传址调用

我们在写自定义函数时常常会进行传参,参数可分为传值和传址

6.1 传值调用

这是一个交换函数,我们将实参传给形参,然后形参进行交换,最后发现x和y的值没有发生改变,这是因为当实参传给形参时,形参是实参的一份临时拷贝,当函数进行结束后,形参会被销毁,而在整个函数进行过程中一直是形参在参与运算,实参一点也没有参与,所以最后实参的值不会被改变,形参和实参分别占有不同的内存块,对形参的修改是不会改变实参的。

6.2 传址调用

我们将swap的参数部分改为指针,然后通过指针去交换x和y的值,发现可以将两者进行交换,这是因为,我们通过地址直接对x,y进行修改,就算局部变量销毁,也不会影响x和y的值,让函数和函数外面的变量建立起联系,也就是函数的内部可以直接操作函数外部的变量。

  • 60
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 37
    评论
评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值