深入理解指针(第一卷)

       很喜欢一部动漫,《星游记》,有这样一句话:“管你什么乱起八糟的,我们只是要,往前走啊!”

来看一下我们这次要抵达的彼岸吧!

一:内存和地址

二:指针变量和地址

三:指针变量类型的意义

四:const修饰指针

五:指针运算

六:野指针

七:assert断言


1.内存和地址

        在深入理解指针前我给大家引入一个生活中的案例:某地呢!有一栋宿舍楼,该宿舍楼最大特点是,没有房间号也没有楼层号,这时同学a进入一个房间,b同学要找a同学玩,那么b同学只能随机访问每个房间,不能很快找到a同学,那如果给楼层和房间标上对应的楼层号和房号,那么b同学能够直接能够锁定a的位置,找其玩耍!

        将上面案例对应到计算机中,CPU访问数据要去内存中读取,那么内存如何高效管理呢!这里把内存划分成了一个一个的小的内存单元,并且规定一个内存单元的大小必须是8bit(比特位)也就是1byte(字节),目前CPU仍然不能直接迅速的访问到想要的数据(这里类比上面的案例),所以我们要为内存单元进行对应且唯一的编号标识,这样CPU就能够像同学b一样迅速找到对应的房间(内存单元)!c语言标准对内存单元的编号进行了规定,编号 = 地址 = 指针

Bit(比特位)

8Bit = 1Byte
Byte(字节)1024Byte = 1KB
KB1024KB = 1MB
MB1024MB = 1GB
GB1024GB = 1TB

补充一点:1bit可以存放二进制位中的一个1或者一个0。

       我们接着对上面引出来的地址进行一个扩展学习,地址如何进行编址!首先CPU向内存访问数据是硬件和硬件之间的交互,那么想让硬件和硬件建立起联系,则将硬件用线(主要关注地址线)进行连接,这时CPU就可以和内存进行交互了。例如:32位操作系统有32根地址线,通电后地址线将电信号转化为数字信号就对应有高电平1和低电平0,一根地址线有2^1种序列,俩根地址线有2^2种序列,那32根地址线有2^32种序列,这里每一种序列代表一个地址。

补充一点:32位操作系统管理权限是2^32个内存单元,但并不代表内存一定是4GB。

再给大家通过图解方式理解上面内容!如图一;

图一

2.指针变量和地址

      创建一个变量本质是什么? 

1.  int  a  =  10;
解释:变量a在内存中开辟了4个内存单元(int 4字节),用来存放数值10。

2.1 取地址操作符(&)

       明白了创建变量的本质后,引入取地址操作符(&),使用该操作符可以拿到一个变量的地址;现将a变量进行取地址操作(&a),则取出的地址是4个内存单元中地址最小的内存单元的地址!如图二。

图二

2.2 指针变量      

       取地址操作符+变量名,可以拿到一个变量的地址,再将该地址进行存放方便后期使用,那该地址存放在哪里合适呢?答案是:指针变量

       指针变量是专门用来存放地址的,反过来说,存放在指针变量里的数据都会被指针变量认为是地址,下面对指针变量的形式展开解释!

1.  int  a  =  10;

     int *p  =  &a;

解释:将a变量的地址取出,存放在指针变量p中。
2.  int *p  =  &a;

解释:int *p  =  &a;语句中 int  和  *  的意义!

           int:代表指针变量p指向的是一个数据类型为int的空间。

           *  :代表p是指针变量。

       再给大家以图解的方式理一下逻辑!如图三!

2.3 解引用操作符      

      既然我们将指针变量进行了存放,目的就是为了有朝一日使用该地址。如何使用呢?是通过解引用操作符(*)对指针进行解引用,即可找到指针所指向的空间,进而操作这块空间。

1.  int a = 10;

     int * p = &a;

     *p = 20;

解释:*p:通过指针变量p所存放的地址,间接找到指针变量p所指向的a空                        间,并且将这块空间的内容改为20;

    本质上: *p 等价于 a  , a = 20;

2.4 指针变量的大小

        既然已经知道指针变量里存放的是地址,那指针变量的大小也可以推算出来;在32位操作系统下,地址总线大概包含有32根地址线,上电后由电信号转化为数字信号1或0,那么32根地址线就有2^32中序列,每一种序列代表一个地址,每一种序列都是由32个1或0组成,所以32位操作系统下指针变量要想存放地址就需要开辟4个字节的内存空间,64位操作系统皆是如此!总结如下!

32位操作系统int大小为4字节指针变量大小为4字节
64位操作系统int大小为4字节指针变量大小为8字节

     再给大家贴一张图证明上面的推理没错,如图四!

图四

       大家有没有这样想过,指针变量的数据类型虽然发生变化,但在内存中开辟的空间是不会改变的,那么指针变量花里胡哨的数据类型也没啥用吧!针对大家的想法和好奇在标题三中会为之解答! 

补充:为什么不管是32位操作系统还是64位操作系统,int的大小不会发生变化呢?

1. 兼容性:因为大量的软件都假设int的大小是4字节,所以64位操作系统也保持了int的大小为4字                     节,这样软件就可以在64位操作系统上无需修改就可正常运行。

2.性能上:使用更小的数据类型通常可以提高性能,因为数据类型小可以有效利用CPU的缓冲,同                      时也减少了内存的占用和数据传输时间。


三:指针变量类型的意义

3.1 指针解引用的大小

不必关心内存1中变量a内容的存放顺序,若想了解可参考数据在内存中的存放。

       前面我们拆解了指针变量数据类型的含义,int*中的int代表指针变量p认为自己指向的是一个数据类型为int的对象,所以再进行*p的时候指针变量p就可以访问4个字节的大小。

       同理char*中的char代表指针变量p认为自己指向的是一个数据类型为char的对象,所以再进行*p的时候指针变量p就可以访问1个字节的大小。

       总结:指针变量数据类型的不同,会使指针解引用时访问权限发生变化。

指针数据类型:char*解引用访问权限:1个字节
指针数据类型:short*解引用访问权限:2个字节

指针数据类型:int*

解引用访问权限:4个字节

3.2 指针+-整数

       指针变量pa骨子里认为自己指向的是char类型的对象,所以pa+1就向后走一个字节的距离,同理pb+1向后走四个字节的距离,减法亦是如此!

       总结:指针+-整数,可使指针向前或者向后走一步跳过多大距离。

       补充:对一个普通变量直接取地址,则该指针的数据类型是:普通变量类型 + *;

3.3 void*(任意类型指针)

        顾名思义任意类型的指针可以接受任意类型的地址,该指针的兼容性很好,可以根据自己的意愿对void*指针进行强制转换再去使用,主要应用在后面的泛型编程中;void*指针的缺点是不能进行解引用和加减运算,因为该指针的数据类型是任意类型,是不确定的。


四:const修饰指针

        我们在创建一个普通变量后是可以修改内容的,但如果用const加以修饰,那么该变量再无法对其进行修改。

#include<stdio.h>
int main()
{
   int a = 10;
   a = 20;
   
   const int b = 10;
   //b = 20;  //报错  
 
   return 0;
}

       原因是:变量被const修饰后,使其具有了常属性,但本质仍为变量,这里给大家证明一下。

#include<stdio.h>
int main()
{
   const int a = 10;
   
   int arr[a] = {0};

   //vs环境下数组的大小必须是常量,而这里用const修饰的变量会报错。
 
   return 0;
}

      上面通过const限制了a变量,但如果我们获取到了a变量的地址,就可以间接的去操控a的内存空间。

#include<stdio.h>
int main()
{
   const int a = 10;
   int * pa = &a;
   *pa = 20;
 
   //通过指针修改了a变量的内存空间。
 
   return 0;
}

       走到这里思考一下,既然用const修饰了该变量,目的就是不让变量的内容再被修改,而这里很明显违背了最初代码的用意,所以呢!我们就要用const关键字去修饰指针变量。

       第一种修饰方式:const关键字修饰在指针类型的*号边:指针变量不能再进行解引用,进而无法修改指针指向的内存空间,但可以修改指针变量所存放的内容。

#include<stdio.h>

int main()
{
  int a = 10;
  int b = 20;

  const int * pa = &a;
  //const也可以写成 int const * pa = &a;

  //*pa = 20;//报错

  pa = &b;

   return 0;
}

        第二种修饰方式:const关键字修饰在指针类型的*号边:指针变量再进行解引用,进而可以修改指针指向的内存空间,但不可以修改指针变量所存放的内容。

#include<stdio.h>

int main()
{
  int a = 10;
  int b = 20;

  int * const pa = &a;

  *pa = 20;

  //pa = &b;//报错

  return 0;
}

       以上就是const关键字值得注意的地方。

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值