为什么结构体引用的内存地址会发生变化?

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

Why does the memory address of a struct reference change?

问题

我有一个结构体和一个在结构体引用上工作的方法。每次调用该方法时,指针地址都会发生变化。为什么会这样呢?

代码:

package main

import "k8s.io/contrib/compare/Godeps/_workspace/src/github.com/emicklei/go-restful/log"

type Whatever struct {
	Name string
}

func (whatever *Whatever) GetNameByReference() string {
	log.Printf("Whatever.GetNameByReference() memory address: %v", &whatever)

	return whatever.Name
}

func evaluateMemoryAddressWhenNotWritingAnything() {
	whatever := Whatever{}

	whatever.GetNameByReference()
	whatever.GetNameByReference()
	whatever.GetNameByReference()
}

func main() {
	evaluateMemoryAddressWhenNotWritingAnything()
}

输出:

log.go:30: Whatever.GetNameByReference() memory address: 0xc420034020
log.go:30: Whatever.GetNameByReference() memory address: 0xc420034030
log.go:30: Whatever.GetNameByReference() memory address: 0xc420034038
英文:

I have a struct and a method that's working on the structs reference. The pointer address is changing every time I call the method. Why is it like that?

Code

package main

import "k8s.io/contrib/compare/Godeps/_workspace/src/github.com/emicklei/go-restful/log"

type Whatever struct{
	Name string
}

func (whatever *Whatever) GetNameByReference() (string) {
log.Printf("Whatever.GetNameByReference() memory address: %v", &whatever)

    return whatever.Name
}

func evaluateMemoryAddressWhenNotWritingAnything()  {
    whatever := Whatever{}

    whatever.GetNameByReference()
    whatever.GetNameByReference()
    whatever.GetNameByReference()
}

func main() {
    evaluateMemoryAddressWhenNotWritingAnything()
}

Output:

log.go:30: Whatever.GetNameByReference() memory address: 0xc420034020
log.go:30: Whatever.GetNameByReference() memory address: 0xc420034030
log.go:30: Whatever.GetNameByReference() memory address: 0xc420034038

答案1

得分: 12

永远不要考虑和谈论引用。Go语言没有"引用"的概念,一切都是值。有些东西是指针值。你的问题源于将*X视为"对X的引用",但实际上它不是:它是一个保存了X的内存地址(或者是nil)的值。

所以在func (whatever *Whatever)中,变量whatever是指向Whatever的指针。whatever的值是指针所指向的Whatever的内存地址。你想要打印这个内存地址,也就是whatever的值。

你使用了Printf("%v", &whatever)。记住:whatever是一个变量(保存了一个内存地址)。所以&whatever是变量本身的地址:&whatever的类型是**Whatever。在地址&whatever上找到的内容并不是你感兴趣的值;它只是用于存储原始Whatever地址的临时变量。当然,这个临时变量并没有固定在内存中,可以自由改变。

你应该使用Printf("%p", whatever)。占位符%p用于指针值,而whatever是一个指针,你对它的值感兴趣,所以打印这个值。

英文:

Never think and never talk about references. Go has no notion of "reference", everything is a value. Some things are pointer values. Your problem stems from thinking about *X as "a reference to an X" which it isn't: It is a value holding the memory address of an X (or nil).

So in func (whatever *Whatever) the variable whatever is a pointer to a Whatever. The value of whatever is the memory address of the Whatever the pointer points to. You would like to print this memory address, i.e. the value of whatever.

You do Printf("%v", &whatever). Remember: whatever is a variable (holding a memory address). So &whatever is the address of the variable itself: &whatever is of type **Whatever. What you find at the address &whatever is not the value you are interested in; it is just the temporary variable used to store the address of the original Whatever. Of course this temporary variable is not pinned in memory an may change freely.

You should do Printf("%p", whatever). The verb %p is for pointer values and whateveris a pointer and you are interested in its value, so print this value.

答案2

得分: 3

你没有显示结构体的地址,而是显示了地址的地址(指针的地址)。指针作为参数传递,因此每次都是新的。删除log.Printf("Whatever.GetNameByReference() memory address: %v", &whatever)中的&,以获得你想要的结果(并使用%p代替%v)。

英文:

You are not displaying the address of the struct, you are displaying the address of the address (address of the pointer). The pointer is passed as a parameter and thus new each time. Remove the & in log.Printf("Whatever.GetNameByReference() memory address: %v", &whatever) to get what you want (and use %p instead of %v).

答案3

得分: 0

调用方法

func (whatever *Whatever) GetNameByReference() (string) {

与调用函数并将接收器作为第一个参数提供的方式几乎相同

func GetNameByReference(whatever *Whatever) (string) {

Go的调用约定是始终通过值传递参数。因此,每次调用whatever.GetNameByReference()时,它都会接收到whatever的新副本,该副本本身保存着相同的*Whatever地址。如上所述,使用以下方式记录日志

log.Printf("Whatever.GetNameByReference() memory address: %v", whatever) //instead of &whatever

将记录相同的whatever值,即地址。

英文:

Calling method

func (whatever *Whatever) GetNameByReference() (string) {

is much the same as calling function providing receiver as first argument

func GetNameByReference(whatever *Whatever) (string) {

Go calling convention is to always pass arguments copy_by_value. So each time you call whatever.GetNameByReference() it receives fresh copy of whatever which itself holds same *Whatever address. As already written doing

log.Printf("Whatever.GetNameByReference() memory address: %v", whatever) //instead of &whatever

will log the same whatever value which is address.

huangapple
  • 本文由 发表于 2017年1月18日 16:41:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/41714727.html
匿名

发表评论

匿名网友

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

确定