3 1 Point

3.1 指针讨论 #

本节源码位置 https://github.com/golang-minibear2333/golang/blob/master/3.grammar-advancement/3.1-point

3.1.1 指针 #

c 中有指针的概念,在 go 中也有,但是实际上用的比较少,因为指针容易出错,而且不易阅读。

每个变量都有他的地址

var a int
fmt.Printf("a 的地址是:%p \n", &a)

输出

a 的地址是:0xc0000b2008

指针用来存地址

//声明 变量名 + 指针类型 , 命令规则以ptr结尾
var ptr *int /* 指向整型*/
// var fp *float32 /* 指向浮点型 */
ptr = &a // 变量内部存的值是普通类型,指针内部存的值是地址
fmt.Printf("ptr 存的值是:%p \n", ptr)

输出,可以看到 ptr 存的值就是 a 的地址。

ptr 存的值是:0xc0000b2008

存的就是 a 的地址,ptr 的指向*ptr 肯定就是 a 本身了。

if a == *ptr {
    fmt.Println("a == *ptr")
}

输出

a == *ptr

3.1.2 指针的作用 #

指针可以消灭掉返回值,直接对参数做改变。

定义一个交换函数,形参为指针类型

func swap(x *int, y *int) {
	var temp int
	temp = *x /* 保存 x 地址的值 */
	*x = *y   /* 将 y 赋值给 x */
	*y = temp /* 将 temp 赋值给 y */
}

调用

a := 100
b := 200

//操作地址,不需要返回
swap(&a, &b)
fmt.Printf("交换后 a 的值 : %d\n", a)
fmt.Printf("交换后 b 的值 : %d\n", b)

输出

交换后 a 的值 : 200
交换后 b 的值 : 100

虽然可以这么做,但是不推荐,因为 goc++ 多出来多返回值的特性,所以这里写在返回里可读性更强。

PS1: 但如果你的参数是比较复杂的类型,比如数组。用指针可以节省空间。

PS2: 对引用类型的操作会改变原引用类型的值,这里与指针有异曲同工之妙。

3.1.3 多维指针 #

刚刚用到的指针,只不过指向一个变量的地址,他就被叫做一维指针。

var ptr *int
ptr = &a

指针本身也是一个变量,是变量就有地址,所以指针也可以被取地址。

var ptr *int
pptr = &ptr

*int 类型的指针存的是 int 类型数据的地址,得到 *变量类型 就是他的指针,推导出指向 *int 变量的指针为 **int 类型,这种类型被称为二维指针,每多一个 * 就多一个维。

var a int
var ptr *int  //一维
var pptr **int // 二维
var ppptr ***int // 三维

ptr = &a
pptr = &ptr
ppptr = &pptr

fmt.Printf("a的地址:%p \n", &a)
fmt.Printf("ptr存的地址:%p \n", ptr)
fmt.Printf("pptr存的地址的指向:%p \n", *pptr)
fmt.Printf("ppptr存的地址的指向的指向:%p \n", **ppptr)

输出

a的地址:0xc000014090
ptr存的地址:0xc000014090
pptr存的地址的指向:0xc000014090
ppptr存的地址的指向的指向:0xc000014090

PS1: 日常工作中,不建议使用多维指针,可读性不好,容易犯错误,一层指针能搞定的,一定不要使用多维炫技术。不然过几个月你自己都看不懂。

PS2: 不得不使用二维指针的场景:你希望在一个函数的参数中改变一个指针的值,你就只能传这个指针的指针给这个函数。

PS3:多维指针的唯一好处:减少传参

你在工作中啥时候用到了指针/多维指针?

3.1.4 小结 #

Java中没有指针的概念,但是有引用的概念,在C++中比较常见,我们操作内存一定会用到指针,存储了变量的地址。

为了程序的可读性,一般只会用到一维指针,掌握指针的概念,后面还有大用。



本图书由小熊©2021 版权所有,所有文章采用知识署名-非商业性使用-禁止演绎 4.0 国际进行许可。