golang中使用指针

介绍 (Introduction)

When you write software in Go you’ll be writing functions and methods. You pass data to these functions as arguments. Sometimes, the function needs a local copy of the data, and you want the original to remain unchanged. For example, if you’re a bank, and you have a function that shows the user the changes to their balance depending on the savings plan they choose, you don’t want to change the customer’s actual balance before they choose a plan; you just want to use it in calculations. This is called passing by value, because you’re sending the value of the variable to the function, but not the variable itself.

在Go中编写软件时,您将在编写函数和方法。 您将数据作为参数传递给这些函数。 有时,该功能需要数据的本地副本,而您希望原始数据保持不变。 例如,如果您是一家银行,并且具有一个功能,可以根据用户选择的储蓄计划向用户显示其余额的更改,那么您就不想在选择计划之前更改客户的实际余额; 您只想在计算中使用它。 这称为按值传递 ,因为您是将变量的值发送给函数,而不是变量本身。

Other times, you may want the function to be able to alter the data in the original variable. For instance, when the bank customer makes a deposit to their account, you want the deposit function to be able to access the actual balance, not a copy. In this case, you don’t need to send the actual data to the function; you just need to tell the function where the data is located in memory. A data type called a pointer holds the memory address of the data, but not the data itself. The memory address tells the function where to find the data, but not the value of the data. You can pass the pointer to the function instead of the data, and the function can then alter the original variable in place. This is called passing by reference, because the value of the variable isn’t passed to the function, just its location.

其他时候,您可能希望函数能够更改原始变量中的数据。 例如,当银行客户向他们的帐户存款时,您希望存款功能能够访问实际余额,而不是副本。 在这种情况下,您无需将实际数据发送到该函数。 您只需要告诉函数数据在内存中的位置即可。 称为指针的数据类型保存数据的内存地址,但不保存数据本身。 内存地址告诉函数在哪里可以找到数据,但不能告诉数据值。 您可以将指针传递给函数而不是数据,然后函数可以在适当位置更改原始变量。 这称为按引用传递 ,因为变量的值不会传递给函数,而只是传递给函数。

In this article, you will create and use pointers to share access to the memory space for a variable.

在本文中,您将创建并使用指针来共享对变量的内存空间的访问。

定义和使用指针 (Defining and Using Pointers)

&**
&**
var myPointer *int32 = &someint
myPointerint32someintint32
myPointerint32someintint32
string
string
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)
}

Run the program with the following command:

使用以下命令运行程序:

When you run the program, it will print out the value of the variable, as well as the address of where the variable is stored (the pointer address). The memory address is a hexadecimal number, and not meant to be human-readable. In practice, you’ll probably never output a memory address to look at it. We’re showing you for illustrative purposes. Because each program is created in its own memory space when it is run, the value of the pointer will be different each time you run it, and will be different than the output shown here:

运行该程序时,它将打印出变量的值以及变量存储的地址(指针地址)。 内存地址是十六进制数,并不意味着人类可以读取。 实际上,您可能永远不会输出内存地址来查看它。 我们向您展示是出于说明目的。 因为每个程序在运行时都是在其自己的内存空间中创建的,所以每次运行时指针的值都将不同,并且将与此处显示的输出不同:


   
   
Output
creature = shark pointer = 0xc0000721e0
creaturestringsharkpointerpointercreature&pointercreature
creaturesharkstringpointerpointercreature&pointercreature
pointer0xc0000721e0creature
pointer0xc0000721e0creature
pointer*pointer
pointerpointer*pointer
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)

    fmt.Println("*pointer =", *pointer)
}

If you run this code, you’ll see the following output:

如果运行此代码,您将看到以下输出:


   
   
Output
creature = shark pointer = 0xc000010200 *pointer = shark
pointer
pointer
pointer
pointer
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)

    fmt.Println("*pointer =", *pointer)

    *pointer = "jellyfish"
    fmt.Println("*pointer =", *pointer)
}

Run this code to see the output:

运行以下代码以查看输出:


   
   
Output
creature = shark pointer = 0xc000094040 *pointer = shark *pointer = jellyfish
pointer*jellyfishjellyfish
*pointerjellyfishjellyfish
creaturepointercreaturepointercreature
creaturepointercreaturepointerpointercreature
package main

import "fmt"

func main() {
    var creature string = "shark"
    var pointer *string = &creature

    fmt.Println("creature =", creature)
    fmt.Println("pointer =", pointer)

    fmt.Println("*pointer =", *pointer)

    *pointer = "jellyfish"
    fmt.Println("*pointer =", *pointer)

    fmt.Println("creature =", creature)
}

The output looks like this:

输出如下所示:


   
   
Output
creature = shark pointer = 0xc000010200 *pointer = shark *pointer = jellyfish creature = jellyfish

Although this code illustrates how a pointer works, this is not the typical way in which you would use pointers in Go. It is more common to use them when defining function arguments and return values, or using them when defining methods on custom types. Let’s look at how you would use pointers with functions to share access to a variable.

尽管此代码说明了指针的工作方式,但这并不是在Go中使用指针的典型方式。 在定义函数参数和返回值时使用它们,或者在自定义类型上定义方法时使用它们更为常见。 让我们看看如何将指针与函数一起使用以共享对变量的访问。

pointer
pointer

功能指针接收器 (Function Pointer Receivers)

When you write a function, you can define arguments to be passed ether by value, or by reference. Passing by value means that a copy of that value is sent to the function, and any changes to that argument within that function only effect that variable within that function, and not where it was passed from. However, if you pass by reference, meaning you pass a pointer to that argument, you can change the value from within the function, and also change the value of the original variable that was passed in. You can read more about how to define functions in our

编写函数时,可以定义要通过引用传递的参数。 按传递表示将该的副本发送到函数,并且对该函数内该参数的任何更改影响该函数内的该变量,而不影响它从何处传递。 但是,如果您通过引用传递,这意味着您将指针传递给该参数,则可以在函数内更改值,还可以更改传入的原始变量的值。您可以阅读有关如何定义函数的更多信息。在我们的

Deciding when to pass a pointer as opposed when to send a value is all about knowing if you want the value to change or not. If you don’t want the value to change, send it as a value. If you want the function you are passing your variable to be able to change it, then you would pass it as a pointer.

确定何时传递指针而不是何时传递值是关于知道是否要更改值的全部。 如果您不希望值更改,则将其作为值发送。 如果希望函数传递变量能够对其进行更改,则可以将其作为指针传递。

value
valuevalue
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature Creature) {
    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

The output looks like this:

输出如下:


   
   
Output
1) {Species:shark} 2) {Species:jellyfish} 3) {Species:shark}
CreatureSpeciesmaincreatureSpeciessharkcreature
CreatureSpeciesmaincreatureSpeciessharkcreature
changeCreaturecreature
changeCreaturecreature
changeCreaturecreatureCreatureSpeciesjellyfishchangeCreatureSpeciesjellyfish2) {Species:jellyfish}
changeCreaturecreatureCreatureSpeciesjellyfishchangeCreatureSpeciesjellyfish2) {Species:jellyfish}
maincreatureSpeciessharkchangeCreature
maincreatureSpeciessharkchangeCreature
changeCreaturecreature*creaturecreature*creaturecreaturestructSpeciesshark*creaturechangeCreature()
changeCreature*creaturecreaturecreature*creaturecreaturestructSpeciesshark*creaturechangeCreature()
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    changeCreature(&creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

Run this code to see the following output:

运行此代码以查看以下输出:


   
   
Output
1) {Species:shark} 2) &{Species:jellyfish} 3) {Species:jellyfish}
SpeciesjellyfishchangeCreaturemaincreature
changeCreaturechangeCreatureSpeciesjellyfishmaincreature

Therefore, if you want a function to be able to change a value, you need to pass it by reference. To pass by reference, you pass the pointer to the variable, and not the variable itself.

因此,如果希望函数能够更改值,则需要通过引用传递它。 要通过引用传递,请将指针传递给变量,而不是变量本身。

However, sometimes you may not have an actual value defined for a pointer. In those cases, it is possible to have a panic in the program. Let’s look at how that happens and how to plan for that potential problem.

但是,有时您可能没有为指针定义实际值。 在这种情况下,程序可能会出现恐慌 。 让我们看看这是如何发生的,以及如何计划该潜在问题。

零指针 (Nil Pointers)

nilnil
nilnil
CreatureCreaturecreaturenilCreature
CreatureCreaturecreaturenilCreature
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature *Creature

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}

The output looks like this:

输出如下所示:


   
   
Output
1) <nil> panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x109ac86] goroutine 1 [running]: main.changeCreature(0x0) /Users/corylanou/projects/learn/src/github.com/gopherguides/learn/_training/digital-ocean/pointers/src/nil.go:18 +0x26 main.main() /Users/corylanou/projects/learn/src/github.com/gopherguides/learn/_training/digital-ocean/pointers/src/nil.go:13 +0x98 exit status 2
creaturechangeCreatureSpecies
creaturechangeCreatureSpecies

It is common in Go that if you are receiving an argument as a pointer, you check to see if it was nil or not before performing any operations on it to prevent the program from panicking.

在Go语言中,通常会收到一个参数作为指针,然后在对其执行任何操作之前检查它是否为nil,以防止程序崩溃。

nil
nil
if someVariable == nil {
    // print an error or return from the method or fuction
}
nilnil
nilnil
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature *Creature

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    if creature == nil {
        fmt.Println("creature is nil")
        return
    }

    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}
changeCreaturecreaturenilSpecies
changeCreaturecreaturenilSpecies

   
   
Output
1) <nil> creature is nil 3) <nil>
nilcreature
creaturenil
Creaturecreature
Creaturecreature
package main

import "fmt"

type Creature struct {
    Species string
}

func main() {
    var creature *Creature
    creature = &Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    changeCreature(creature)
    fmt.Printf("3) %+v\n", creature)
}

func changeCreature(creature *Creature) {
    if creature == nil {
        fmt.Println("creature is nil")
        return
    }

    creature.Species = "jellyfish"
    fmt.Printf("2) %+v\n", creature)
}
Creature
Creature

   
   
Output
1) &{Species:shark} 2) &{Species:jellyfish} 3) &{Species:jellyfish}
nil
nil

Next, let’s look at how using pointers and values affects defining methods on a type.

接下来,让我们看看使用指针和值如何影响在类型上定义方法。

方法指针接收器 (Method Pointer Receivers)

A receiver in go is the argument that is defined in a method declaration. Take a look at the following code:

go中的接收者是方法声明中定义的参数。 看下面的代码:

type Creature struct {
    Species string
}

func (c Creature) String() string {
    return c.Species
}
c CreaturecCreature
c CreaturecCreature

Just like the behavior of functions is different based on whether you send in an argument as a pointer or a value, methods also have different behavior. The big difference is that if you define a method with a value receiver, you are not able to make changes to the instance of that type that the method was defined on.

就像函数的行为根据您将参数作为指针还是值发送而有所不同一样,方法也具有不同的行为。 最大的区别是,如果使用值接收器定义方法,则无法更改定义该方法的类型的实例。

There will be times that you would like your method to be able to update the instance of the variable that you are using. To allow for this, you would want to make the receiver a pointer.

有时候,您希望您的方法能够更新您正在使用的变量的实例。 为此,您需要使接收器成为指针。

ResetCreatureSpecies
CreatureResetSpecies
package main

import "fmt"

type Creature struct {
    Species string
}

func (c Creature) Reset() {
    c.Species = ""
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    creature.Reset()
    fmt.Printf("2) %+v\n", creature)
}

If we run the program, we will get the following output:

如果运行程序,将得到以下输出:


   
   
Output
1) {Species:shark} 2) {Species:shark}
ResetSpeciescreaturemainsharkResetvaluecreature
ResetSpeciesmaincreaturesharkResetvaluecreature
creaturepointer
creaturepointer
package main

import "fmt"

type Creature struct {
    Species string
}

func (c *Creature) Reset() {
    c.Species = ""
}

func main() {
    var creature Creature = Creature{Species: "shark"}

    fmt.Printf("1) %+v\n", creature)
    creature.Reset()
    fmt.Printf("2) %+v\n", creature)
}
*CreatureResetCreatureReset
ResetCreature*ResetCreature

   
   
Output
1) {Species:shark} 2) {Species:}
ResetSpecies
ResetSpecies

结论 (Conclusion)

Defining a function or method as a pass by value or pass by reference will affect what parts of your program are able to make changes to other parts. Controlling when that variable can be changed will allow you to write more robust and predictable software. Now that you have learned about pointers, you can see how they are used in interfaces as well.

将函数或方法定义为按传递或按引用传递将影响程序的哪些部分能够对其他部分进行更改。 控制何时可以更改该变量将使您能够编写更健壮和可预测的软件。 现在,您已经了解了指针,您还可以看到它们如何在接口中使用。

golang中使用指针