【C语言】指针和内存的认知

前言

人们常说啊,C语言因为有了指针,而有了生命感,但是初学者对于指针,却很是苦恼,指针是什么?是干嘛的?为什么要有指针?有应该怎么用?对此,博主梳理了指针的全部知识点,带领大家一起来深入的理解一下指针

内存和地址

学习指针,就一定离不开内存,而内存最直白的表现形式就是地址,我们就先来讲讲什么是内存,什么又是地址。我们可以很形象理解成

  • 内存 :房间
  • 地址 :门牌号

当你去一家酒店,跟酒店经理说:“来,给我来一间房间。”酒店经理就会回答:“好的请稍等,您的房间号是XXX。”这个时候你就可以用这间房间,并且可以根据大堂经理说的门牌号找到自己的房间。
那么到底要多大的房间呢?这就涉及到内存的大小,再计算机中常见的内存大小和转换如下

在这里插入图片描述
在这里插入图片描述
内存再计算机中的存储,是连续条状存储的,存储的基本单元是,字节,一个字节8个比特位,而每一个字节就像一间房间,而每间房间都有一个“门牌号”就是地址,而在每间房间8个比特位的大小里面,你就可以存放自己想要存放的东西,这就是内存和地址
在这里插入图片描述

指针变量和地址

了解了内存和地址的关系,我们回到C语言中,C语言中创建变量其实就是在内存开辟空间
在这里插入图片描述
我们给a开辟了一个0x009FFB5C地址的空间,里面存放的就是10(16进制是a,这里显示的是16进制)
那我们应该如何索取a的地址,这里就需要用到取地址(&)操作符
在这里插入图片描述
注意了,这里打印出来的地址,只是a的4个地址中最小的一个地址,我们知道int类型所占4个字节,所以应该是要开辟一个有4个字节大小的空间,但是我们内存是连续存放的,并且最小存放单位都是字节,所以就会往下面紧跟着开辟空间,我们拿到的只是第一个字节的空间地址,就类似于下面的图片,这只是一个a所占有的连续的开辟的空间,当我们用到a的时候就可以找到开辟内存的第一个地址,往下顺藤摸瓜把里面的内容都找到
在这里插入图片描述

指针变量和解引用操作符(*)

我们拿到地址之后,总要想办把他存储起来方便使用,就像int a = 10;我们把10存放到int类型的a变量当中,同理,地址一样可以存放,我们把地址存放的地方叫做指针变量,指针变量,也是一种变量,只不过是专门用来存放指针。
在这里插入图片描述
这里的p我们就叫作指针变量,他的类型是(int*)代表指针类型,所以指针就是地址,地址就是指针。
我们再用画图的方法,来理解一下指针变量

在这里插入图片描述
p和a都是变量,都有各自的地址,但是一个类型是int*一个是int,一个是存放地址,一个是存放整型。
如果有一个char类型的变量,那应该用一个什么样的指针类型接收呢?没错应该用char*指针类型
在这里插入图片描述
我们把指针存起来,将来怎么使用呢?
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
在这里插入图片描述
我们可以通过解引用操作符直接更改存入指针变量中地址所指向的地址的值。

指针变量的大小

变量都有一个大小,取决与他的类型,就像前面提到的int a = 10;a的大小就是int类型的大小4个字节如果是double a = 10.0;那么a就是double类型的大小就是8个字节,同理指针变量的大小又是多少呢?
指针变量的大小取决与编译器的环境,在32位下大小为4个字节,在64位下大小为8个字节。所以指针变量的大小和存放的指针类型无关,无论你是int*还是char*在32位下都是4个字节,在64位下都是8个字节
在这里插入图片描述

指针的解引用

竟然指针类型的大小都一样,那是不是用不同类型的指针变量都可以接收一个相同的值,答案是正确的!但是他们所包含的意义千差万别
在这里插入图片描述
经过调试,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。

指针±整数

我们先看一段代码,观察地址的变化
在这里插入图片描述
我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异。
总结:指针的类型,决定了指针向前或者向后走一步有多大(距离)。

void*指针

void*指针可以用来接收任何类型的地址,但是他不可直接解引用和±运算,因为编译器不知道是那种类型,在使用之前,必须先强制类型转化,在进行解引用和±运算

const修饰指针和修改指针变量

const修饰变量

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。
在这里插入图片描述

const修饰指针

我们知道了const修饰变量能让他无法直接修改,但是当我们用指针去接收变量的地址,再去修改发现,一样是可以修改的,那么怎样才能让他用指针也无法修改呢?那就是用const修改指针
在这里插入图片描述
在这里插入图片描述
我们注意看const放的位置,只要是放在解引用*之前都是可以的,都能满足要求。这时候就不能通过指针解引用来修改作指的对像的数据。

const修饰指针变量

const还能修饰指针变量,上面我们讲的是const修饰后,无法修改指针所指向的对象的值,而const修饰指针变量就是const修饰之后无法修改指针的指向,但是还是可以修改指针指向对象的值。相信大家有点懵,我们来看一下代码
在这里插入图片描述
我们用画图的方式来讲解
在这里插入图片描述
简单来说,就是const修饰的对象不同。
结论:const修饰指针变量的时候
const如果放在* 的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。但是指针变量本⾝的内容可变。
const如果放在* 的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

指针的运算

指针的运算有 2种分别是

  • 指针 ± 整数
  • 指针 - 指针

指针 ± 整数

因为底层内存是连续存放的,所以指针±整数就是指针指向的地址向前或者向后移动,这类似于数组,因为数组空间的开辟也是连续的空间,我们可以把指针和数组相结合,是可以通过指针来访问数组的。我们看下面代码
在这里插入图片描述
在这里插入图片描述
我们取出数组的地址,把它放到指针变量p中,因为我们拿到的也是数组第一个存放的地址,然后通过解引用就可以拿到第一个下标为0的元素,和arr[0]一样,然后每次让下标往后移动即可。
我们不难发现

*(p+0) = arr[0]
*(p+1) = arr[1]
*(p+2) = arr[2]

而我们知道数组名就是首元素的地址(可以在自己的编译器上面验证),所以也可以写成
*(arr+0) = arr[0]
*(arr+1) = arr[1]
*(arr+2) = arr[2]

那么数组的底层到底是什么?他为什么可以用指针来运算,其实,我们写arr[x]他底层都会转换成*(arr+x),就可以理解成,首元素地址+偏移量,去底层查找相应的位置,找到存放的内容
竟然这样,我们都知道,加法满足交换律,竟然底层都是首元素+偏移量来计算那是不是arr[x]可以写成x[arr]呢???因为按照底层x[arr] =* (x+arr)=* (arr+x)=arr[x]!!
没错,是的!这种写法是认可的,你甚至可以写成p[x] (因为p的地址就是arr)、x[p]都可以,是不是惊呆了,老铁!所以,只要我们知道了底层的逻辑,你想怎么写都是没问题的~

指针 - 指针

指针 - 指针的绝对值是元素之间的个数,初学者可能会以为是它们之间地址差值,但是不是的,看下面的代码
在这里插入图片描述

  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值