我试图了解指针在Go中是如何工作的。在一般情况下,我对指针的经验很少,因为我主要使用javascript。
我写了这个虚拟程序:
func swap(a, b *int) {
fmt.Println("3", &a, &b)
*a, *b = *b, *a
fmt.Println("4", a, b)
}
func main() {
x := 15
y := 2
fmt.Println("1", x, y)
fmt.Println("2", &x, &y)
swap(&x, &y)
fmt.Println("5", x, y)
}
打印出以下结果:
$ go run test.go
1 15 2
2 0x208178170 0x208178178
3 0x2081ac020 0x2081ac028
4 0x208178178 0x208178170
5 2 15
我有几个问题:
据我所知,&x
提供了存储x
的地址。要获得x的实际值,我需要使用*x
。然后,我不明白为什么&x
属于*int
类型。由于*x
和&x
都属于*int
类型,我们之间的差异根本不明确。
在我的交换功能中,使用*a, *b = *b, *a
和使用a, b = b, a
有什么区别?两者都有效,但我无法解释原因......
为什么在步骤2和3之间打印时地址会有所不同?
为什么我不能直接修改地址直接将&b
分配给&a
?
非常感谢你的帮助
答案 0 :(得分:4)
1)对于使您认为*x
和&x
属于同一类型的语言存在混淆。正如Not_a_Golfer指出的那样,*
在表达式和类型名称中的使用是不同的。 *x
中的main()
语法无效,因为表达式中的*
试图获取后面指针指向的值,但x
不是指针(它是int
)。
我认为您正在考虑这样一个事实:当您使用x
指向&x
时,您添加到类型名称的字符为*
以形成*int
}。我可以看到&var
让你*typ
而非&typ
让你感到困惑。另一方面,如果他们将&
放在类型名称上,那么在其他情况下会令人困惑。一些棘手是不可避免的,并且像人类语言一样,通过使用比单独讨论更容易学习。
2)再次,结果表明假设是不准确的:a, b = b, a
交换swap
函数所看到的指针,但不会交换main
视角的值和输出的最后一行更改为5 15 2
:http://play.golang.org/p/rCXDgkZ9kG
3)swap
打印指针变量的地址,而不是底层整数的地址。您可以打印a
和b
来查看整数的地址。
4)我将假设,也许是错误的,你希望你能用&
语法交换任意变量指向的位置,如&x, &y = &y, &x
,而不用声明指针变量。有一些含糊之处,如果这不是你想要的,我不确定这部分答案是否会对你有帮助。
与许多“为什么我不能......”这样的问题一样容易出问题是“因为他们用这种方式定义了语言”。但是为了为什么这样一点点,我认为你需要在某个时候(或another type implemented using pointers, like maps or slices)将变量声明为指针,因为指针带有诱杀陷阱:你例如,可以在一段代码中执行某些操作,这些代码以意外的方式更改其他代码的局部变量。因此,无论您在哪里看到*int
,它都会告诉您,您可能不得不担心(或者,您可以使用)nil
指针之类的内容,来自多个代码段的并发访问等。
Go比其他语言更明确地使用指针式显示器:例如,C ++具有“参考参数”(int& i
)的概念,您可以在其中执行swap(x,y)
没有&
或main
中出现的指针。换句话说,在具有引用参数的语言中,您可能必须查看函数的声明以了解它是否将更改其参数。围棋人采用这种行为有点太令人惊讶/暗示/棘手。
没有解决所有的引用和解除引用需要一些思考,你可能只需要使用它一段时间来获得它;希望这一切都有所帮助。
答案 1 :(得分:1)
- 根据我的理解,
醇>&x
提供了存储x
的地址。要获得x的实际值,我需要使用*x
。然后,我不明白为什么&x
属于*int
类型。由于*x
和&x
都属于*int
类型,我们之间的差异根本不明确。
当*int
在函数声明中时,它表示int的指针。但是在声明中,*x
不是*int
类型。它意味着指针的解除引用。所以:
*a, *b = *b, *a
这意味着,通过取消引用交换值。 功能参数通过copyed传递。因此,如果要将值导出到调用者,则需要将变量的指针作为参数传递。
- 在我的交换功能中,使用
醇>*a, *b = *b, *a
和使用a, b = b, a
有什么区别?两者都有效,但我无法解释原因......
正如我所说,a, b = b, a
意味着交换指针而不是交换值。
- 为什么在步骤2和3之间打印时地址会有所不同?
醇>
这不是定义的结果。变量的地址由go运行时处理。
- 为什么我不能直接修改地址直接将
醇>&b
分配给&a
?
并非不可能。例如:
package main
import (
"unsafe"
)
func main() {
arr := []int{2, 3}
pi := &arr[0] // take address of first element of arr
println(*pi) // print 2
// Add 4bytes to the pointer
pi = (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(pi)) + unsafe.Sizeof(int(0))))
println(*pi) // print 3
}
使用不安全的软件包,您可以更新地址值。但它没有得到认可。