> Когда происходит копирование элементов при передаче массива или слайса в функцию в Go (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Wildberries, Avito
Стек: Go
> Пример ответа
Короткий ответ
В Go при передаче массива в функцию происходит полное копирование всех элементов - массив передаётся по значению. Слайс передаётся как структура из трёх полей (указатель на массив, длина, ёмкость), при этом сами элементы не копируются - копируется только заголовок слайса. Изменение элементов через слайс внутри функции затронет исходный массив, а изменение длины или ёмкости - нет.
Подробное объяснение
Массив в Go - это значение фиксированной длины. Когда вы передаёте массив в функцию, компилятор создаёт полную копию всех элементов в стеке или куче. Это означает, что любые изменения внутри функции не влияют на исходный массив. Стоимость копирования пропорциональна размеру массива.
Слайс - это дескриптор, который содержит:
- указатель на первый элемент массива (или nil)
- длину (len)
- ёмкость (cap)
При передаче слайса в функцию копируется только этот дескриптор (24 байта на 64-битной платформе). Указатель внутри копии ссылается на тот же самый массив, поэтому изменения элементов через индексацию или срезы будут видны вызывающему коду.
Важный нюанс: если внутри функции вы делаете append и это приводит к превышению ёмкости, создаётся новый массив, и указатель в локальной копии слайса начинает ссылаться на него. Исходный слайс при этом остаётся неизменным.
На практике
Для senior-позиции важно понимать, когда копирование действительно происходит:
- Передача массива по значению - всегда копия, даже если массив огромный. Для больших массивов это может быть дорого.
- Передача слайса - копируется только заголовок, но не данные. Это дёшево и обычно предпочтительно.
- Если нужно гарантированно изменить массив в функции - передавайте указатель на массив
*[N]T. - Если нужно изменить длину слайса внутри функции и сохранить это изменение - возвращайте новый слайс или передавайте указатель на слайс
*[]T.
На практике в Go почти всегда используют слайсы, а не массивы, именно из-за дешёвой передачи и гибкости.
Пример кода
GOpackage mainimport "fmt"func modifyArray(arr [3]int) {arr[0] = 100 // изменение не повлияет на оригинал}func modifySlice(s []int) {s[0] = 200 // изменение повлияет на оригиналs = append(s, 300) // это создаст новый массив, если ёмкость превышена// и не повлияет на длину исходного слайса}func main() {arr := [3]int{1, 2, 3}modifyArray(arr)fmt.Println(arr) // [1 2 3]slice := []int{1, 2, 3}modifySlice(slice)fmt.Println(slice) // [200 2 3] - длина не изменилась, но элемент изменён}
Как отвечать на собеседовании
Начните с чёткого разграничения: массив - значение, слайс - ссылочная структура. Затем объясните механику: копирование массива целиком, копирование заголовка слайса. Обязательно упомяните поведение append при превышении ёмкости - это ключевой момент для senior. Приведите пример, показывающий разницу. Если спросят про производительность - скажите, что передача слайса O(1) по времени и памяти, а массива - O(n). Можно добавить, что в реальном коде массивы редко передают напрямую, обычно используют слайсы или указатели.
Что проверяет интервьюер
Интервьюер проверяет:
- понимание модели памяти в Go
- знание внутреннего устройства слайса
- умение предсказывать побочные эффекты при передаче данных
- понимание trade-off между копированием и ссылочной семантикой
- способность объяснить поведение
appendи связанные с ним подводные камни
Для senior это базовая проверка - ожидается мгновенный и точный ответ без раздумий.
Типичные ошибки
- Утверждение, что слайс передаётся по ссылке - это неверно, передаётся копия заголовка
- Забывают, что изменение элементов через слайс видно вызывающему коду
- Не учитывают, что
appendможет создать новый массив - Путают массивы и слайсы в контексте копирования
- Думают, что передача слайса всегда безопасна от копирования - это верно только для элементов, но не для заголовка
> Похожие задачи по backend
Какую функцию выполняет транзакция
Где в памяти живут глобальные переменные в Go?
Как используется WaitGroup для ожидания завершения горутин в Go
Для чего используются NoSQL решения?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью