一个 指针变量A 指向了另一个 变量B 的 内存地址。这个内存地址,往往是在内存中存储的 变量B的值的起始位置 。
可以理解为: 指针变量A 所在的内存地址,保存着 变量B 的内存地址。
如果对指针的使用,理解得还很朦胧。希望此文可以拨云见日。
值类型: int, float, bool, string, 数组, 结构体
引用类型(指针类型): 指针、切片、map、函数、channel
对于指针变量,只有改变指针所引用的地址,才算是改变了指针的值。
因此,指针类型 的变量,进行 值传递 时,指针所指向的变量的值 是允许更改的。
因为 指针类型 的变量,保存着另一个 变量的地址。改变另一个 变量的值,该地址并未发生改变。
如上图所示: 变量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"
}
结构体值传递时,指针属性不可重新赋值,但可更改键值对。
输出:
---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)
}
输出:
--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--