Копирование массивов и многомерные массивы

1. Копирование массивов (глубокое копирование)

Основной принцип

В Go присваивание массива создаёт полную копию всех элементов, а не ссылку на исходный массив.

Пример

original := [3]int{1, 2, 3}
copyArr := original  // Полная копия всех элементов

copyArr[0] = 999

fmt.Println(original)  // [1, 2, 3]   — не изменился!
fmt.Println(copyArr)   // [999, 2, 3] — изменилась копия

Проверка независимости через адреса

fmt.Printf("&original: %p\n", &original)  // 0xc000012078
fmt.Printf("&copyArr:  %p\n", &copyArr)   // 0xc000012090

// Адреса разные → это два разных объекта в памяти

Вывод: Изменение копии не влияет на оригинал — массивы полностью независимы.


2. Многомерные массивы

Определение

Многомерный массив — массив размерности ≥ 2 (двумерные, трёхмерные и т.д.).

Практическое применение

  • Двумерные массивы — таблицы, матрицы, игровые поля
  • Трёхмерные и выше — редко используются (специфичные задачи: математика, физика, 3D-графика)

3. Двумерные массивы (матрицы)

Синтаксис объявления

// [строки][столбцы]тип
var matrix [4][3]int  // 4 строки, 3 столбца

Важно: Порядок размерностей — сначала строки, потом столбцы.

Инициализация

// Способ 1: при объявлении
grid := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

// Способ 2: построчная
grid := [3][2]int{
    {1, 2},
    {3, 4},
    {5, 6},
}

4. Доступ к элементам двумерного массива

Индексация: [строка][столбец]

matrix := [3][2]int{
    {1, 2},  // строка 0
    {3, 4},  // строка 1
    {5, 6},  // строка 2
}

fmt.Println(matrix[0][0])  // 1 — элемент (строка 0, столбец 0)
fmt.Println(matrix[2][0])  // 5 — элемент (строка 2, столбец 0)
fmt.Println(matrix[1][1])  // 4 — элемент (строка 1, столбец 1)

Визуализация индексов

     столбец 0   столбец 1
строка 0:  [0][0]    [0][1]
строка 1:  [1][0]    [1][1]
строка 2:  [2][0]    [2][1]

5. Получение размеров двумерного массива

Количество строк

grid := [3][2]int{
    {1, 2}, 
    {3, 4}, 
    {5, 6}
}

rows := len(grid)  // 3 — количество строк
fmt.Println(rows)

Количество столбцов

cols := len(grid[0])  // 2 — количество элементов в первой строке
fmt.Println(cols)

// grid[1] — это отдельный массив (вторая строка)
fmt.Println(len(grid[1]))  // 2

Концепция: Двумерный массив = массив массивов.


6. Перебор двумерного массива (вложенные циклы)

Классический for

grid := [3][2]int{
    {1, 2},
    {3, 4},
    {5, 6},
}

for row := 0; row < len(grid); row++ {
    for col := 0; col < len(grid[row]); col++ {
        fmt.Printf("grid[%d][%d] = %d\n", row, col, grid[row][col])
    }
}

Вывод:

grid[0][0] = 1
grid[0][1] = 2
grid[1][0] = 3
grid[1][1] = 4
grid[2][0] = 5
grid[2][1] = 6

С использованием for range

for rowIndex, row := range grid {
    for colIndex, value := range row {
        fmt.Printf("[%d][%d] = %d\n", rowIndex, colIndex, value)
    }
}

7. Выход за границы в многомерных массивах

Пример ошибок

grid := [3][2]int{
    {1, 2},
    {3, 4}, 
    {5, 6}
}

// ❌ Нет 4-й строки (индексы 0-2):
// grid[4][1] = 10
// panic: runtime error: index out of range [4] with length 3

// ❌ Нет 3-го столбца (индексы 0-1):
// grid[0][3] = 99
// panic: runtime error: index out of range [3] with length 2

Правило: Проверяйте границы обеих размерностей.


8. Трёхмерные и высокоразмерные массивы

Синтаксис

// [глубина][строки][столбцы]
var cube [2][3][2]int  // "Кубик" 2×3×2

Инициализация

cube := [2][3][2]int{
    {  // Слой 0
        {1, 2},
        {3, 4},
        {5, 6},
    },
    {  // Слой 1
        {7, 8},
        {9, 10},
        {11, 12},
    },
}

fmt.Println(cube[0][1][1])  // 4

Практическая заметка: Трёхмерные массивы сложны для отладки и редко нужны в реальных задачах.


9. Итоги: Массивы в Go

Ключевые особенности

Свойство Описание
Размер Фиксированный, часть типа: [3]int[5]int
Копирование Глубокое (по значению), не по ссылке
Индексация С 0, для всех размерностей
Производительность Эффективны (размер известен заранее)
Ограничения Нельзя изменить размер после создания

Преимущества

✅ Известный размер → эффективное выделение памяти
✅ Безопасность: копии независимы
✅ Простота для табличных данных (2D)

Недостатки

❌ Фиксированная длина → нет динамического роста
❌ Копирование может быть дорогим (большие массивы)
❌ Для динамических коллекций нужны срезы (slices)


10. Переход к срезам

Проблема массивов:

arr := [3]int{1, 2, 3}
// Нужно добавить 4-й элемент? Придётся:
// 1. Создать новый массив [4]int
// 2. Скопировать все элементы
// 3. Добавить новый элемент

Решение: Использовать срезы (slices) — динамические обёртки над массивами.

Следующий шаг: Изучение срезов для работы с коллекциями переменного размера.


11. Практические советы

  1. Для фиксированных данных (дни недели, месяцы) → массивы
  2. Для динамических коллекций → срезы
  3. Двумерные структуры → подумайте, нужна ли вам именно матрица или можно обойтись срезом структур
  4. Всегда тестируйте новые конструкции через fmt.Println() и эксперименты

Итоговая формула:

Массив = Фиксированный размер + Глубокое копирование + Индексация с 0