理解Go中的指针

时间:2015-01-07 23:33:22

标签: pointers 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

我有几个问题:

  1. 据我所知,&x提供了存储x的地址。要获得x的实际值,我需要使用*x。然后,我不明白为什么&x属于*int类型。由于*x&x都属于*int类型,我们之间的差异根本不明确。

  2. 在我的交换功能中,使用*a, *b = *b, *a和使用a, b = b, a有什么区别?两者都有效,但我无法解释原因......

  3. 为什么在步骤2和3之间打印时地址会有所不同?

  4. 为什么我不能直接修改地址直接将&b分配给&a

  5. 非常感谢你的帮助

2 个答案:

答案 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 2http://play.golang.org/p/rCXDgkZ9kG

3)swap打印指针变量的地址,而不是底层整数的地址。您可以打印ab来查看整数的地址。

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)

  
      
  1. 根据我的理解,&x提供了存储x的地址。要获得x的实际值,我需要使用*x。然后,我不明白为什么&x属于*int类型。由于*x&x都属于*int类型,我们之间的差异根本不明确。
  2.   

*int在函数声明中时,它表示int的指针。但是在声明中,*x不是*int类型。它意味着指针的解除引用。所以:

*a, *b = *b, *a

这意味着,通过取消引用交换值。 功能参数通过copyed传递。因此,如果要将值导出到调用者,则需要将变量的指针作为参数传递。

  
      
  1. 在我的交换功能中,使用*a, *b = *b, *a和使用a, b = b, a有什么区别?两者都有效,但我无法解释原因......
  2.   

正如我所说,a, b = b, a意味着交换指针而不是交换值。

  
      
  1. 为什么在步骤2和3之间打印时地址会有所不同?
  2.   

这不是定义的结果。变量的地址由go运行时处理。

  
      
  1. 为什么我不能直接修改地址直接将&b分配给&a
  2.   

并非不可能。例如:

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
}

使用不安全的软件包,您可以更新地址值。但它没有得到认可。