指针简介
一个 指针变量A
指向了另一个 变量B
的 内存地址
。这个内存地址,往往是在内存中存储的 变量B的值的起始位置
。
可以理解为: 指针变量A
所在的内存地址,保存着 变量B
的内存地址。
如果对指针的使用,理解得还很朦胧。希望此文可以拨云见日。
Go语言数据类型
值类型: 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--
结论
- 切片为引用类型(指针类型)
- 不想改变切片容量时,传递切片使用
值传递
- 切片因长度增大而扩容(切片容量改变)时,切片指针(切片为指针类型)会改变
- 使用 append 函数可能改变切片容量,传递切片应使用
指针传递
encoding/json 包
package main
import (
"encoding/json"
"fmt"
)
type Man struct {
Age int
Name string
}
func main() {
man := Man{}
jjj := `{"Age":26, "Name":"Tom"}`
json.Unmarshal([]byte(jjj), &man)
fmt.Printf("Hello(%+v)\n", man)
mann, err := json.Marshal(man)
fmt.Printf("--jsonMan(%s)--err(%v)---\n", string(mann), err)
mannn, errr := json.Marshal(&man)
fmt.Printf("--jsonMan(%s)--err(%v)---\n", string(mannn), errr)
}
输出:
Hello({Age:26 Name:Tom})
--jsonMan({"Age":26,"Name":"Tom"})--err(<nil>)---
--jsonMan({"Age":26,"Name":"Tom"})--err(<nil>)---
解析json字符串
: 使用指针类型的变量Go变量转json
: 变量指针非指针均可
Go语言数据类型 https://blog.csdn.net/weixin_44211968/article/details/121221309