在开启这个章节之前,我看过很多书上都是先讲数组,后续又说数组名就是是地址,如果没有前期知识的铺垫,大多数都会想,地址是个啥玩意?到底什么是数组?什么是指针?他们之间又有什么样的关系?本章节我们将深入学习。
1、指针是什么?
在看这个问题之前,我们先引入一段简单的代码做铺垫:
int main() { int a = 10; //定义a变量开辟了一块空间并且初始化 a = 20; //使用的是a的空间:左值 int b = a; //使用的是a的内容:右值,此时a等价于20 return 0; }
同过上面代码及注释,我们可以看出,同样一个 a 变量,在不同的应用场景中,a 的本身含义是不同的!
那么这个代码跟指针又有什么关系呢?
指针就是地址!就好比你 &a,取出变量 a 的地址,而这个就可以称作是指针!地址的本质上是数据!数据是可以被保存在变量空间里面的!
这里可能有人会说,你在胡说,那我之前就知道指针是存放地址的,你现在又告诉我指针就是地址?这直接把我搞糊涂了。这里你就不够严谨了!我们口头上说的指针,指的是指针变量!什么是指针变量?就是保存指针的变量,也可以说是保存地址的变量,这个变量就叫做指针变量!
从严格意义上,指针和指针变量是不同的,指针就是地址,而指针变量是C语言中的变量!要在特定区域开辟空间,用来保存地址数据,也可以被取出地址。
但是我们在口语化表达的时候,经常将这两个概念混合,是以前的书不够严谨?还是教学的人学的不够通透,或者是翻译外国资料的时候没翻译清楚?具体原因无从考证,在以后,我们分开理解,和别人讨论时,要明确概念,指针就是指针,指针变量就是指针变量!
有了上面的认识,我们再来看一段代码:
int main() { int a = 10; int* p = &a; //取出变量a的地址放到指针变量p中 p = (int*)0x11223344; //使用p变量的空间,左值 int* q = p; //使用p变量的内容:右值,此时的p等价于0x11223344 return 0; }
我们再次总结一下:指针就是地址!指针变量本质是变量,里面保存的是地址(指针)值!
2、为什么要有指针?
这里我们举个例子来说明,张三在寝室打游戏,到了中午肚子饿了,于是拿起手机点了份外卖,外卖小哥送到寝室楼下,一头雾水(假设没有门牌号的情况下),打电话问张三:“你在哪里啊?”,张三:“我在宿舍里啊”,“你在哪个宿舍啊?”,“我不知道该怎么表达,你一间间找吧”,外卖小哥很无奈,只能一间间的找,好不容易找到对应的房间,菜都凉了,外卖小哥成功获得了一个差评。
通过上述案例,证明了门牌号的重要性,显然而知没有门牌号是多么麻烦的一件事,门牌号归根到底就是为了提高查找效率!
那么我们类比到计算机中,CPU 在内存中寻址的基本单位是字节,在32位机器下,最多能识别 4G 的物理内存,既然 CPU 寻址是按照字节,但是内存又很大,所以可以看作众多字节的集合:
其实我们门牌号就可以等价于每个字节空间对应的地址,也就是该空间对应的指针
存在指针的意义就是为了提高CPU寻址效率。
那么如何理解编址呢?
我们得了解一个概念,计算机内部是有很多硬件单元的,而硬件单元是需要互相协同工作,最基本的他们之间要互相进行信息传递,而我们一个个硬件之间是相互独立的,总得想个办法把他们相关联起来,就举例CPU和内存之间要传递大量的数据,那么他们之间就会用“线”给连起来,今天我们只关心地址总线。
在CPU需要访问内存的某一个空间的时候,必须知道这个字节空间在内存的什么位置上,因为内存中字节很多,所以就如我们上面所说引入了地址,也就是给内存进行编址,在计算机中的编址,并不是把每个字节的地址信息保存下来,而是通过各硬件厂商出厂前就设计完成了。
硬件编址也是如此,32 位机器有 32 根地址总线,每根线可以表示两种形态 0,1【高电压低电压】,也就是说 1 根线能表示 2 种含义,2 根线表示 4 种含义,以此类推,32根线能表示 2^32 种含义,而每种含义则代表着一个地址,在地址信息被下达给内存,在内存的内部就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。(点到即可)
指针的大小?
有了上面的认识就理解指针的大小就很简单了,如果是 32 位机器我们要存一个地址数据是不是应该用 4 个字节,那如果是 64 位的环境就应该用 8 个字节大小空间来存放一个地址,那么我们的结论正不正确呢?
结论:指针变量是用来存地址的,地址的大小表示是根据操作环境变化的,所以要想放得下地址的数据大小,指针的变量大小也会随着系统环境而变化。
3、指针的内存布局
在很多教材上都会说某某指针指向了某某变量,是真的指向吗?既然指针变量是用来存地址的,那一个 int 占四个字节,他存的又是哪个字节的地址呢?下面我们来一一讨论。
先看一段代码:
int main() { int a = 10; int* p = &a; return 0; }
这里我们先来分析一下,创建了一个整型 a 变量,同时初始化为 10,接着我们创建了一个整型指针变量 p ,并取出变量 a 的地址赋值给指针变量 p,那么他们的内存布局是这样的:
所以我们常说的指针变量 p 指向变量 a 实质上是指针变量 p 中存放着 a 变量的起始地址,也是最小的地址。也能明白,取地址本质是取出变量的起始地址。如果有的老师画图把指向图画在变量中间的话,那么他显然画图画的很不严谨!