2015-10-01 3 views
11

Недавно я начал изучать GoLang. Было несколько случаев, когда компилятор выдает ошибку, когда я пытаюсь передать переменные в качестве аргументов внутри функций Go. Я мог отлаживать это иногда, используя указатель перед переменной. Как &, так и * указатели, похоже, устраняют ошибку. Хотя, я хотел бы понять, почему. Мне интересно, какая разница между & и * есть, и когда каждый из них должен использоваться. Спасибо!Пожалуйста, объясните & & и указатели * в GoLang

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 
    } 
} 
+0

Логически оба они служат цели.Подумайте об этом так: когда вы передаете & n в функцию, вы передаете копию адреса памяти n - эквивалентную указателю. Если вы передаете * n в функцию, функция получает копию своих значений, которая является адресом памяти n. Так что в этом случае они не все так разные ... кроме, может быть, звездочки выглядят лучше :) – Snowman

+2

@Snowman: ваш комментарий очень запутан. Вы не можете сказать, что они «не все так разные», когда они выполняют функционально противоположные вещи. Оператор '&' _references_ (принимает адрес значения) и '*' operator _dereferences_ (принимает значение по адресу). – JimB

+0

@JimB Я просто указываю, что в контексте удовлетворения аргументов в вызове функции они не такие разные. В других контекстах они представляют собой два противоположных конца спектра. Является ли какой-либо из того, что я сказал неправильно? – Snowman

ответ

31

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

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

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

вар х * INT

Это может быть прочитана как х будет перенаправлять на междунар. И когда вы присваиваете значение х вы могли бы дать ему адрес, как это: у: = 10 х = & у

где у какой-то Int. Поэтому, если вы хотите распечатать 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!") 
    } 

} 

Надеется, что это помогает!

+0

Отличный пример, хотя было бы лучше, если бы вы включили метки в статусы println. – mikias

2

Процитирую один умный чувак:

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

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

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

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

Смежные вопросы