您当前的位置:首页 > 计算机 > 编程开发 > Go语言

彻底搞懂Go语言中指针的使用

时间:02-28来源:作者:点击数:

指针简介

一个 指针变量A 指向了另一个 变量B 的 内存地址。这个内存地址,往往是在内存中存储的 变量B的值的起始位置 。

可以理解为: 指针变量A 所在的内存地址,保存着 变量B 的内存地址。

如果对指针的使用,理解得还很朦胧。希望此文可以拨云见日。

Go语言数据类型

值类型: int, float, bool, string, 数组, 结构体

引用类型(指针类型): 指针、切片、map、函数、channel

变量的值传递和引用传递

  • 变量 值传递 到函数内,函数内对变量的改动,无法改变 函数外原有变量的值.
  • 变量 引用传递 到函数内,函数内对变量的改动,可以改变 函数外原有变量的值.

对于指针变量,只有改变指针所引用的地址,才算是改变了指针的值。

因此,指针类型 的变量,进行 值传递 时,指针所指向的变量的值 是允许更改的。

因为 指针类型 的变量,保存着另一个 变量的地址。改变另一个 变量的值,该地址并未发生改变。

Go指针原理示意图

如上图所示: 变量b 指向 变量a的地址,即 &a。而 &b 则指向 变量b的地址&b 为 指针的指针

代码语句: b := &a, 让系统为 指针变量b ,开辟了一块独立的内存空间来保存它。

下面以 结构体指针 和 切片指针 为例,分别展开论述。如果懒得看代码,可直接看最下面的结论。

结构体指针

package main

import "fmt"

type Car struct {
	Brand string
	Attr  map[string]interface{}
}

func main() {
	c := Car{Brand: "BYD"}
	UpdateWithValue(c)
	fmt.Printf("---Car(%+v)---c.Attr(%p)---UpdateWithValue--\n", c, c.Attr)
	UpdateWithPointer(&c)
	fmt.Printf("---Car(%+v)---c.Attr(%p)--UpdateWithPointer--\n", c, c.Attr)
	ChangeMap(c)
	fmt.Printf("---Car(%+v)---c.Attr(%p)--ChangeMap--\n", c, c.Attr)
}

func UpdateWithValue(c Car) {
	c.Attr = map[string]interface{}{"brand": "BENZ", "change": "UpdateWithValue"}
	c.Brand = "BENZ"
	c.Attr["BENZ111"] = "B111"
}

func UpdateWithPointer(c *Car) {
	// 任意更改有效
	c.Attr = map[string]interface{}{"brand": "AUDI", "change": "UpdateWithPointer"}
	c.Brand = "AUDI"
	c.Attr["ext"] = "extAUDI"
}

func ChangeMap(c Car) {
	// 引用类型的属性,不改变引用指针,改变指针对应的值,更改有效。
	// 更改字典键值对,未改变指针。更改有效
	c.Attr["brand"] = "BUICK"
	c.Attr["change"] = "ChangeMap"

	// 字典重新赋值,改变了指针。故赋值无效。
	c.Attr = map[string]interface{}{"brand": "BUICK", "change": "ChangeMap"}
	// 重新赋值后,已更改了指针。后续再更改字典键值对,更改同样无效。
	c.Attr["change"] = "ChangeMapChange"

	// 字符串等值传递的属性,更改无效
	c.Brand = "BUICK"
}

结构体值传递时,指针属性不可重新赋值,但可更改键值对。

Go练兵场在线演示

输出:

---Car({Brand:BYD Attr:map[]})---c.Attr(0x0)---UpdateWithValue--
---Car({Brand:AUDI Attr:map[brand:AUDI change:UpdateWithPointer ext:extAUDI]})---c.Attr(0xc0000a01b0)--UpdateWithPointer--
---Car({Brand:AUDI Attr:map[brand:BUICK change:ChangeMap ext:extAUDI]})---c.Attr(0xc0000a01b0)--ChangeMap--
结论
  • 结构体指针传递时,结构体各属性的值可以任意更改
  • 结构体值传递时,值类型的属性(字符串,数字等),不可更改
  • 结构体值传递时,引用类型的属性(指针,字典等),不可更改原有引用(不可重新赋值),但可修改引用指针所对应的值(修改原有字典的键值对)

切片指针

package main

import "fmt"

func main() {
	var val []int
	for i := 0; i < 3; i++ {
		UpdateWithValue(val, i)
		fmt.Printf("--val(%+v)---point(%p)--cap(%d)--UpdateWithValue--\n", val, val, cap(val))
		UpdateWithPointer(&val, i)
		fmt.Printf("--val(%+v)---point(%p)--cap(%d)--UpdateWithPointer--\n", val, val, cap(val))
	}

	fmt.Println("------------使用make函数指定切片长度------------------------")

	vv := make([]int, 3)
	for j := 0; j < 3; j++ {
		UpdateWithValue(vv, j)
		fmt.Printf("--val(%+v)---point(%p)--cap(%d)--UpdateWithValue--\n", vv, vv, cap(vv))
		UpdateWithPointer(&vv, j)
		fmt.Printf("--val(%+v)---point(%p)--cap(%d)--UpdateWithPointer--\n", vv, vv, cap(vv))
	}

}

func UpdateWithValue(val []int, add int) {
	if len(val) > 1 {
		val[0] = 33
	}
	val = append(val, add)
}

func UpdateWithPointer(val *[]int, add int) {
	if len(*val) > 1 {
		(*val)[0] = 333
	}
	*val = append(*val, add)

}

Go练兵场在线演示

输出:

--val([])---point(0x0)--cap(0)--UpdateWithValue--
--val([0])---point(0xc0000b8010)--cap(1)--UpdateWithPointer--
--val([0])---point(0xc0000b8010)--cap(1)--UpdateWithValue--
--val([0 1])---point(0xc0000b8040)--cap(2)--UpdateWithPointer--
--val([33 1])---point(0xc0000b8040)--cap(2)--UpdateWithValue--
--val([333 1 2])---point(0xc0000be020)--cap(4)--UpdateWithPointer--
------------使用make函数指定切片长度------------------------
--val([33 0 0])---point(0xc0000ba018)--cap(3)--UpdateWithValue--
--val([333 0 0 0])---point(0xc0000bc060)--cap(6)--UpdateWithPointer--
--val([33 0 0 0])---point(0xc0000bc060)--cap(6)--UpdateWithValue--
--val([333 0 0 0 1])---point(0xc0000bc060)--cap(6)--UpdateWithPointer--
--val([33 0 0 0 1])---point(0xc0000bc060)--cap(6)--UpdateWithValue--
--val([333 0 0 0 1 2])---point(0xc0000bc060)--cap(6)--UpdateWithPointer--
结论
  • 切片为引用类型(指针类型)
  • 不想改变切片容量时,传递切片使用 值传递
  • 切片因长度增大而扩容(切片容量改变)时,切片指针(切片为指针类型)会改变
  • 使用 append 函数可能改变切片容量,传递切片应使用 指针传递
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门