理解 Go 中的指针
Understanding pointers in Go
我想了解指针在 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
?
非常感谢您的帮助
- From what I understand
&x
gives the address at which x
is stored. To get the actual value of x, I need to use *x
. Then, I don't understand why &x
is of type *int
. As *x
and &x
are both of type *int
, their differences are not clear to me at all.
当*int
在函数声明中时,表示int指针。但在声明中,*x
不是类型 *int
。这意味着取消引用指针。所以:
*a, *b = *b, *a
这意味着,通过取消引用交换值。
函数参数通过复制传递。所以如果你想将值导出给调用者,你需要传递一个变量的指针作为参数。
- In my swap function, what is the difference between using
*a, *b = *b, *a
and using a, b = b, a
? Both work but I can't explain why...
正如我所说,a, b = b, a
意味着交换指针而不是交换值。
- Why are the addresses different when printed between step 2 and 3?
这不是定义的结果。变量的地址由 go 的运行时处理。
- Why can't I just modify the address directly assigning
&b
to &a
for example?
并非不可能。例如:
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
}
使用不安全包,您可以更新地址值。但是不认可。
1) 语言混淆导致您认为 *x
和 &x
是同一类型。正如 Not_a_Golfer 所指出的,*
在表达式和类型名称中的用法是不同的。 main()
中的 *x
是无效语法,因为表达式中的 *
试图获取后面的指针指向的值,但 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
中一样,而无需声明指针变量。有一些歧义,如果这不是你想要的,我不确定这部分答案是否对你有帮助。
与许多 "why can't I..." 问题一样,简单的答案是 "because they defined the language that way"。但是要为什么有点这样,我认为你需要在某些时候(或another type implemented using pointers, like maps or slices)将变量声明为指针,因为指针带有诱杀装置陷阱:例如,您可以在一段代码中以意想不到的方式更改其他代码的局部变量。因此,无论您看到 *int
出现在哪里,它都在告诉您您可能不得不担心(或者,您能够使用)诸如 nil
指针、多段代码的并发访问等问题。
Go 在使指针罩显式化方面比其他语言更为保守:例如,C++ 具有 "reference parameters" (int& i
) 的概念,您可以在其中执行 swap(x,y)
没有 &
或指针出现在 main
中。换句话说,在具有引用参数的语言中,您可能必须查看函数的声明才能知道它是否会更改其参数。对于 Go 的人来说,这种行为有点太 surprising/implicit/tricky 了。
所有的引用和取消引用都需要一些思考,你可能只需要花点时间就能搞定;不过希望这一切对您有所帮助。
我想了解指针在 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
?
非常感谢您的帮助
- From what I understand
&x
gives the address at whichx
is stored. To get the actual value of x, I need to use*x
. Then, I don't understand why&x
is of type*int
. As*x
and&x
are both of type*int
, their differences are not clear to me at all.
当*int
在函数声明中时,表示int指针。但在声明中,*x
不是类型 *int
。这意味着取消引用指针。所以:
*a, *b = *b, *a
这意味着,通过取消引用交换值。 函数参数通过复制传递。所以如果你想将值导出给调用者,你需要传递一个变量的指针作为参数。
- In my swap function, what is the difference between using
*a, *b = *b, *a
and usinga, b = b, a
? Both work but I can't explain why...
正如我所说,a, b = b, a
意味着交换指针而不是交换值。
- Why are the addresses different when printed between step 2 and 3?
这不是定义的结果。变量的地址由 go 的运行时处理。
- Why can't I just modify the address directly assigning
&b
to&a
for example?
并非不可能。例如:
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
}
使用不安全包,您可以更新地址值。但是不认可。
1) 语言混淆导致您认为 *x
和 &x
是同一类型。正如 Not_a_Golfer 所指出的,*
在表达式和类型名称中的用法是不同的。 main()
中的 *x
是无效语法,因为表达式中的 *
试图获取后面的指针指向的值,但 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
中一样,而无需声明指针变量。有一些歧义,如果这不是你想要的,我不确定这部分答案是否对你有帮助。
与许多 "why can't I..." 问题一样,简单的答案是 "because they defined the language that way"。但是要为什么有点这样,我认为你需要在某些时候(或another type implemented using pointers, like maps or slices)将变量声明为指针,因为指针带有诱杀装置陷阱:例如,您可以在一段代码中以意想不到的方式更改其他代码的局部变量。因此,无论您看到 *int
出现在哪里,它都在告诉您您可能不得不担心(或者,您能够使用)诸如 nil
指针、多段代码的并发访问等问题。
Go 在使指针罩显式化方面比其他语言更为保守:例如,C++ 具有 "reference parameters" (int& i
) 的概念,您可以在其中执行 swap(x,y)
没有 &
或指针出现在 main
中。换句话说,在具有引用参数的语言中,您可能必须查看函数的声明才能知道它是否会更改其参数。对于 Go 的人来说,这种行为有点太 surprising/implicit/tricky 了。
所有的引用和取消引用都需要一些思考,你可能只需要花点时间就能搞定;不过希望这一切对您有所帮助。