web-dev-qa-db-fra.com

Veuillez expliquer les pointeurs &, et * dans GoLang

J'ai récemment commencé à apprendre le GoLang. Il y a eu plusieurs cas où le compilateur génère une erreur lorsque j'essaie de passer des variables comme arguments dans les fonctions Go. J'ai pu déboguer cela parfois en utilisant un pointeur devant la variable. Les pointeurs & et * semblent effacer l'erreur. Mais j'aimerais comprendre pourquoi. Je me demande quelle est la différence entre &, et *, et quand chacun doit être utilisé. Je vous remercie!

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
    }
}
23
zero_cool

Dans votre exemple ci-dessus, vous avez défini u comme type User, mais pas comme pointeur vers un utilisateur. Vous avez donc besoin du & u car la fonction Decode du package json attend une adresse ou un pointeur.

Si vous avez créé l'instance de User comme ceci: u: = new (User), ce serait un pointeur puisque la nouvelle fonction retourne un pointeur. Vous pouvez également créer un pointeur vers un utilisateur comme celui-ci: var u * User. Si vous avez fait l'un ou l'autre de ces éléments, vous devrez retirer le & dans l'appel à Decode pour que cela fonctionne.

Les pointeurs sont essentiellement des variables qui contiennent des adresses. Lorsque vous placez le & devant une variable, il renvoie l'adresse. Le * pourrait être lu comme "redirection de". Ainsi, lorsque vous créez un pointeur comme celui-ci:

var x * int

Cela peut être lu car x redirigera vers un int. Et lorsque vous attribuez une valeur à x, vous lui donnez une adresse comme celle-ci: y: = 10 x = & y

Où y est un int. Donc, si vous imprimez x, vous obtiendrez l'adresse de y, mais si vous imprimez * x, vous redirigerez vers ce que x pointe vers la valeur de y qui est 10. Si vous imprimez & x, vous obtiendrait l'adresse du pointeur, x, lui-même.

Si vous essayez d'imprimer * y, qui est juste un int, pas un pointeur, cela générera une erreur car vous redirigerez avec une valeur qui n'est pas une adresse vers laquelle rediriger.

Exécutez ci-dessous pour vous amuser avec le pointeur:

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!")
    }

}

J'espère que cela t'aides!

46
wueb

Je vais citer un mec intelligent:

& devant le nom de la variable est utilisé pour récupérer l'adresse de stockage de la valeur de cette variable. Cette adresse est ce que le pointeur va stocker.

* devant un nom de type, signifie que la variable déclarée stockera une adresse d'une autre variable de ce type (pas une valeur de ce type).

* devant une variable de type pointeur est utilisé pour récupérer une valeur stockée à une adresse donnée. Dans Go Speak, cela s'appelle le déréférencement.

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

9
user1660210

pointer est utilisé pour pointer vers address et il stocke l'adresse mémoire

Ajout d'un exemple pour mieux comprendre pointer vs address:

Code démo

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)
}

production

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

Comme nous pouvons le voir, la valeur peut changer mais le address (&) reste le même et donc le pointer (*) pointe sur la valeur de address.

Dans l'exemple ci-dessus,

  1. pointerToY contient le pointeur pour faire référence à address de y.
  2. x contient la valeur que nous lui transmettons en utilisant pointer à address de y.
  3. Après avoir modifié la valeur de y, le x a toujours 10 mais si nous essayons d'accéder à la valeur en utilisant pointer à address ( pointerToY ), nous obtenons 4
1
Shashank Vivek