Механизм перераспределения памяти в срезах (Slices Reallocation)
Определения
Len (Length) — текущее количество элементов в срезе. Cap (Capacity) — максимальное количество элементов, которое может вместить нижележащий массив (underlying array) без выделения новой памяти. Underlying Array — фактическая область памяти, где хранятся данные. Срез — это лишь дескриптор (указатель) на этот массив.
Поведение функции append()
Функция append реализует логику динамического расширения: Наличие емкости (Len < Cap): Новый элемент записывается в следующую свободную ячейку существующего массива. Все срезы, указывающие на этот же массив, «видят» изменения в общей области памяти (если их Cap позволяет до нее дотянуться). Дефицит емкости (Len == Cap): Среда исполнения Go выделяет новый, более крупный блок памяти (обычно в 2 раза больше предыдущего для небольших срезов). Старые данные копируются в новый массив. Возвращаемый срез теперь указывает на новый адрес в памяти. Разрыв связи: Старые срезы продолжают указывать на прежний (маленький) массив. Изменения в новом срезе больше не влияют на старые.
Оптимизация с помощью make()
Чтобы избежать лишних аллокаций (копирований) и непредсказуемого разрыва связей, используется функция make. Она позволяет заранее забронировать нужный объем памяти. Синтаксис:
// make([]Тип, Длина, Емкость)
users := make([]string, 0, 100)
Здесь len = 0 (срез пустой). Но cap = 100 (массив в памяти уже зарезервирован под 100 элементов). Результат: Первые 100 вызовов append будут работать молниеносно и гарантированно не изменят адрес среза в памяти.
Для повторения
Срез — это ссылка. Меняешь элемент в срезе — меняешь его в массиве. append — это риск. Если массив переполнен, срез «переедет» на новый адрес. copy() — это безопасность. Если нужна полная независимость данных, создавай новый срез через make и копируй данные вручную.