Pointers 当go中的form参数是map时,传入了什么?

Pointers 当go中的form参数是map时,传入了什么?,pointers,go,go-map,Pointers,Go,Go Map,当形式参数为map时,直接向形式参数赋值不能更改实际参数,但是如果向形式参数添加新的键和值,也可以看到函数外部的实际参数。为什么呢 我不理解下面代码的输出值,形式参数与实际参数不同 unc main() { t := map[int]int{ 1: 1, } fmt.Println(unsafe.Pointer(&t)) copysss(t) fmt.Println(t) } func copysss(m map[int]int)

当形式参数为map时,直接向形式参数赋值不能更改实际参数,但是如果向形式参数添加新的键和值,也可以看到函数外部的实际参数。为什么呢

我不理解下面代码的输出值,形式参数与实际参数不同

unc main() {
    t := map[int]int{
        1: 1,
    }
    fmt.Println(unsafe.Pointer(&t))
    copysss(t)
    fmt.Println(t)
}
func copysss(m map[int]int) {
    //pointer := unsafe.Pointer(&m)
    //fmt.Println(pointer)
    m = map[int]int{
        1: 2,
    }
}

我想知道参数是值还是指针。

在Go中,map是引用类型。这意味着映射实际上驻留在堆中,而变量只是指向该堆的指针

地图是按副本传递的。您可以在函数中更改本地副本,但这不会反映在调用方的作用域中

但是,由于map变量是指向驻留在堆中的唯一映射的指针,因此任何指向同一映射的变量都可以看到每个更改

本文可以阐明以下概念:。

参数既是一个值也是一个指针

等待为什么? 是的,映射(以及切片)是类型,与您要实现的非常相似。想象一下这样的地图:

type map struct {
    // meta information on the map
    meta struct{
        keyT   type
        valueT type
        len    int
    }
    value *hashTable // pointer to the underlying data structure
}
func update(s []int) {
    for i, v := range s {
       s[i] = v*2
    }
}
因此,在第一个函数中,重新分配
m
,传递上述结构的副本(按值传递),并为其分配一个新映射,在此过程中创建一个新的哈希表指针。函数作用域中的变量会被更新,但是您传递的变量仍然保留对原始映射的引用,并且使用它,指向原始映射的指针会被保留

在第二个代码段中,您正在访问底层哈希表(指针的副本,但指针指向同一内存)。您直接操作原始地图,因为您只是更改内存的内容

So TL;博士 映射是一个值,包含映射外观的元信息,以及指向存储在其中的实际数据的指针。指针是按值传递的,就像其他任何东西一样(与C/C++中指针按值传递的方式相同),但当然,取消对指针的引用意味着直接更改内存中的值

仔细的 正如我所说,切片的工作方式基本相同:

type slice struct {
    meta struct {
        type T
        len, cap int
    }
    value *array // yes, it's a pointer to an underlying array
}
底层数组是这样的,即如果一个int片段的cap是10,则该片段将是
[10]int
,而与长度无关。切片由go运行时管理,因此如果超出容量,将分配一个新数组(是上一个数组的
cap
的两倍),复制现有数据,并将切片
value
字段设置为指向新数组。这就是为什么
append
返回要追加的切片,底层指针可能已更改等。。您可以找到有关这方面的更深入的信息

需要注意的是,函数如下所示:

type map struct {
    // meta information on the map
    meta struct{
        keyT   type
        valueT type
        len    int
    }
    value *hashTable // pointer to the underlying data structure
}
func update(s []int) {
    for i, v := range s {
       s[i] = v*2
    }
}
将以与正在分配的函数
m[1]=2
相同的方式运行,但一旦开始追加,运行时就可以随意移动底层数组,并指向新的内存地址。所以底线是:贴图和切片有一个内部指针,它可能会产生副作用,但最好避免bug/歧义。Go支持多个返回值,所以如果您打算更改它,只需返回一个切片即可

笔记: 在您尝试弄清楚什么是映射(引用、值、指针…)时,我注意到您尝试了以下方法:

pointer := unsafe.Pointer(&m)
fmt.Println(pointer)
您在那里所做的,实际上是打印参数变量的地址,而不是任何实际对应于映射本身的地址。传递给
的参数不安全。指针
的类型不是
map[int]int
,而是
*map[int]int
类型

就我个人而言,我认为传递值和传递值之间有太多的混淆。Go在这方面与C完全一样,就像C一样,绝对所有的东西都是通过值传递的。碰巧这个值有时可以是内存地址(指针)


更多详情(参考资料)
  • 注:由于指针、切片和映射被称为*引用类型*,因此存在一些混淆,但是正如其他人和其他地方所解释的,这不应该与C++引用混淆。
您无法确定映射是否驻留在堆IIRC中。运行时管理底层哈希表指针,它可以是指向堆栈内存或堆的指针。。。你就是不知道。此外,您可以因为指针而更改映射中的内容,但由于底层指针是受管理的,因此添加新键可能会导致分配新的哈希表,并且副本中的指针会得到更新,此时调用方范围中的映射不会受到影响
type slice struct {
    meta struct {
        type T
        len, cap int
    }
    value *array // yes, it's a pointer to an underlying array
}
func update(s []int) {
    for i, v := range s {
       s[i] = v*2
    }
}
pointer := unsafe.Pointer(&m)
fmt.Println(pointer)