Подтвердить что ты не робот

Пожалуйста, объясните &, и * указатели в GoLang

Недавно я начал изучать GoLang. Было несколько случаев, когда компилятор выдает ошибку, когда я пытаюсь передать переменные в качестве аргументов внутри функций 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
    }
}
4b9b3361

Ответ 1

В приведенном выше примере вы определили u как тип User, но не указатель на User. Так что вам нужен & u, потому что функция Decode в пакете json ожидает адрес или указатель.

Если вы создали экземпляр User следующим образом: u: = new (User), это был бы указатель, поскольку новая функция возвращает указатель. Вы также можете создать указатель на пользователя следующим образом: var u * User. Если бы вы сделали один из них, вам нужно было бы отключить & в вызове Decode, чтобы он заработал.

Указатели - это в основном переменные, которые содержат адреса. Когда вы помещаете & перед переменной, она возвращает адрес. * Можно прочитать как "перенаправление". Итак, когда вы создаете указатель, как это:

var x * int

Это можно прочитать, так как x перенаправит на int. И когда вы присваиваете значение x, вы присваиваете ему такой адрес: y: = 10 x = & y

Где у какой-то инт. Таким образом, если бы вы распечатали x, вы бы получили адрес y, но если вы распечатали * x, вы перенаправили бы на то, на что указывает x, значение y равно 10. Если вы должны были распечатать & x, вы получит адрес указателя, х, сам.

Если вы попытаетесь распечатать * y, который является просто целым числом, а не указателем, он выдаст ошибку, потому что вы будете перенаправлять с некоторым значением, которое не является адресом для перенаправления.

Запустите ниже для некоторого веселья указатель:

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

}

Надеюсь это поможет!

Ответ 2

Я процитирую одного умного парня:

& перед именем переменной используется для получения адреса, где хранится значение этой переменной. Этот адрес - то, что указатель собирается сохранить.

* перед именем типа означает, что объявленная переменная будет хранить адрес другой переменной этого типа (не значение этого типа).

* перед переменной указателя типа используется для извлечения значения, хранящегося по данному адресу. В Go говорят, что это называется разыменованием.

источник: http://piotrzurek.net/2013/09/20/pointers-in-go.html

Ответ 3

pointer используется для указания на address и извлечения сохраненного значения по адресу памяти

Добавление одного примера, чтобы помочь понять pointer против address:

Демо-код

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 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 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 содержит указатель для ссылки на address y.
  2. x содержит значение, которое мы передаем ему, используя pointer на address y.
  3. После изменения значения y у x остается 10 но если мы попытаемся получить доступ к значению, используя pointer на address (pointerToY), мы получим 4