Массивы
Чему вы научитесь:
- хранить несколько значений в массиве и обходить его в цикле;
- работать со срезами: создавать их через
makeи добавлять элементы черезappend; - отличать длину среза от ёмкости и понимать, когда два среза делят один массив;
- писать функции с переменным числом аргументов.
![Ряд из четырёх шкафчиков с номерами 0 1 2 3, шкафчик 1 открыт, на его дверце бирка a[1]](/course-covers/go-beginners/07-arrays-slices.webp)
Представьте ряд пронумерованных шкафчиков в раздевалке:
- шкафчики одинаковые и стоят вплотную;
- у каждого на дверце номер, и чтобы положить вещь, вы называете номер, а не придумываете шкафчику имя;
- ряд привинчен к полу: сколько шкафчиков поставили, столько и будет.
Пять оценок можно хранить в пяти переменных, но тогда сумма перечисляет пять имён, а цикл по ним пройти не может. Для нескольких значений одного типа в Go есть массив — ряд элементов фиксированной длины. Каждый элемент читают и меняют по номеру, который называют индексом:
package main
import "fmt"
func main() {
a := [3]int{10, 20, 30}
fmt.Println(a[0], a[2]) // 10 30
a[1] = 25
fmt.Println(a, len(a)) // [10 25 30] 3
}Давайте разберём пример по строкам:
a := [3]int{10, 20, 30}ставит ряд из трёх шкафчиков и кладёт в них три числа.a[0]иa[2]читают первый и последний шкафчики, поэтому строка печатает10 30.a[1] = 25меняет содержимое среднего шкафчика.- Последний
Printlnпечатает весь массив и его длину:[10 25 30] 3.
Запись и индексы
Запись [3]int читается как «массив из трёх int». В ней две части:
[3]— длина, число элементов;int— тип каждого элемента.
Индексы начинаются с нуля, поэтому у трёх элементов индексы 0, 1 и 2. Функция len возвращает длину массива, а fmt.Println печатает массив целиком в квадратных скобках.
Обход в цикле
Индекс может быть переменной, поэтому по массиву проходит цикл. Здесь range len(scores) перебирает индексы от 0 до 4:
scores := [5]int{4, 5, 3, 5, 4}
sum := 0
for i := range len(scores) {
sum += scores[i]
}
fmt.Println(sum) // 21На каждой итерации scores[i] читает очередную оценку и прибавляет её к sum. После пяти итераций программа печатает сумму 21.
Как создать массив
Массив можно получить тремя способами.
Литерал со всеми элементами
[3]int{10, 20, 30} перечисляет значения в фигурных скобках по порядку: первое попадает в индекс 0, второе — в 1. Так создают массив, когда все значения известны при написании кода. Компилятор не пропустит лишний элемент сверх длины.
Объявление через var
Массив, объявленный через var без значений, заполнен нулевыми значениями: var b [4]int содержит [0 0 0 0]. Так удобно заводить массив, который заполнят потом, например в цикле.
Неполный литерал
Литерал может перечислить не все элементы: остальные получают нулевое значение. Поэтому [5]int{1, 2} содержит [1 2 0 0 0]. Литерал заполняет первые индексы по порядку, а хвост остаётся нулями.
Длина — часть типа
Длина входит в тип, поэтому [3]int и [4]int — разные типы. Из этого следуют два ограничения:
- присвоить массив
[3]intпеременной типа[4]intнельзя:cannot use a (variable of type [3]int) as [4]int value in assignment; - изменить длину после создания тоже нельзя — ряд привинчен к полу.
Поэтому массив подходит для наборов, размер которых не меняется: три координаты точки, семь дней недели.
Массив копируется целиком
Переменная типа массива хранит все его элементы, как переменная типа int хранит число. Поэтому Go копирует массив в двух местах.
При присваивании
Присваивание b := a создаёт новый массив и копирует в него все элементы a. После этого у каждой переменной свои элементы, и запись в один массив не видна в другом:
a := [3]int{1, 2, 3}
b := a
b[0] = 9
fmt.Println(a, b) // [1 2 3] [9 2 3]Здесь аналогия со шкафчиками заканчивается. В жизни никто не строит второй ряд шкафчиков, чтобы отдать другу ключ. А в Go присваивание массива делает именно это: появляется второй ряд с копиями всех вещей. Поэтому b[0] = 9 меняет только второй ряд, и программа печатает [1 2 3] [9 2 3].
При вызове функции
Параметр типа [3]int получает копию массива, как параметр типа int получает копию числа. Функция, которая меняет элементы своего параметра, массив вызывающего кода не трогает. Чем длиннее массив, тем больше значений копирует каждое присваивание и каждый вызов.
На собеседовании этот факт часто проверяют задачей: массив присвоили другой переменной и изменили копию — что напечатает программа? Помните, что массив копируется целиком, и исходный остаётся прежним.
Частая ошибка
Длину массива берут из переменной:
n := 3
var a [n]int // ✗invalid array length nКомпилятор должен знать длину массива: это литерал или константа, например const n = 3. Значение переменной появляется только во время работы программы, поэтому для длины массива оно не годится.
Главное
- Массив — ряд элементов фиксированной длины; индексы идут от
0доlen(a) - 1. - Длина входит в тип:
[3]intи[4]int— разные типы, а длину задают литералом или константой. - Присваивание и передача в функцию копируют весь массив.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.