很多主流语言中都有指针作为变量的一种,go也不例外。一方面,go保留了例如c、cpp等语言的严谨性,同时兼有python、函数式编程语言的灵活性;针对长历史的语言,去其糟粕取其精华,依然保留了指针。
值传递和引用传递
相信在初学c语言时,指针对于大部分人来说有些难以理解,尤其是不了解计算机底层的人,传值、传地址难以区分。c和cpp中,指针操作有传值和**传引用(地址)**两种方式。
- 传值
传值就是传递变量的值,相当于把这个变量复制一份,用于函数域中的操作。函数域之外,变量的值不受影响。
- 传引用
传引用,也叫传地址,在某个函数中操作以引用方式传进去的变量,会改变变量实际的值,也就是在函数域之外,变量的值也会被改变。
void con_value(int num){
num++;
}
void con_refer(int &a){
num++;
}
int main(){
int num = 1;
con_value(num);
printf("n1 = %d\n", num);
con_refer(num);
printf("n2 = %d\n", num);
}
con_value()
就是值传递,con_refer()
就是传引用。执行main()
函数后,n1 = 1
因为传入的只是值,相当于con_value()
把num
复制了一份用来操作,实际上函数的作用域之外num
没有变;n2 = 2
,con_refer()
则是传入的引用,直接使用了num
,所以无论函数内还是外,num
的值都发生的改变。
go中的指针
在go语言中,指针只有值传递这一种,也就是函数做的只是拷贝,然后再使用。
情况一:函数内改变不影响函数外
var a int
fun f(a int){
...
}
这种情况下,f()
中对a
的改变不会影响的外面的a
,因为是拷贝过去的。
情况二:通过指针改变变量实际的值(通过传递指针达到引用传递的效果)
var na int = 2
var pa *int = &na
func f(pa *int) {
*pa++
fmt.Println(*pa)
}
首先,na
是int
型变量,而pa
是指向na
地址的指针,我们通过传入一个指针来改变变量的值。同样以值传递的思想,函数f()
拷贝了一份pa
,但是无论怎么拷贝,拷贝出的东西它都是指向na
的地址的,所以通过传一个指针就可以改变对应地址的值,函数外na
的值也发生了改变。
情况三: 关于object
的传递
var object Object
func f(object Object){
···
}
如果是这种情况,就要看在定义object
时,是把他当作值传递来用还是引用传递来用。
// 1.这种情况可以安全的当作值传递来用
type Object struct {
a *int
}
func f(object Object){
...
}
func main(){
l := 9
o := Object{&l} // 不管怎么拷贝,拷贝出的 object.a 都是指向原本l的地址,会改变l的值
f(o)
}
// 2.如果对象需要例如维护某些状态,仅仅传值是不会改变函数外参数原本的值的,如:st
type Object struct {
a *int
st bool // 一个bool值
}
func fo(object Object) { // 传值,一个Object类型的值
*object.a ++
object.st = false // 原本的st不会被改变
}
func main() {
l := 9
o := Object{&l, true}
fo(o)
fmt.Printf("%d ", *o.a) // 10
fmt.Println(o.st) // true
}
// 3.如果需要,传指针来实现引用传递的效果,如下面的 st 被修改了
func p_fo(object *Object) { // 接收一个Object类型的指针
*object.a++
object.st = false // 拷贝后的 object 指向原来的地址,赋值操作实际上改变原值
}
func main() {
l := 9
o := Object{&l, true}
o_p := &o
p_fo(o_p)
fmt.Printf("%d ", *o.a) // 10
fmt.Println(o.st) // false
}
所以,在定义一个Object
时,要考虑以后是用值还是用引用,再来定义使其更方便的被使用。