目录
免责申明(dog:
以下是我对指针的学习和片面的见解,有问题可以在评论区告诉我,我会改的(手动比心)。
一.内存和地址
1.内存
我们首先要知道CPU在处理数据的时候,需要的数据是从内存中读取的,处理后数据再存放回内存中
然后我们需要明白哪些内存空间如何高效的管理,哪么它们是如何管理呢?(自问自答.ing)
它们将内存分为一个个单元内存,每个内存单元的大小为1个字节。
(内存的单位)
(盲猜有人还没背/dog)
每个内存单元都有自己的一个编号,而CPU要快速的访问一个内存空间就需要这个编号,而内存单元的编号就是地址 也叫做指针。
内存单元编号==地址==指针
1.2如何理解编址
了解编址之前我们要先了解计算机内是有很多的硬件单元(比如:内存,CPU)而硬件单元是要互相协同工作的。所谓的协同,就是相互之间要能够进行数据传递。
问:内存编址的目的是什么呢?
答:是为了方便寻找到每一块空间,所以需要对每一块空间进行标识。
问:而它们如何协同如何通信呢?
答:用“线”连起来
因为CPU和内存之间有着大量的数据交互,所以也要用线连起来,而其中我们比较重要也比较关心的一组线,叫做地址总线。
CPU访问内存中的某个字节空间,需要知道这个字节空间在内存的什么位置而内存中字节有很多所以要给每个内存进行编址。
计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。
32位机器有32根地址总线,每根线只有两种状态就是0和1(电脉冲有无),我们知道一根线可以表示2中含义那么2根线就能表示4总含义,以此类推32根线就能表示2的 32次方种含义,每一种含义其实就是一个地址。
如果内存里的值需要放到CPU中进行计算那么它会给控制总线一个读(r)的信号,通过地址总线产生要找的内存的指针,然后找到内存中恰好对应的地址通过数据总线传输会CPU进行计算(可以配合底下的图进行理解)
二、指针变量和地址
1.取地址操作符(&)
我们知道在C语言中创建变量就是在内存中申请空间。
如图我们看到代码中创建了整形变量a他在内存中申请了4个字节的内存空间,来存放10(16进制为a)其中4个字节的地址分别为:
问:哪我们如何得到地址呢?
答:运用取地址符(&)
举个栗子:
我们可以看到左边的代码的结果是0060FFBF4;是a申请空间中地址较小的一位地址。
哪么有人可能就问了:这有啥用呀?
答:我们通过知道类型的大小(int 的大小是 4 个字节)和运用取地址符取到的地址,我们也就可以知道这个变量申请的所有的内存空间的地址。
1.2指针变量和解引用操作符(*)
1.2.1指针变量
问:有时我们要将取地址符(&)取来的地址存放起来方便后期的使用,哪么我们要储存在哪里呢?
答:指针变量中
举个栗子:
指针变量也是一种变量,是一总专门存放指针的一种变量,存放在其中的值都会理解为地址。
1.2.2如何拆解指针类型
int * pa中pa是指针变量(int *pa)的名称而类型是 int * ,那我们如何去理解指针的类型呢?
图中 int *中的*代表着这个变量为指针变量,int代表pa中地址指向的对象的类型是整形(int)类型(就是上图中的int a = 10中a的类型是int,而pa中存放的地址是a的)。
我们按照上面的解释举个栗子:
假设我现在创建一个char a = ’a’,我要将其放入到pa这个指针变量中,那么pa的类型是啥?
(可以看看是不是和自己理解的一样)
1.2.3解应用操作符
我们将地址存起来,后面该如何使用呢?
我们只要拿到地址,就可以通过这个地址,知道这个地址指向的对象。
就像现实中我们可以通过门牌号(地址)来改变里面的物品和状态(地址指向的对象)
举个栗子:
如图*pa的意思为就是通过pa里存放的地址找到它指向的对象,以*pa = 20,这个指令是把a改成了20。
问:这里的*pa是什么作用呢?
答:*是解应用操作符(也叫间接访问操作符)*pa就是间接访问pa中地址指向对象的值;
哪有人又问了你这不是脱裤子放屁吗?要访问a就直接给a赋值不就完了吗?
我认为指针变量的作用之一是,当某种原因使a无法被赋值或者不方便直接赋值时可以使用这种方法来改变指针中的值。(也就是有多一种访问地址中的值的方法)
1.3指针变量的大小
前情回顾:前面的内容我们了解到, 32位机器假设有32跟地址总线,每根地址线传出的电信号转换成数字信号后是1或0,那我们把32根地址线产生的2进制序列当做一个地址,那么一个地址就是32个bit位,需要4个字节(1个字节==8个bit位)才能储存。
如果指针变量是用来储存地址的,那么指针变量的大小就得是地址的大小就是4字节的空间才可以。
同理64位机器,假设有64根地址线,一个地址就是64个二进制位组成的二进制序列,储存起来就需要8个字节空间,指针变的大小就是8个字节
指针变量的大小与类型无关,只要指针类型的变量是同一环境和平台的情况下大小都相等。
在不同环境中指针变量大小不同
在84位(也就是32位)环境下,指针变量大小为4。
在64位环境下,指针变量大小为8。
三.指针变量类型的意义
3.1指针解引用
举个栗子:
我们来观察下面几张图的变化
我们假设将4个字节放满。
当我们将其解引用为零时 4个字节都变为0
当我将指针变量p的类型改成char*时我们可以发现,我们要将其解引用为0时只改了一个字节。
所以得出了一个结论,指针变量的类型是有用的,它可以决定解引用是有多大的权限。(改变字节的大小)
问:那么为什么int的权限是4个字节,char的权限是1个字节呢?
答:和它自身类型的大小有关。
3.2指针+-整数
我们从另一个方面来体现不同类型的作用
举亿个栗子
char*类型+1地址向后跳了一个字节,int*类型+1地址向后4个字节
结论:我们可以看到不同类型的指针函数可以向前或向后的步距有多大,也可以从另一方面的了解不同类型的指针函数的权限有多大。
问:这有什么用呢?
答:我举个栗子
这是其作用之一,我们可以通过指针对数组进行遍历。
3.3void*指针
void*指针在指针类型中是一种特殊的指针,叫泛型指针(也可以理解为无具体类型指针),void指针可以接收任何类型的地址,但是它不能被解引用和指针+-运算。
举个栗子:
可以看到vs并没有报错,所以是可以存放大部分类型的。
问:void很鸡肋啊,啥都不能做。
答:它可以运用在函数参数中,用于接收不确定类型的地址,这样可以实现泛型编程。
四.const修饰指针
4.1const修饰变量
const修饰变量将const放在开头可以使变量拥有常量的属性,这时变量叫做常变量
可以看到当在创建变量时加入const时n在语法层面是不能被修改的,但是n是可以通过&地址的方式来修改值,所以n的本质还是变量
问:那么这个const有什么用呢,我如果想改还能通过取地址这种方式来更改
这就衍生出了下面的问题我们如何让它通过取地址的方式也改不了。
4.2const修饰指针变量
可以看到当const放入*的左边,就会限制指针变量中存放的地址指向的内容被修改,但是可以修改指针变量中的地址。
可以看到当const放入*的右边,就会限制指针变量中存放的地址,但是可以修改指针变量中存放地址指向的变量。
可以看到当const放入*的两边,就会限制指针变量中存放的地址和指针变量中存放地址指向的值。(这块可能会记混,我是这样记的:const放在*左边,右边就会变成*p那么也就是*p不能被修改
,如果将const放在*右边,那么后面就会变成p那么也就是p不能被修改)