Golang передает массивы в функцию и модифицирует ее

В большинстве языков (например, c ++) передача массивов приводит к неявной передаче его по ссылке, поэтому любые изменения переданного массива в функции приводят к изменению исходного. Я изучаю Голанг, и в книге «Язык программирования Go» Алана А.А. Донован и Брайан У. Керниган. Говорят, что его поведение отличается от других языков — он неявно передает массив по ссылке.

Это немного смущает меня — не значит ли это, что передача массива без ссылки не должна изменять сам массив? Позвольте мне проиллюстрировать это:

func main() {
tab := []int{1, 2, 3}
fmt.Println(tab)
// Results in [1 2 3]
reverse(tab)
fmt.Println(tab)
// Results in [3 2 1]
}

func reverse(tab []int) {
for i, j := 0, len(tab)-1; i < j; i, j = i+1, j-1 {
tab[i], tab[j] = tab[j], tab[i]
}
}

В приведенном выше коде массив не передается по ссылке, но функция reverse изменяет исходную функцию, поэтому он работает в некоторой степени так, как это делает программа на C ++. Может ли кто-нибудь объяснить мне разницу?

PS: Извините, если это глупый вопрос, я совершенно не знаком с Голангом и пытаюсь хорошо понять основы.

1

Решение

Объяснение довольно простое: нет ни одного массив объявлено или использовано явно в вашем коде выше. Ваш tab локальная переменная и tab параметр ломтики.

В Go длина массива является частью типа, например, [3]int (это верно до такой степени, что, например, [2]int а также [3]int 2 разных / разных типов массивов). Если длина отсутствует (либо явно, как [2]int или неявное, как в составной буквальный [...]int{1, 2, 3}), то это не тип массива, а тип среза.

Да, когда вы читаете, значение массива означает все его элементы, и при передаче (или назначении) все его элементы копируются. Однако фрагменты — это просто небольшие дескрипторы, заголовки, описывающие непрерывный раздел массива; и когда срезы передаются (или назначаются), копируется только этот заголовок (включая указатель), который будет указывать на тот же базовый массив. И поэтому, если вы измените элементы копии фрагмента, изменения будут отражены в исходном фрагменте, поскольку существует только один резервный массив, содержащий элементы.

Если вы хотите точно знать, что находится в заголовке слайса, вы можете проверить reflect.SliceHeader тип: это struct содержащий указатель на первый элемент среза, длину и емкость среза.

Пожалуйста, прочитайте следующие сообщения в блоге, которые объясняют это в деталях:

Go Slices: использование и внутренности

Массивы, срезы (и строки): механика «добавления»

Также см. Эти связанные вопросы для более подробной информации:

Почему массивы в Go?

Срезы Голанга проходят по значению?

4

Другие решения

То, что вы определяете, не array но slice массива, который передается по ссылке, как указано в документации golang. Проверьте этот ссылка на сайт.

1

По вопросам рекламы [email protected]