指针用来指向数据对应的内存地址。Python没有指针的概念,也就不存在操作指针的事情。
先明确两个概念:
- 指针传递:只是将内存地址提供给调用方进行操作,任何的数据改动都会影响到内存地址对应的原值
- 值传递:将内存地址对应的原值拷贝一份给调用方,任何数据改动都不会影响到原值。
Python中的深拷贝和浅拷贝底层也是这么区分的。
来看一个值传递的例子,给函数传参。
package main
import "fmt"
func div(s int) {
s = s - 1
fmt.Println("函数div()内的s值: ", s)
}
func main() {
s := 5
// 变量s把自己的值复制了一份传给了div()函数
div(s)
fmt.Println("函数main()内的s值: ", s)
}
% go run main.go
函数div()内的s值: 4 #变量s的副本被减去了1
函数main()内的s值: 5 #变量s的原值不受影响
在Go中假设我们要操作int变量s的指针,会使用到2个符号
获取变量s的指针地址:
s_ptr := &s
获取指针地址对应的值:
s_ptr_va; := *s_ptr
修改一下代码,传入div()函数的不在是变量值,而是传入变量对应的指针地址
package main
import (
"fmt"
)
func div(ptr *int) {
fmt.Println("传入函数div()内的ptr指针地址: ", ptr)
fmt.Println("操作前,函数div()内的ptr指针地址对应的值: ", *ptr)
//通过星号* 获取指针地址的值,进行运算操作并赋值
*ptr = *ptr - 1
fmt.Println("操作后,函数div()内的ptr指针地址对应的对应的值: ", *ptr)
fmt.Println("---------函数div()执行结束----------")
}
func main() {
s := 5
// 获取指针地址
s_ptr := &s
fmt.Println("变量s的内存地址: ", s_ptr)
fmt.Println("---------函数div()执行前----------")
div(s_ptr)
fmt.Println("变量s在函数main()中最终的值: ", s)
}
% go run main.go
变量s的内存地址: 0x1400001a090
---------函数div()执行前----------
传入函数div()内的ptr指针地址: 0x1400001a090
操作前,函数div()内的ptr指针地址对应的值: 5
操作后,函数div()内的ptr指针地址对应的对应的值: 4 # 在函数中操作的最终结果被写入指针地址对应的值
---------函数div()执行结束----------
变量s在函数main()中最终的值: 4 #函数外部变量最终的值也同步改变
在Go语言中下面五个默认就是指针传递,即不需要特殊声明&获取指针地址,*获取地址对应的值之类的操作。直接对它们内容进行变更,全局的引用就会生效。
- slice
- map
- channel
- interface
- func()
其他类型的数据则默认就是值传递,包括上面我们演示的普通变量s,以及下面要演示的结构体。如果结构体的内容只是姓名、住址之类不怎么改动的信息也就算了。只要结构体不是太大,值传递复制的副本也不会太占用内存。但是遇到内部属性的值经常需要变更的场景,就需要使用结构体指针来保持全局数据的一致了。例如银行的ATM机操作场景。
package main
import (
"fmt"
)
type Account struct {
// 账号
ID int
// 账号余额
Balance float32
}
type ATM interface {
// 余额查询
show() float32
// 消费支出
send(num float32) (string, bool)
}
// 由于账号结构体中包含了账号余额字段,为了保障全局数据一致,就不能接受方法操作的是结构体的拷贝副本了。
func (account *Account) show() {
fmt.Println("当前余额: ", account.Balance)
}
// 接收器接收的是结构体实例指针的值,方法内部操作的也就不再是结构体的副本了
func (account *Account) send(num float32) (string, bool) {
var ret string
if num > account.Balance {
ret = "余额不足"
return ret, false
} else {
ret = "支付成功"
account.Balance = account.Balance - num
return ret, true
}
}
func main() {
// 用内置方法new()创建结构体,返回值为指针地址
account := new(Account)
account.ID = 123456
account.Balance = 200.5
// 看看当前余额
account.show()
// 先消费一把
msg, _ := account.send(105.85)
fmt.Println(msg)
account.show()
fmt.Println("直接查看结构体的balance属性")
fmt.Println(account.Balance)
}
% go run main.go
当前余额: 200.5
支付成功
当前余额: 94.65
直接查看结构体的balance属性
94.65
总结:指针操作就是为了避免数据在内存中反复拷贝,影响效率占用资源。对于结构体而言,没有特殊需求的话只直接使用结构体指针吧,省的数据不一致。