大家好,我是小风哥。假设给你一块很小的内存,这块内存只有8个字节,没有高级语言,没有操作系统,你操作的数据单位是单个字节,你怎么读写这块记忆力?注意这里的限制,再读一遍,没有高级语言,没有操作系统,在这样的限制下,你必须面对内存读写的本质。这个本质是什么?本质是你需要意识到内存是一个字节的小盒子,这些小盒子是从0到N编号的。这时候如果你要计算1+2,那么你必须先把1和2放进去分别是两个小盒子。假设我们使用Store命令将数字1放入第六个小方框,那么指令是这样表示的:store16注意这条指令。这里有两个数字:1和6,虽然都是数字,但是这两个数字的含义是不一样的。一个代表数值,一个代表内存地址。与写对应的是读,假设我们使用load指令,像这样:loadr16还有一个问题,这条指令是把数字6写入r1寄存器,还是把6号小方框里的数字写入r1寄存器?可以看出这里的数字是有歧义的。它既可以代表一个值,也可以代表一个地址。为了区分它,我们需要在数字上添加一个标识符。比如前面加了$符号就代表一个值,否则就是一个地址:store$16loadr16这样就不会产生歧义了。现在6号内存加载值1:即地址6代表数字1:地址6->数字1但是“地址6”对人类太不友好了,人类更喜欢代号,也就是命名,假设我们把“地址6”的名字改成a,a代表地址6,a中存储的值为1,人类用代数直观表示为:a=1,所以所谓的可变字就是出生。我们可以看到,变量a表面上等价于值1,但是其背后隐藏着一个重要的信息,即变量a所代表的数字1存放在第6个内存地址,即变量a或者符号a后面的意思是:表示数值1,数值存放在6号内存地址。到现在为止,第二个信息似乎不是很重要,先不管它吧。既然有变量a,就会有变量b。如果有这样一个表达式:b=a把a的值赋值给b,这个赋值在内存中应该怎么表示呢?很简单,我们同样为变量b找一个小盒子,假设变量b放在第二个小盒子上:可以看到,我们已经完整的复制了变量a的数据。有了变量,我们来升级,假设变量a不仅可以表示占用1个字节的数据,还可以表示占用任意大小内存的数据,像这样:现在变量a占用5个字节,占了一半以上整个内存空间。如果此时我们还想表达b=a怎么办?如果你还是用copy的方式,你会发现我们的内存空间不够了,因为整个内存大小只有8个字节。使用copy方式,只有这两个变量代表的数据会占用10个字节。怎么做?不要忘记变量a背后有两个含义。我们再看一下:表示值1,值存放在6号内存地址,我们重点看第二个意思。这个意思告诉我们什么?它告诉我们,无论一个变量占用多少内存空间,我们总能通过它在内存中的地址找到数据,而内存地址只是一个数字,与数据占用空间的大小无关.啊哈,现在终于可以使用变量的第二个含义了。如果我们想用变量b也引用变量a,为什么还要直接复制一份数据呢?直接用地址不好,像这样:变量a在内存中的地址是3,所以我们只能把3这个数存到变量b中。现在变量b开始变得非常有趣。首先,变量b并没有什么特别的,只是变量b里面存放的东西不能根据值来解释,而必须根据地址来解释。当一个变量不仅可以用来存储值,还可以用来存储内存地址时,指针就诞生了。有很多资料只说指针是地址,但是小风哥觉得这是偷懒的解释。只停留在汇编层面去理解是有失偏颇的。在高级语言中,指针首先是一个变量,但是这个变量保存的地址只是地址,而指针是对内存地址的更高层次的抽象。如果你只把指针理解为内存地址,那你肯定知道所谓的间接寻址。这是什么意思?用汇编语言加载变量a的值怎么办?loadr11想一想,这是不是一个问题,所以在这种情况下,指令会将值3加载到r1寄存器中,但是我们要将存储在内存地址1中的值解释为内存地址,此时必须为1time再次添加标记,如@:loadr1@1。这时候指令会先读取内存地址1存储的值,发现是3,然后再根据内存地址解释3。3指向的数据改为a:Address1->Address3->Dataa这就是所谓的indirectaddressing,Indirectaddressing,大家一定知道汇编语言中的这种间接寻址级别,因为有汇编语言中没有变量的概念。但是,高级语言就不一样了。这里是变量的概念。这时候地址1就代表了变量b,但是使用变量的一个好处就是很多时候我们只需要关心它的第一个意思,也就是说我们只需要关心变量b。地址3保存在中,我们不需要关心变量b存放在哪里,所以我们在使用变量b的时候不需要脑子里想着间接寻址。在程序员的大脑中,变量b直接指向数据a:b->数据a再对比一下:地址1->地址3->数据a#汇编语言级变量b->数据a#高级语言级这是为什么我说指针其实是对内存地址的更高层次的抽象,这个抽象的目的是为了屏蔽间接寻址。当变量不仅能存值还能存地址的时候,一个新的时代已经到来:看似松散的内存可以通过指针在内部组织起来,而这也使得程序直接处理复杂的数据结构成为可能,比如像下图:这就是所谓的链表。指针的概念最早出现在PL/I语言中。当时是为了增加链表的处理能力。不要以为链表这种数据结构很常见。可以参考这篇文章《彻底理解链表》。值得一提的是Multics操作系统是用PL/I语言实现的。这也是第一个用高级语言实现的操作系统。然而,Multics操作系统在商业上并不成功。KenThompson,DennisRitchie,参与项目的人后来决定自己写一个简单一点的。Unix和C语言诞生了。或许是在开发Multic的时候看到了PL/I语言中指针的强大。C语言中也有指针的概念。那么C语言中的指针是一个什么样的概念呢?为什么指针功能强大但具有破坏性?引用和指针有什么关系?
