Go. Структуры данных
Массивы: объявление, инициализация, базовые операции
Введение в массивы
Представьте, что у вас есть набор одинаковых коробок, расположенных в ряд. Каждая коробка пронумерована, и в каждой можно что-то хранить. Массивы в Go работают точно так же — это набор элементов одного типа с доступом по индексу.
Давайте разберёмся, что такое массивы, как их создавать и использовать в Go.
Что такое массив?
Массив — это упорядоченная коллекция элементов одного типа с фиксированной длиной. В отличие от некоторых других языков, в Go массивы имеют определённую длину, которая является частью их типа.
package main

import "fmt"

func main() {
    // Объявление массива из 5 целых чисел
    var numbers [5]int
    
    // Присваивание значений
    numbers[0] = 10
    numbers[1] = 20
    numbers[2] = 30
    numbers[3] = 40
    numbers[4] = 50
    
    fmt.Println("Массив:", numbers)        // [10 20 30 40 50]
    fmt.Println("Первый элемент:", numbers[0]) // 10
    fmt.Println("Длина массива:", len(numbers)) // 5
}
Что здесь происходит?
  • var numbers [5]int — объявляем массив из 5 элементов типа int.
  • numbers[0] — обращаемся к первому элементу (индексация начинается с 0).
  • len(numbers) — получаем длину массива.
Объявление и инициализация массивов
Способы объявления массивов
1. Объявление с нулевыми значениями
Когда мы объявляем массив без явной инициализации, Go автоматически присваивает каждому элементу нулевое значение для его типа. Для чисел это 0, для строк — пустая строка "" , для булевых значений — false. Это гарантирует, что массив всегда находится в предсказуемом состоянии, и вам не нужно беспокоиться о неинициализированных данных.
package main

import "fmt"

func main() {
    // Массив целых чисел (все элементы равны 0)
    var numbers [5]int
    fmt.Println("Массив чисел:", numbers) // [0 0 0 0 0]
    
    // Массив строк (все элементы равны пустой строке)
    var names [3]string
    fmt.Println("Массив строк:", names) // [   ] (три пустые строки)
    
    // Массив булевых значений (все элементы равны false)
    var flags [4]bool
    fmt.Println("Массив флагов:", flags) // [false false false false]
}
2. Инициализация при объявлении
package main

import "fmt"

func main() {
    // Полная инициализация
    numbers := [5]int{10, 20, 30, 40, 50}
    fmt.Println("Полная инициализация:", numbers)
    
    // Частичная инициализация (остальные элементы будут нулевыми)
    partial := [5]int{10, 20} // [10 20 0 0 0]
    fmt.Println("Частичная инициализация:", partial)
    
    // Инициализация с указанием индексов
    indexed := [5]int{1: 10, 3: 30} // [0 10 0 30 0]
    fmt.Println("Инициализация с индексами:", indexed)
}
3. Автоматическое определение длины
package main

import "fmt"

func main() {
    // Go автоматически определит длину массива
    numbers := [...]int{10, 20, 30, 40, 50}
    fmt.Println("Автоматическая длина:", len(numbers)) // 5
    
    // Пустой массив
    empty := [...]int{}
    fmt.Println("Длина пустого массива:", len(empty)) // 0
}
Базовые операции с массивами
Доступ к элементам
package main

import "fmt"

func main() {
    numbers := [5]int{10, 20, 30, 40, 50}
    
    // Чтение элементов
    fmt.Println("Первый элемент:", numbers[0])   // 10
    fmt.Println("Последний элемент:", numbers[4]) // 50
    
    // Изменение элементов
    numbers[2] = 35
    fmt.Println("После изменения:", numbers) // [10 20 35 40 50]
    
    // Использование переменной в качестве индекса
    index := 1
    fmt.Printf("Элемент с индексом %d: %d\n", index, numbers[index])
}
Важно

Выход за границы массива вызывает панику во время выполнения. Если вы обратитесь к элементу с индексом, равным или превышающим длину массива, программа завершится с ошибкой panic: runtime error: index out of range. Всегда проверяйте, что индекс находится в допустимом диапазоне: 0 <= index < len (array).
Перебор массивов
1. Классический цикл for
package main

import "fmt"

func main() {
    numbers := [5]int{10, 20, 30, 40, 50}
    
    fmt.Println("Перебор с индексами:")
    for i := 0; i < len(numbers); i++ {
        fmt.Printf("Индекс %d: значение %d\n", i, numbers[i])
    }
}
2. Цикл for range
package main

import "fmt"

func main() {
    numbers := [5]int{10, 20, 30, 40, 50}
    
    // Перебор с индексом и значением
    fmt.Println("Перебор с for range:")
    for index, value := range numbers {
        fmt.Printf("Индекс %d: значение %d\n", index, value)
    }
    
    // Только значения (игнорируем индекс)
    fmt.Println("\nТолько значения:")
    for _, value := range numbers {
        fmt.Println(value)
    }
    
    // Только индексы
    fmt.Println("\nТолько индексы:")
    for index := range numbers {
        fmt.Println(index)
    }
}
Сравнение массивов
package main

import "fmt"

func main() {
    // Массивы можно сравнивать оператором ==
    // (только если все элементы сравнимы)
    arr1 := [3]int{1, 2, 3}
    arr2 := [3]int{1, 2, 3}
    arr3 := [3]int{1, 2, 4}
    
    fmt.Printf("arr1 == arr2: %t\n", arr1 == arr2) // true
    fmt.Printf("arr1 == arr3: %t\n", arr1 == arr3) // false
}
Важно

Массивы разной длины нельзя сравнивать, так как длина входит в понятие типа. Переменные-массивы разной длины являются переменными с разными типами, поэтому попытка сравнения [3]int и [4]int приведёт к ошибке компиляции.
Передача массивов в функции
package main

import "fmt"

// Массивы передаются по значению (создаётся полная копия)
func modifyArray(arr [5]int) {
    fmt.Printf("Внутри функции (до изменения): %v\n", arr)
    arr[0] = 999 // ← Это изменение затронет только копию!
    fmt.Printf("Внутри функции (после изменения): %v\n", arr)
}

// Для изменения оригинала нужно передавать указатель
func modifyArrayPtr(arr *[5]int) {
    fmt.Printf("Внутри функции (до изменения): %v\n", *arr)
    arr[0] = 999 // ← Это изменение затронет оригинал
    fmt.Printf("Внутри функции (после изменения): %v\n", *arr)
}

func main() {
    numbers := [5]int{1, 2, 3, 4, 5}
    
    fmt.Println("=== Передача по значению ===")
    fmt.Printf("До вызова modifyArray: %v\n", numbers)
    modifyArray(numbers)
    fmt.Printf("После вызова modifyArray: %v\n", numbers) // ← Не изменилось!
    
    fmt.Println("\n=== Передача по указателю ===")
    fmt.Printf("До вызова modifyArrayPtr: %v\n", numbers)
    modifyArrayPtr(&numbers)
    fmt.Printf("После вызова modifyArrayPtr: %v\n", numbers) // ← Изменилось!
}
Важно

Массивы передаются в функции по значению, что означает создание полной копии всех элементов. Для изменения оригинального массива передавайте указатель *[N]Type.
Многомерные массивы
В Go можно создавать многомерные массивы — массивы массивов. Чаще всего используются двумерные массивы для представления матриц, таблиц или игровых полей.
package main

import "fmt"

func main() {
    // Двумерный массив (матрица)
    var matrix [3][3]int
    
    // Заполнение матрицы
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            matrix[i][j] = i*3 + j + 1
        }
    }
    
    fmt.Println("Матрица:")
    for _, row := range matrix {
        fmt.Println(row)
    }
    
    // Инициализация многомерного массива
    matrix2 := [2][2]int{
        {1, 2},
        {3, 4},
    }
    
    fmt.Println("\nВторая матрица:")
    for _, row := range matrix2 {
        fmt.Println(row)
    }
}
Важные особенности многомерных массивов:
  • Размеры всех измерений должны быть известны на этапе компиляции
  • Каждое измерение имеет фиксированный размер
Когда использовать массивы?
Используйте массивы, когда:
  • Размер коллекции известен заранее и не изменится
  • Нужна максимальная производительность доступа к элементам
  • Работаете с низкоуровневыми операциями (например, сетевые пакеты)
Заключение
Теперь вы знаете основы работы с массивами в Go. Давайте подведём итоги.
Ключевые моменты:
  • Массивы имеют фиксированную длину, которая является частью их типа
  • Индексация массивов начинается с 0
  • Массивы передаются в функции по значению (создаётся полная копия)
  • Для изменения массива в функции нужно передавать указатель
  • Массивы одинаковой длины можно сравнивать оператором == (если элементы сравнимы)
  • Выход за границы массива вызывает панику во время выполнения
  • Используйте len () для получения длины массива
  • Циклические ссылки
    Не вызывают проблем — GC корректно их обрабатывает
Что дальше?
Теперь вы готовы погрузиться глубже в работу массивов или перейти к изучению других структур данных в Go:
  • Массивы: как работает с памятью.
  • Слайсы: создание, срезы, встроенные функции (append, copy).
  • Слайсы: внутренее устройство.
Поздравляю, вы только что освоили основы работы с массивами в Go!
Теперь вы можете эффективно использовать их в своих программах.