duanqinjiao5244
2014-09-01 08:33 阅读 83
已采纳

为什么对变量进行内联实例化需要显式地将其地址用作指针方法,而对于现有的var则隐式

Is there a reason for this behaviour? I would like to know what is different in the memory level. The compiler returns "cannot take the address of composite literal" while i can explicitly ask it to do it.

Heres the go playground go playground link

u := User{"john"}
fmt.Println(u.Name()) //implicit

//fmt.Println(User{"john"}.Name()) //Error: cannot call pointer method on composite literal, cannot take the address of composite literal
fmt.Println((&User{"jim"}).Name()) //explicit

type User struct {
    name string
}

func (u *User) Name() string {
    return u.name
}
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    duanhe4155 duanhe4155 2014-09-01 08:43

    Because a composite litteral is not addressable until it's been assigned to a variable:

    The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array.
    As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal.

    You can take the address to User{"jim"}: (&User{"jim"}) (literal pointer).
    But you cannot directly use the value of User{"jim"} (literal value, not until it is assign to a variable).
    See more in this thread.


    This thread adds to the"Expression isn't addressable" issue:

    Taking the address of something, that up to this hypothetical point was not addressable (an expression that doesn't happen to be rooted to an identifier), is semantically no different then setting the dereferenced value of the destination pointer.
    In other words, the following are equivalent:

    var x *int
    *x = (2 * 3) + 4
    
    var x *int
    x = &((2 * 3) + 4)
    

    Since an expression is thrown away as soon as it's used, the address of its value (which often is entirely register-resident, and thus doesn't actually have an address) is entirely meaningless in any case, and the only benefit from taking it's address is the syntactic convenience of type-inference in variable declarations (saving at most a single line):

    x := &((2 * 3) + 4)
    

    A problem with making arbitrary expressions addressable:

    m := map[int]int{1:2}
    x := &m[1]
    x = 3
    fmt.Println("x is", x, "while m[1] is", m[1])
    // will print `x is 3 while m[1] is 2`
    

    In short: it'll be unclear (and nigh impossible to debug) whether you're taking the address of something you expect to be addressable, or when you're taking the address of an implicit intermediate var (which is very often what you don't want).

    点赞 评论 复制链接分享
  • dongzhen7108 dongzhen7108 2014-09-01 09:15

    Is there a reason for this behaviour?

    The reason for the behavior is that there is an exception to addressability for that specific notation and the reason for that exception is explained here by one of the Go maintainers :

    They're special because we decided that the notation was useful enough for constructors to merit a special case:

    Taking the address of a composite literal (§Address operators) 
    generates a unique pointer to an instance of the literal's value. 
    
    http://golang.org/doc/go_spec.html#Composite_literals
    

    Russ


    "Taking the address of a composite literal (§Address operators) generates a unique pointer to an instance of the literal's value." Could you elaborate that?

    I think that means the notation &Object{} notation is simply a literal pointer each time it is executed it creates a new instance of the value and a new pointer is created that points to the new value. The addressability exception simply results from the notation of this literal, it is unlike the other addressables because it is not being acted upon by the & operator.

    I would like an answer similar to jesse's on the "here" link you mentioned. That is for C. Can anyone confirm that for golang?

    My guess is this that this notation is helpful with escape analysis, which determines at compile time whether a references escapes the function and determines whether it should be allocated on the heap or the stack ( faster ). From what I understand the variables that have their address taken are considered candidates for escape analysis. Since one of Go's main goals is fast compilation I think reference creation and tracking is required to be clear.

    For example, for Item{}.Method() the need to create a pointer and therefore run escape analysis to the Item{} value would depend on the receiver type of the Method() whether it is *Item or just Item this unnecessarily complicates compilation. That's why you use the explicit notation &Item{} to construct a *Item.

    点赞 评论 复制链接分享

相关推荐