go语言中,新分配的变量如何对函数/方法调用方可见?

问题:json.Unmarshal函数什么需要传一个指向map的指针,若map是一个引用类型?为什么必须传指针,尤其是map已经是一个引用类型了? why?

下面是一个示例:

func main(){
	m := make(map[string]string)
	data := `{“foo”:”bar”}`
	err := json.Unmarshal([]byte(data), m)
	if err != nil {
		log.Fatalf("err:%v\n",err)
	}
	fmt.Println(m)
}
// 输出结果
err:json: Unmarshal(non-pointer map[string]string)

解答

如果需求是: 一个函数/方法可能分配一个变量,但新分配的变量需要对调用方(caller)可见,解决方案有:(a)该变量必须在函数的return语句(译者注:大白话就是,该函数必须有一个返回类型) 或者 (b)该(新分配的)变量可被赋给函数/方法参数。因为在go语言中,一切传参都是传值(everything is pass by value)。若是情况(b),该(函数/方法)参数必须是个指针。下面是代码:

func mapFunc(m map[string]interface{}) {
	// fmt.Printf("m未分配内存之前,m=%p\n", m)
	// fmt.Printf("m=%v\n", m)
	m = make(map[string]interface{})
	m["abc"] = "123"
	// fmt.Printf("m分配内存以后,m=%p\n", m)
}
// 译者加的函数
func mapFuncWithReturn(m map[string]interface{}) map[string]interface{}{
	// fmt.Printf("m未分配内存之前,m=%p\n", m)
	// fmt.Printf("m=%v\n", m)
	m = make(map[string]interface{})
	m["abc"] = "123"
	// fmt.Printf("m分配内存以后,m=%p\n", m)
	return m
}

func mapPtrFunc(mp *map[string]interface{}) {
	// fmt.Printf("mp=%p\n", mp)
	m := make(map[string]interface{})
	m["abc"] = "123"
	// fmt.Printf("m=%p\n", m)
	*mp = m
	// fmt.Printf("mp=%p\n", mp)
}
func main() {
	var m1, m2 map[string]interface{}
	mapFunc(m1)
	mapPtrFunc(&m2)
	/*
	译者添加的情况
	m1 = mapFuncWithReturn(m1)  // 作为return返回
	*/
	fmt.Printf("%+v, %+v\n", m1, m2)
}
// 输出结果
map[], map[abc:123]

下面的图阐述了变量分配的过程。

下面是对上述代码片段的解读。

  1. 首先,两个map变量m1m2都指向nil
  2. 调用mapFunc将把变量m1指向的值拷贝到m,生成的m也将指向nil map(译者注:nil mapempty map不是一回事,注意区别)。
  3. 如果序号1中,已经为map变量分配了内存,那么在序号2,变量m1指向的底层的map数据结构的地址(不是m1的地址)将被拷贝到变量m。在这种情况下,m1m都指向相同的map数据结构,因此通过变量m1修改map中的项将对变量m是可见的。
  4. mapFunc函数中,分配了一个新的map并赋值给了变量m。无法将其赋值给m1

若是指针:

  1. 在调用mapPtrFunc函数时,变量m2的地址将被拷贝到变量mp(译者注:mp存的是变量m2的地址)。
  2. mapPtrFunc函数中,分配了一个新的map变量并赋值给了*mp(不是mp)。因为mp是指向m2的指针,将新创建的map赋值给*mp将改变指针变量m2指向的值。注意到:mp的值未改变,例如:m2的地址。

注:上面的内容出自StackOverflow。感兴趣的读者可以点击进入原文查看。本文是对其中比较有意思的地方做了一个笔记,方便复习。本文的代码、图等的版权归属于原作者。

你可能感兴趣的:(programming,languages)