Go语言之理解指针
1. 说一下内存
我们在编程的时候,实际上就是在操作内存,除非是进行IO操作写磁盘。其余的不管你是一半的变量还是Hibernate的Entity,都是在内存中闪转腾挪。
我上学的时候,C语言课程是第一门编程语言课程,其中最难的部分就是指针,而指针就是直接操作内存的,所谓的C语言是最接近底层的语言,其中很重要的原因就是以为C语言让程序员可以直接去动内存。
其实在很多年前,人们编程的时候绝对不像想在这么幸福,总是要直接操作内存的,而更久远一点的程序员们,要用汇编语言直接写指令,再久一点的程序员,就要在纸带上打孔,用01010这种二进制编码编程了。
我说了这么多,其实想说的是,现在的很多编程语言比如Java,其实是对程序员隐藏了其内存操作的细节的。
我们都知道Java有堆内存和栈内存,堆内存里是实际的对象,栈内存中的变量指向了对象,这里的指向,其实就是指针了。那么指向的是什么?有没有人曾经思考过这个问题,在内存中,如何快速的寻找一个值?
答案自然是地址,只有用地址访问是最快的。
内存结构如上图所示,如果有一种低级语言,也许是这样的:
//我设计了一种运算符,[]内表示内存地址
var [1] = 101
这样就将内存地址1的块设置成了101。
现在时代进步了,我们发明了变量这个概念,其实就是给内存地址起了名字:
var a = 101
此时变量a就是地址1的别名了,可以这么理解。
2. 现在谈谈指针
那么指针是什么?指针的值是一个变量的地址,一个指针只是值所保存的位置。
下面写一段正确的Go代码,这段代码来自《The Go Programming Language》:
x := 1
p := &x
//打印1
fmt.Println(*p)
*p = 2
//打印2
fmt.Println(x)
这个时候画一个内存模型,应该是这样的:
指针1&是取地址运算符,根据变量x,取到了相应的地址,此时如果打印p,得到的是一段类似这样的字符串:“0xc000016050”。
接下来利用*p=2
这个语句将指针指向的值改成2,画成图是这样的:
看看,利用指针我根本不需要去知道变量叫什么,只要改指针就可以了。
下面再来一段代码,来源还是一样的:
package main
import "fmt"
func incr(p *int) int {
*p++
return *p
}
func main() {
v := 1
incr(&v)
fmt.Println(incr(&v))
}
这段代码比较迷惑的地方在于*p++
,程序最终的返回结果是3,注意,这一句只是将指针p所指向的值进行了递增操作,但是并没有变更p本身,因此画出图来是这样的:
3. 搞点事情
如指针那张图,其实p也是内存中的一个地址,那么一定可以有一个指针指向它,这没毛病:
v := 1
p := &v
q := &p
此时的q就是一个指向指针的指针了。q得到的是p的地址,*q得到的就是p指向的值了。
指向指针的指针在C语言里常常会用到,我现在初学Go,也不知道会不会频繁使用,反正有个印象就好了。