【问题标题】:Please explain &, and * pointers请解释 &, 和 * 指针
【发布时间】:2015-10-01 00:08:52
【问题描述】:

当我尝试在 Go 函数中将变量作为参数传递时,编译器会抛出多个错误。我有时可以通过在变量前面使用指针来调试它。 & 和 * 指针似乎都可以清除错误。不过,我想了解原因。我想知道 & 和 * 之间的区别是什么,以及何时应该使用它们。谢谢!

func (ctx *NewContext) SendNotification(rw http.ResponseWriter, req *http.Request, p httprouter.Params) {

    decoder := json.NewDecoder(req.Body)

    var u User

    if err := decoder.Decode(&u); err != nil {
        http.Error(rw, "could not decode request", http.StatusBadRequest)
        return
    }
}

【问题讨论】:

  • 从逻辑上讲,它们都达到了目的。可以这样想:当您将 &n 传递给函数时,您传递的是 n 的内存地址的副本——相当于一个指针。如果将 *n 传递给函数,则函数会获取其值的副本,即 n 的内存地址。所以在那种情况下,它们并没有什么不同......除了星号看起来更好:)
  • @Snowman:你的评论很混乱。当他们做功能相反的事情时,你不能说他们“没有那么不同”。 & 操作符 references(获取值的地址)和 * 操作符 dereferences(获取地址处的值)。
  • @JimB 我只是指出,在满足函数调用中的参数的上下文中,它们并没有那么不同。在其他情况下,它们是频谱的两个相反端。我说的有什么不对吗?

标签: pointers go


【解决方案1】:

在上面的示例中,您将 u 定义为 User 类型,但不是指向 User 的指针。所以你需要 &u 因为 json 包中的 Decode 函数需要一个地址或指针。

如果您像这样创建 User 的实例: u := new(User) 它将是一个指针,因为 new 函数返回一个指针。您还可以像这样创建指向用户的指针:var u *User.如果您执行其中任何一项,则必须在对 Decode 的调用中取出 & 才能使其正常工作。

指针基本上是保存地址的变量。当您将 & 放在变量前面时,它会返回地址。 * 可以读作“重定向的”。因此,当您创建这样的指针时:

var x *int

这可以理解为 x 将重定向到一个 int。当你给 x 赋值时,你会给它一个这样的地址: y := 10 x = &y

其中 y 是一些整数。所以如果你要打印出 x,你会得到 y 的地址,但如果你打印出 *x,你会重定向到 x 指向的 y 的值,即 10。如果你要打印出 &x,你将获得指针 x 本身的地址。

如果您尝试打印出 *y,它只是一个 int,而不是指针,它会引发错误,因为您将使用一些不是重定向到的地址的值进行重定向。

运行以下代码以获得一些指针的乐趣:

package main

import "fmt"

func main() {
    var y int
    var pointerToY *int
    var pointerToPointerToInt **int

    y = 10
    pointerToY = &y
    pointerToPointerToInt = &pointerToY

    fmt.Println("y: ", y)
    fmt.Println("pointerToY: ", pointerToY)
    fmt.Println("pointerToPointerToInt: ", pointerToPointerToInt)

    fmt.Println("&y: ", &y)     // address of y
    fmt.Println("&pointerToY: ", &pointerToY)// address of pointerToY
    fmt.Println("&pointerToPointerToInt: ", &pointerToPointerToInt) // address of pointerToPointerToInt

    // fmt.Println(*y) throws an error because 
    // you can't redirect without an address.. 
    // y only has int value of 10
    fmt.Println("*pointerToY: ", *pointerToY) // gives the value of y
    fmt.Println("*pointerToPointerToInt: ", *pointerToPointerToInt)     // gives the value of pointerToY which is the address of y

    fmt.Println("**pointerToPointerToInt: ", **pointerToPointerToInt)    // this gives 10, because we are redirecting twice to get y

    if pointerToY == *pointerToPointerToInt {
        fmt.Println("'pointerToY == *pointerToPointerToInt' are the same!")
    }

    if pointerToY == &y {
        fmt.Println("'pointerToY == &y' are the same!")
    }

    if &pointerToY == pointerToPointerToInt {
        fmt.Println("'&pointerToY == pointerToPointerToInt' are the same!")
    }

    if y == **pointerToPointerToInt {
        fmt.Println("'y == **pointerToPointerToInt' are the same!")
    }

    if pointerToY == *pointerToPointerToInt {
        fmt.Println("'pointerToY == *pointerToPointerToInt' are the same!")
    }

}

希望这会有所帮助!

【讨论】:

  • 很好的例子,虽然如果你在 println 语句中包含标签会更好。
【解决方案2】:

我会引用一个聪明的家伙:

&前面的变量名用于检索where的地址 这个变量的值被存储。该地址就是指针 去商店。

* 在类型名称前面,表示声明的变量将存储该类型的另一个变量的地址(不是该类型的值 类型)。

* 前面的指针类型变量用于检索存储在给定地址的值。在 Go 语言中,这称为解引用。

来源:http://piotrzurek.net/2013/09/20/pointers-in-go.html

【讨论】:

    【解决方案3】:

    显示代码执行顺序的简单示例。

       import (
            "fmt"
        )
    
        func main() {
            x := 0
            fmt.Println("Step 1", x)
            foo(&x)
            fmt.Println("Step 4", x)
        }
    
        func foo(y *int) {
    
            fmt.Println("Step 2", *y)
            *y = 100
            fmt.Println("Step 3", *y)
        }
    /*
     Steps  Result
       1      0
       2      0
       3      100
       4      100
     */
    

    【讨论】:

      【解决方案4】:

      pointer 用于指向指向address 并存储内存地址

      添加一个例子来帮助理解pointer vs address

      Demo code

      package main
      
      import "fmt"
      
      func main() {
          var y int
          var pointerToY *int
          var x int
          //var willThrowErrorVariable int
      
          y = 10
          pointerToY = &y
          //willThrowErrorVariable = &y 
          x = *pointerToY
      
          fmt.Println("y: ",y)
          fmt.Println("y's address using pointerToY: ",pointerToY)
      
          y = 4
          fmt.Println("====================================================")
          fmt.Println("Address of y after its value is changed: ",pointerToY)
          fmt.Println("value of y using pointer after its value is changed: ",*pointerToY)
          fmt.Println("Value of x after y value is changed: ",x)
      }
      

      输出

      y:  10
      y's address using pointerToY:  0x414020
      ====================================================
      Address of y after its value is changed:  0x414020
      value of y using pointer after its value is changed:  4
      Value of x after y value is changed:  10
      

      正如我们所见,值可能会改变,但address(&) 保持不变,因此pointer(*) 指向address 的值。

      在上面的例子中,

      1. pointerToY 持有指向 addressy 的指针。
      2. x 保存我们使用pointer 传递给它的值到yaddress
      3. 更改 y 的值后,x 仍然有 10 但如果我们尝试使用 pointeraddress (pointerToY) 访问该值,我们得到4

      【讨论】:

        【解决方案5】:

        对于这个答案,我将尝试用一个变量值来解释它。指针也可以指向结构值。

        & 返回一个指向变量值的指针。

        * 读取指针所指向的变量值。

        例子:

        func zero(xPointer *int) {
          *xPointer = 0
          fmt.Println(*xPointer)
        }
        
        func main() {
          x := 1
          zero(&x)
          fmt.Println(x) // x is 0
        }
        

        【讨论】:

          【解决方案6】:

          我想用一个例子来解释一下指针的概念(*&):

          想一个例子,我们想在函数的帮助下增加一个变量1

          package main
          
          import (
              "fmt"
          )
          
          func main() {
              x := 7
              fmt.Print(inc(x))
          }
          func inc(x int) int {
              return x + 1
          }
          

          上面的解释:我们有一个函数func inc(x int) int,它接受一个整数并返回一个执行增量的整数。

          注意:请注意 func inc(x int) int 返回一个 int,如果我们没有返回类型会发生什么 功能??这是通过指针解决的。

          看下面的代码:

          package main
          
          import (
              "fmt"
          )
          
          func main() {
              x := 7
              inc(&x)
              fmt.Print(x)
          
          }
          func inc(x *int) {
              *x++
          }
          

          上面代码的解释:

          现在,由于我们的函数func inc(x *int) 没有返回类型,我们无法从该函数获取任何递增的值,但我们可以做的是我们可以向该函数发送一个位置(地址)并告诉它递增该值在这个位置,现在我们可以从main() 内部访问该位置,我们的工作就完成了。

          快速提示:变量前面的* 表示该变量中存储了什么?变量前面的& 表示该变量的内部地址是什么

          【讨论】:

            猜你喜欢
            • 2013-03-15
            • 1970-01-01
            • 2011-11-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-04-14
            • 1970-01-01
            相关资源
            最近更新 更多