В большинстве языков (например, 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: Извините, если это глупый вопрос, я совершенно не знаком с Голангом и пытаюсь хорошо понять основы.
Объяснение довольно простое: нет ни одного массив объявлено или использовано явно в вашем коде выше. Ваш tab
локальная переменная и tab
параметр ломтики.
В Go длина массива является частью типа, например, [3]int
(это верно до такой степени, что, например, [2]int
а также [3]int
2 разных / разных типов массивов). Если длина отсутствует (либо явно, как [2]int
или неявное, как в составной буквальный [...]int{1, 2, 3}
), то это не тип массива, а тип среза.
Да, когда вы читаете, значение массива означает все его элементы, и при передаче (или назначении) все его элементы копируются. Однако фрагменты — это просто небольшие дескрипторы, заголовки, описывающие непрерывный раздел массива; и когда срезы передаются (или назначаются), копируется только этот заголовок (включая указатель), который будет указывать на тот же базовый массив. И поэтому, если вы измените элементы копии фрагмента, изменения будут отражены в исходном фрагменте, поскольку существует только один резервный массив, содержащий элементы.
Если вы хотите точно знать, что находится в заголовке слайса, вы можете проверить reflect.SliceHeader
тип: это struct
содержащий указатель на первый элемент среза, длину и емкость среза.
Пожалуйста, прочитайте следующие сообщения в блоге, которые объясняют это в деталях:
Go Slices: использование и внутренности
Массивы, срезы (и строки): механика «добавления»
Также см. Эти связанные вопросы для более подробной информации:
То, что вы определяете, не array
но slice
массива, который передается по ссылке, как указано в документации golang. Проверьте этот ссылка на сайт.