Массивы и срезы

Массивы фиксированы, а срезы — гибкие. Разбираем append, len, cap и срезы среза.

Срез (slice) — это «окно» поверх массива: гибкая, растущая последовательность элементов одного типа. На практике используют именно срезы.

Массивы: фиксированный размер

Массив в Go имеет размер прямо в типе: [3]int и [4]int — разные типы. Размер нельзя изменить, поэтому массивы используют редко.

package main

import "fmt"

func main() {
    var a [3]int      // [0 0 0]
    a[0] = 10
    b := [3]int{1, 2, 3}
    fmt.Println(a, b, len(b))
}

Вывод:

[10 0 0] [1 2 3] 3

Срезы: то, чем пользуются всегда

Срез объявляют без размера: []int. Создать можно литералом или функцией make. Элементы добавляют через встроенную append.

package main

import "fmt"

func main() {
    nums := []int{1, 2, 3}
    nums = append(nums, 4, 5)
    fmt.Println(nums, len(nums))
}

Вывод:

[1 2 3 4 5] 5

Важно: append может вернуть новый срез (если не хватило места), поэтому результат всегда присваивают обратно: nums = append(nums, ...).

len и cap

У среза две характеристики: len — сколько элементов сейчас, cap — сколько помещается без перевыделения памяти. Когда длина упирается в ёмкость, append выделяет больший массив и копирует данные.

package main

import "fmt"

func main() {
    s := make([]int, 0, 4) // len=0, cap=4
    fmt.Println(len(s), cap(s))
    s = append(s, 1, 2, 3)
    fmt.Println(len(s), cap(s))
}

Вывод:

0 4
3 4

Срезы среза

Из среза можно вырезать подсрез синтаксисом s[low:high]. Элемент с индексом low входит, high — нет (полуинтервал). Получившийся срез делит ту же память с исходным — изменение элемента видно в обоих.

package main

import "fmt"

func main() {
    s := []int{10, 20, 30, 40, 50}
    mid := s[1:4] // [20 30 40]
    fmt.Println(mid)

    mid[0] = 99      // меняем общий массив
    fmt.Println(s)   // изменение видно в исходном
}

Вывод:

[20 30 40]
[10 99 30 40 50]

Это частый источник недоразумений: подсрез — не копия. Если нужна независимая копия, используйте copy или append в новый срез.

Итог

  • Массив фиксирован ([3]int), срез ([]int) растёт через append.
  • Результат append всегда присваивайте обратно; lencap.
  • Подсрез s[a:b] делит память с исходным — это не копия.
Проверьте себя
1. Почему результат append всегда присваивают обратно?
Aappend не меняет срез
Bappend может вернуть новый срез при нехватке ёмкости
CТак требует синтаксис языка для всех функций
DИначе будет утечка памяти
2. Что показывает cap у среза?
AЧисло элементов сейчас
BСколько элементов помещается без перевыделения памяти
CРазмер одного элемента
DИндекс последнего элемента
3. Что делает подсрез s[1:4] с памятью исходного среза?
AСоздаёт независимую копию
BДелит ту же память — изменения видны в обоих
CУдаляет элементы из исходного
DЗамораживает исходный срез
Поддержать проект