Please explain &, and * pointers

huangapple go评论93阅读模式
英文:

Please explain &, and * pointers

问题

在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
    }
}
英文:

There have been multiple instances where the compiler throws an error when I try to pass variables as arguments inside Go functions. I've been able to debug this sometimes by using a pointer in front of the variable. Both &, and * pointers seem to clear the error. Though, I'd like to understand why. I'm wondering what the difference between &, and * is, and when each should be used. Thank you!

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

答案1

得分: 52

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

如果你像这样创建User的实例:u := new(User),它将是一个指针,因为new函数返回一个指针。你也可以像这样创建一个指向User的指针:var u *User。如果你这样做了,你就需要在调用Decode时去掉&才能正常工作。

指针基本上是保存地址的变量。当你在变量前面加上&时,它返回地址。*可以读作'redirect of'。所以当你像这样创建一个指针:

var x *int

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

其中y是某个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)		// y的地址
	fmt.Println("&pointerToY: ", &pointerToY)// pointerToY的地址
	fmt.Println("&pointerToPointerToInt: ", &pointerToPointerToInt)	// pointerToPointerToInt的地址
	
	// fmt.Println(*y) 抛出错误,因为
	// 你不能没有地址进行重定向..
	// y只有int值10
	fmt.Println("*pointerToY: ", *pointerToY) // 给出y的值
	fmt.Println("*pointerToPointerToInt: ", *pointerToPointerToInt)     // 给出pointerToY的值,即y的地址
	
	fmt.Println("**pointerToPointerToInt: ", **pointerToPointerToInt)    // 这给出10,因为我们重定向两次才能得到y
	
	if pointerToY == *pointerToPointerToInt {
		fmt.Println("'pointerToY == *pointerToPointerToInt' 是相同的!")
	}
	
	if pointerToY == &y {
		fmt.Println("'pointerToY == &y' 是相同的!")
	}
	
	if &pointerToY == pointerToPointerToInt {
		fmt.Println("'&pointerToY == pointerToPointerToInt' 是相同的!")
	}
	
	if y == **pointerToPointerToInt {
		fmt.Println("'y == **pointerToPointerToInt' 是相同的!")
	}
	
	if pointerToY == *pointerToPointerToInt {
		fmt.Println("'pointerToY == *pointerToPointerToInt' 是相同的!")
	}
	
}

希望对你有所帮助!

英文:

In your example above you defined u as type User, but not a pointer to a User. So you need the &u because the Decode function in the json package is expecting an address or pointer.

If you created the instance of User like this: u := new(User) it would be a pointer since the new function returns a pointer. You could also create a pointer to a user like this: var u *User. If you did either of those, you would have to take out the & in the call to Decode for it to work.

Pointers are basically variables that hold addresses. When you put the & in front of a variable it returns the address. The * could be read as 'redirect of'. So when you create a pointer like this:

var x *int

This can be read as x will redirect to an int. And when you assign a value to x you would give it an address like this:
y := 10
x = &y

Where y is some int. So if you were to print out x, you would get the address of y, but if you printed out *x you would redirect to the what x points to which is y's value which is 10. If you were to print out &x, you would get the address of the pointer, x, itself.

If you tried to print out *y, which is just an int, not a pointer, it would throw an error because you would be redirecting with some value that is not an address to redirect to.

Run the below for some pointer fun:

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

Hope this helps!

答案2

得分: 16

我将引用一个聪明的人的话:

> & 在变量名前面用于检索存储该变量值的地址。该地址是指针将要存储的内容。
>
> * 在类型名前面,表示声明的变量将存储另一个该类型变量的地址(而不是该类型的值)。
>
> * 在指针类型的变量前面用于检索存储在给定地址的值。在Go语言中,这被称为解引用。

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

英文:

I will quote one smart dude:

> & in front of variable name is used to retrieve the address of where
> this variable’s value is stored. That address is what the pointer is
> going to store.
>
> * in front of a type name, means that the declared variable will store an address of another variable of that type (not a value of that
> type).
>
> * in front of a variable of pointer type is used to retrieve a value stored at given address. In Go speak this is called dereferencing.

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

答案3

得分: 3

一个简单的示例展示了代码的执行顺序。

import (
    "fmt"
)

func main() {
    x := 0
    fmt.Println("步骤 1", x)
    foo(&x)
    fmt.Println("步骤 4", x)
}

func foo(y *int) {
    fmt.Println("步骤 2", *y)
    *y = 100
    fmt.Println("步骤 3", *y)
}

步骤 结果
1 0
2 0
3 100
4 100

英文:

A simple example showing the code execution sequence.

   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

得分: 2

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

添加一个示例来帮助理解pointeraddress

演示代码

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的地址(使用pointerToY): ", pointerToY)

    y = 4
    fmt.Println("====================================================")
    fmt.Println("y值改变后的地址: ", pointerToY)
    fmt.Println("使用pointer访问y的值(值已改变): ", *pointerToY)
    fmt.Println("y值改变后的x的值: ", x)
}

输出结果:

y:  10
y的地址(使用pointerToY):  0x414020
====================================================
y值改变后的地址:  0x414020
使用pointer访问y的值(值已改变):  4
y值改变后的x的值:  10

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

在上面的示例中,

  1. pointerToY保存指向yaddress的指针。
  2. x保存我们使用pointer传递给它的address的值。
  3. 在更改y的值后,x仍然为10,但如果我们尝试使用指向addresspointerpointerToY)访问该值,我们会得到4
英文:

pointer is used to point towards address and it stores the memory address

Adding one example to help understand 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)
}

>output

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

As we can see, the value might change but the address(&) remains same and so the pointer(*) points to the value of address.

In above example,

  1. pointerToY holds the pointer to refer address of y.
  2. x holds the value which we pass to it using pointer to address of y.
  3. After changing the value of y , the x still has 10 but if we try to access the value using pointer to address (pointerToY) , we get 4

答案5

得分: 1

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

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

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

示例:

func zero(xPointer *int) {
  *xPointer = 0
  fmt.Println(*xPointer)
}

func main() {
  x := 1
  zero(&x)
  fmt.Println(x) // x 的值为 0
}
英文:

For this answer I will try to explain it with a variable value. A pointer can point also to a struct value.

& returns a pointer, which points to a variable value.

* reads the variable value to which the pointer is pointing.

Example:

func zero(xPointer *int) {
  *xPointer = 0
  fmt.Println(*xPointer)
}

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

答案6

得分: 0

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

假设我们想要通过一个函数将变量增加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)没有返回类型,我们无法从该函数中获取任何增加后的值,但我们可以将一个位置(地址)发送给该函数,并告诉它将该位置上的值增加1,然后我们可以从main()函数中访问该位置,完成我们的任务。

一个快速提示:在变量前面的*表示存储在该变量中的内容,而在变量前面的&表示该变量的内部地址。

英文:

I would like to explain the concept of pointers(* and &) with an example:

Think of an example where we want to increment a variable by 1 with help of a function.

package main

import (
	"fmt"
)

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

Explanation of above: We have a function func inc(x int) int which takes an integer and returns an integer with performing an increment.

> Note: Kindly pay attention that func inc(x int) int returns an int, Now what happens if we do not have a return type with that
> function?? This is solved by the pointer.

Look at the below code:

package main

import (
	"fmt"
)

func main() {
	x := 7
	inc(&x)
	fmt.Print(x)

}
func inc(x *int) {
	*x++
}

Explanation of the above code:

Now as our function func inc(x *int) does not have a return type we cannot get any incremented value from this function but what we can do is that we can send a location(address) to this function and tell it to increment the value at this location by one and now we can access that location from inside main() and our job is done.

A quick tip: * in front of a variable means what is stored in that variable?? and & in front of a variable means what is the internal address of that variable?

huangapple
  • 本文由 发表于 2015年10月1日 08:08:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/32877525.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定