一.内存和地址
(1)内存
对于内存的理解,我们先举个生活中的例子——在一个大房子力找人;
就像将小白放在一个有100间没有房号的房间的房子里头,让小黑去寻找小白,最坏的情况,小黑要找100次才可以找到小白,这样的寻找效率非常低,如果房间有了门牌号,小黑便可以根据门牌号找到所对应门牌号的房间,找到小白。
⽣活中,每个房间有了房间号,就能提⾼效率,能快速的找到房间。
其实生后如此,计算机也是如此。
在计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的 数据也会放回内存中,那我们买电脑的时候,电脑上内存为8GB/16GB/32GB等,那这些内存空间如何⾼效的管理呢?
其实也是把内存划分为⼀个个的内存单元,每个内存单元的大小为一个字节(Byte)
补充:(计算机中常见的单位)
![](https://i-blog.csdnimg.cn/blog_migrate/59cfbf52fc469977f982620d4a28863e.png)
(2)编址的理解
二.指针变量和地址
(1)怎么取地址
通过内存的知识,我们了解了其实地址就是指针 ,那么我们该如何取到地址呢?我们需要用到取地址操作符(&),下面,通过代码来看看怎么用:
(x86)环境下
那如果我们想把a的地址存起来,那么,我们就要为a的地址开辟一个空间p(指针变量),
所以,接下来,我们来说说到底什么才是指针变量。
(2)指针变量
对于指针变量的理解,我们可以类比整型变量,浮点型变量.......
在指针(地址)中,有整型的地址,有字符的地址,等等,所以说,应该有整型对应的整型指针,字符对应的字符指针........
我们看看下图:
![](https://i-blog.csdnimg.cn/blog_migrate/f79251dccb1cb36084e97ee05ba385f4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6cfc2fece3a6671ed5836f14df298c22.png)
其实:
“*”是一个解引用操作符(也可以称:间接访问操作符),因为创建一个变量就需要开辟一个空间,因此,指针变量也需要开辟一个空间来存放地址,我们可以通过使用解引用操作符来通过地址来找到该地址所存放的变量
一字节=8比特;
注意:指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的
三.指针解引用的注意点
(1)指针类型带来的注意点
这也就是为什么在相同的环境下(X86/X64),指针变量都占(4字节/8字节)还要区分他的类型(int*,char*,float*.....)
我们可以看看对*(解引用)的赋值后,发生的变化:
前:
后:
那若我们将pa的类型换成char*呢:
(记得与上图进行对比)
前:
char*pa可以发下a,是因为指针本来就是用来存放地址的;
与解引用不要搞混哦
后:
发现效果的差异了吧
当pa为int*类型时,*pa=0;改变的时四个字节:(因为他为int,访问的便是int(4字节))
当pa为char*类型时,*pa=0;改变的时1个字节: (因为他为char,访问的便是char(1字节))
结论:
指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作,访问⼏个字节)
-----此概念就可以用于下面要说明的指针+/-整数了;
所以说,我们若是要该变前2个字节,那么就将pa返回为short*类型;
(2)void*指针(泛型指针)(无具体类型)
void*可以接收任意类型的指针,可以到达泛型编程的效果。不过,void*是有局限性的 ,正因为void*是无具体类型的指针,对其指针变量进行*(解引用操作)或+/-整数是无法知道要访问多少字节的,所以:
void*类型的指针变量不能用于解引用操作与指针+-整数操作的;
(3)const修饰
const(常属性)可以对变量进行修饰 ,使所修饰变量不可被修改(被赋值)但本质还是变量,只是变成了常变量(可以用数组这个案例来说明—还是变量(切记在C中实现)):
a被const修饰,不能被改变了,既然是a被const修饰,那么,我们是不是可以不通过a,而是通过访问a的地址来进行解引用操作来改变a的值呢?那我们将const应用到指针:
看,我们通过指针该变了a的值,可是,我们既然要保护a不被改变,那们,我们做事就该做绝:
直接修饰指针:
引入理解:
1.p是指针变量,里面存放了其他变量的地址;2.*p是指针指向的对象a;
3.指针变量也是变量,所以p也有自己的地址,&p(二级指针);
const修饰指针有3种形式(对int*p来说明)
1. const放在*p的左边
*p不可以被改变(赋值),但p可以被改变(指向另一个变量的地址):
2.const放在*后面
*p可以被改变(赋值),但p不可以被改变(指向另一个变量的地址):
3.const一个放在*前,一个放在*后
*p不可以被改变(赋值),p也不可以被改变(指向另一个变量的地址):
四.指针的运算
补充:
1.数组在内存当中是连续存放的,随着数组下标的增长,地址是由低到高变化的;
2.在数组arr中,arr代表的是数组首元素的地址,也就是指针,但有两个例外;
例外1:&arr表示的是取出整个数组的地址;
例外2:
3.arr[i]等价于*(p+i);
4.&与*结合可视为抵消;
(1)指针+/-整数
我们还是通过代码的演示来理解
p1与p2的地址是相同的(&a),不过p1+1跳过的是4个字节,p2+1跳过的为1个字节
{与前面的指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)进行联想}:并与下图进行理解:
结论:
指针类型决定了指针的步长,就是向前/向后走i步的多大距离
type*p:
p+i就是跳过了i * sizeof(type)个字节
EG:int*p:
p+2:就是跳过了2*4=8个字节;
(这对下一篇my CSDN的“指针的应用“个别题目有很大的理解帮助)
(2)指针-指针
指针1+5=指针2;
指针2-指针1=5;
所以:
指针-指针得到的是两个指针之间元素的个数,不是字节的个数(是有正负区分的(高地址-低地址)/(低地址-高地址));
也说明了指针+指针无意义;
而且,指针-指针运算的前提是两个指针指向的是同一块空间,而不仅仅是要同类型(比如说是在同一个数组里);
(3)指针的大小比较
对于指针大小的比较,也就是比较在内存里,地址大小的比较。
五.野指针的出现与防止
(1)野指针的出现
![](https://i-blog.csdnimg.cn/blog_migrate/00e3e256e6f8fef3ac5690a242e0fa2e.png)
(2)野指针的防止
1.运用空指针NULL;
assert(p!=NULL);