← Назад к вопросам

Как проходит итерация по массиву?

1.0 Junior🔥 132 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Итерация по массивам в Go: методы и внутренняя механика

В Go итерация по массивам может осуществляться несколькими способами, каждый из которых имеет свои особенности, преимущества и случаи применения. Рассмотрим основные методы подробно.

1. Классический цикл for с индексом

Наиболее базовый и контролируемый способ — использование цикла for с доступом к элементам по индексу:

package main

import "fmt"

func main() {
    arr := [5]int{10, 20, 30, 40, 50}
    
    // Стандартная итерация по индексам
    for i := 0; i < len(arr); i++ {
        fmt.Printf("Индекс: %d, Значение: %d\n", i, arr[i])
    }
    
    // Более идиоматичный вариант с range
    for i := range arr {
        fmt.Printf("Индекс: %d, Значение: %d\n", i, arr[i])
    }
}

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

  • Прямой доступ к индексу элемента
  • Возможность модификации исходного массива
  • Полный контроль над логикой итерации (можно пропускать элементы, менять шаг)

2. Итерация с помощью range для значений

Оператор range предоставляет наиболее идиоматичный способ итерации в Go, возвращая индекс и значение элемента:

package main

import "fmt"

func main() {
    arr := [3]string{"Яблоко", "Банан", "Апельсин"}
    
    for index, value := range arr {
        fmt.Printf("arr[%d] = %s\n", index, value)
    }
    
    // Если нужен только индекс
    for index := range arr {
        fmt.Printf("Индекс: %d\n", index)
    }
    
    // Если нужно только значение (используем пустой идентификатор)
    for _, value := range arr {
        fmt.Printf("Значение: %s\n", value)
    }
}

Важная особенность: При использовании range значение элемента копируется в переменную value. Это означает, что изменения value не влияют на исходный массив:

arr := [3]int{1, 2, 3}
for _, v := range arr {
    v = v * 2  // Эта операция не меняет исходный массив!
}
fmt.Println(arr)  // Вывод: [1 2 3]

3. Итерация по указателю на массив

При работе с большими массивами эффективнее использовать указатели для избежания копирования:

package main

import "fmt"

func main() {
    arr := [4]float64{1.1, 2.2, 3.3, 4.4}
    pArr := &arr  // Указатель на массив
    
    // Итерация по указателю с range
    for i, v := range pArr {
        fmt.Printf("pArr[%d] = %.1f\n", i, v)
        // v здесь - всё ещё копия значения
    }
    
    // Модификация через указатель
    for i := range pArr {
        pArr[i] = pArr[i] * 2  // Изменяем исходный массив
    }
    fmt.Println(arr)  // Вывод: [2.2 4.4 6.6 8.8]
}

4. Внутренняя механика и производительность

С точки зрения производительности, итерация по массивам в Go имеет важные характеристики:

  1. Массивы в Go — это значения, а не ссылки. При передаче массива в функцию происходит полное копирование.
  2. Range оптимизирован компилятором и обычно работает быстрее или сравнимо с ручным циклом for.
  3. Компилятор Go может проводить оптимизацию границ (bounds check elimination) для циклов, что повышает производительность.
// Пример с бенчмарком итерации
func BenchmarkRange(b *testing.B) {
    var arr [1000]int
    for i := 0; i < b.N; i++ {
        sum := 0
        for _, v := range arr {
            sum += v
        }
        _ = sum
    }
}

5. Особые случаи и практические рекомендации

Итерация по части массива:

arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

// Итерация по срезу массива
for i, v := range arr[3:7] {
    fmt.Printf("Срез[%d] = %d\n", i, v)
}

Итерация с пропуском элементов:

for i := 0; i < len(arr); i += 2 {
    fmt.Println(arr[i])  // Только чётные индексы
}

Практические рекомендации:

  1. Используйте range для простых итераций — это идиоматично и безопасно
  2. При необходимости модификации элементов используйте доступ по индексу
  3. Для больших массивов работайте через указатели или срезы
  4. Помните, что массивы имеют фиксированный размер, в отличие от срезов

Важное отличие от срезов: При итерации по массиву range определяет границы на этапе компиляции, тогда как для срезов проверка границ происходит во время выполнения.

Заключение

Итерация по массивам в Go сочетает простоту использования с гибкостью. Range является предпочтительным способом для большинства случаев благодаря своей безопасности и читаемости, тогда как классический цикл for предоставляет больше контроля для сложных сценариев. Понимание различий между передачей по значению и по ссылке, а также особенностей копирования значений при использовании range — ключ к написанию эффективного и корректного кода на Go.