Когда параметром формы в 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) {
    //pointer := unsafe.Pointer(&m)
    //fmt.Println(pointer)
    m = map[int]int{
        1: 2,
    }
}
stdout :0xc000086010

        map[1:1]
func 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[1] = 2
}
stdout :0xc00007a010

        map[1:2]
func 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[1] = 2
}
stdout:0xc00008a008
       0xc00008a018
       map[1:2]

Я хочу знать, является ли параметр значением или указателем.

2 ответа

Параметр является одновременно значением и указателем.

Подожди.. что?

Да, карта (и, если на то пошло, фрагменты) - это типы, очень похожие на то, что вы бы реализовали. Подумайте о карте так:

type map struct {
    // meta information on the map
    meta struct{
        keyT   type
        valueT type
        len    int
    }
    value *hashTable // pointer to the underlying data structure
}

Итак, в вашей первой функции, где вы переназначаете mВы передаете копию вышеупомянутой структуры (передача по значению) и назначаете ей новую карту, создавая новый указатель хеш-таблицы в процессе. Переменная в области действия функции обновляется, но переданная вами по-прежнему содержит ссылку на исходную карту, и вместе с ней указатель на исходную карту сохраняется.

Во втором фрагменте вы получаете доступ к базовой хеш-таблице (копия указателя, но указатель указывает на ту же память). Вы напрямую манипулируете исходной картой, потому что вы просто меняете содержимое памяти.

Так что TL;DR

Карта - это значение, содержащее метаинформацию о том, как карта выглядит, и указатель на фактические данные, хранящиеся внутри. Указатель передается по значению, как и все остальное (аналогично тому, как указатели передаются по значению в C/C++), но, конечно, разыменование указателя означает, что вы изменяете значения в памяти напрямую.

Осторожный...

Как я уже сказал, ломтики работают примерно так же:

type slice struct {
    meta struct {
        type T
        len, cap int
    }
    value *array // yes, it's a pointer to an underlying array
}

Базовый массив, скажем, кусок целых будет [10]int если крышка среза равна 10, независимо от длины. Срез управляется средой выполнения go, поэтому, если вы превышаете емкость, выделяется новый массив (в два раза больше cap предыдущего), существующие данные копируются, и срез value поле установлено, чтобы указать на новый массив. Вот почему append возвращает фрагмент, к которому вы добавляете, основной указатель мог измениться и т. д. вы можете найти более подробную информацию по этому вопросу.

Вы должны быть осторожны с такой функцией:

func update(s []int) {
    for i, v := range s {
       s[i] = v*2
    }
}

будет вести себя так же, как функция, которую вы назначаете m[1] = 2, но как только вы начнете добавлять, среда выполнения сможет свободно перемещать базовый массив и указывать на новый адрес памяти. Итак, суть: карты и фрагменты имеют внутренний указатель, который может вызывать побочные эффекты, но лучше избегать ошибок / неясностей. Go поддерживает несколько возвращаемых значений, поэтому просто верните фрагмент, если вы решите изменить его.

Заметки:

В вашей попытке выяснить, что такое карта (ссылка, значение, указатель...), я заметил, что вы попробовали это:

pointer := unsafe.Pointer(&m)
fmt.Println(pointer)

На самом деле вы печатаете адрес переменной-аргумента, а не адрес, который действительно соответствует самой карте. аргумент передан unsafe.Pointer не того типа map[int]int, а скорее это типа *map[int]int,

Лично я думаю, что слишком много путаницы в отношении передачи по значению против передачи мимо. В этом отношении Go работает точно так же, как C, так же как и C, абсолютно все передается по значению. Так уж получилось, что это значение иногда может быть адресом памяти (указателем).


Подробнее (ссылки)

  • Ломтики: использование и внутренности
  • Карты Примечание: есть некоторая путаница, вызванная этим, так как указатели, фрагменты и карты называются * ссылочными типами *, но, как объяснено другими, и в других местах, это не следует путать со ссылками на C++

В Go карта является ссылочным типом. Это означает, что карта фактически находится в куче, а переменная является просто указателем на это.

Карта передается копией. Вы можете изменить локальную копию в своей функции, но это не будет отражено в области действия вызывающего.

Но, поскольку переменная map - это указатель на уникальную карту, находящуюся в куче, каждое изменение можно увидеть по любой переменной, которая указывает на ту же карту.

Эта статья может прояснить концепцию: https://www.ardanlabs.com/blog/2014/12/using-pointers-in-go.html.

Другие вопросы по тегам