一个 指针变量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--
-