新手问题 Go中取址符(&)取的到底是内存地址,还是指针变量?

ldh33514 · 2019年06月01日 · 最后由 haohongfan 回复于 2019年06月04日 · 753 次阅读

** 最近在书上看到一个例子:**

package main

import "fmt"

func main() {
    var a = [3]int{1, 2, 3}
    var b = &a // & 应该是取址符,变量b就应该是数组a在内存中的地址,那么为什么b[1]可以自加
    b[1]++
    fmt.Println(a, *b)
}

** 对于取址符有了一些疑问,根据这个例子,单独写了一段 demo 代码:**

package main

import "fmt"

func main() {
    var a = []int{1,2,3} // or var a = 3
    var b = &a
    fmt.Println(b)
}

** 经过测试 dmeo 代码中,如果变量 a 为基本类型(例如:int、float、string 等),则变量 b 返回的就是变量 a 在内存中的地址,但是如果变量 a 为复合类型(例如:数组、切片),则变量 b 返回的是指针变量,且可以当作复合类型的变量直接使用(例如:b[1]++),为什么 Go 中的&符返回的东西不一致呢?**

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
var c = []int{1,2,3}
var d = &c
fmt.Println(reflect.TypeOf(d))   // *[]int

var e int = 5
var f = &e
fmt.Println(reflect.TypeOf(f))  // *int

个人理解: & 取的就是地址 你用 fmt.Println() 去打印这个变量,其实是调用了这个变量类型的 String() 方法,可以试试 println() 打印原始值。

func main() {
    var a = []int{1, 2, 3} // or var a = 3
    var b = &a
    println(b) // 0xc00008a000
    fmt.Println(b) // &[1 2 3]                        
}
type Custom [3]int                

func main() {
    var a Custom = [3]int{1, 2, 3}
    fmt.Println(&a) // &[1 2 3]
}
type Custom [3]int                

func (c *Custom) String() string {
    return "*Custom"
}

func main() {
    var a Custom = [3]int{1, 2, 3}
    fmt.Println(&a) // *Custom
}

另外指针类型 b[1]++, 可能是 go 内部做了解引用操作,当执行 b[1]++ 时如果是指针类型是数组,就自动解引用 (*b)[1]++,类似于下边:

type A struct {              
    value int
}

func main() {
    a := A{1}
    b := &a
    fmt.Println(a.value) // 1
    fmt.Println(b.value) // 1
    fmt.Println((*b).value) // 1
}

Go 语言中数组是值语义。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如 C 语言的数组),而是一个完整的值。如果数组较大的话,数组的赋值也会有较大的开销。为了避免复制数组带来的开销,可以传递一个指向数组的指针,但是数组指针并不是数组。 ————by advanced-go-programming-book

取地址就是取变量的地址,如果变量是 slice 就取 slice 的地址 (slice wrapper struct 的地址),如果是 array 就取 array 的地址 (等于第一个元素的地址)。 http://golang.org/ref/spec#Address_operators 这一点没有异议。

奇葩点在于数组的指针可以当作数组变量本身来做 indexing 操作,这个完全就是兼容 C 语言指针用法的语法糖了。 https://golang.org/ref/spec#Index_expressions

> For a of pointer to array type: a[x] is shorthand for (*a)[x]

golang 是没有指针操作的, 所以不会出现像 c/c++ 这样的题:

#include <stdio.h>
int a[2] = {1,2};
int main(){
        printf("a = %p\n", a); // I
        printf("&a = %p\n", &a); // II
        printf("a + 1 = %p\n", a + 1);// III
        printf("&a + 1 = %p\n", &a + 1);// IV
        return 0;
}

所以不用考虑那么复杂: [] 优先级大于 ++, 故 b[1]++ 就是 (*b)[1]++, 所以结果就是 [ 1, 3, 3]

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册