Массив НЕ указатель (на какую-то область памяти) и внутри него НЕТ никакого указателя.
Это структура данных, у которой есть только:
адрес начала
длина
размер элементов
var values = [...]int{10, 20, 30}var value1 int = 10var value2 int = 20var value3 int = 30
Оба объявления “эквиваленты”, просто массив условно объединил эти три переменные в одну область памяти и хранит начало этой области памяти, длина и размер (в байтах, типа) элемента (каждого).
val := arr[5] // read from arrayarr[5] = 100 // write to the arraylen(arr) // get lengthcap(arr) // get capacityp := &arr // get pointer to the arrays := arr[1:4] // get slice from the array
Examples
Out of bounds
arr := [3]int{1, 2, 3}idx := 4arr[idx] // panic: runtime error: index out of range [4] with length 3arr[4] // compilation error: invalid argument: index 4 out of bounds [0:3]
В go есть BCE (Bound Check Elimination), который, по сути, нужен для предотвращения записи/чтения в соседние (чужие) ячейки памяти.
Negative indexes
arr := [3]int{1, 2, 3}idx := -1arr[idx] // panic: runtime error: index out of range [-1]arr[-1] // compilation error: invalid argument: index -1 (constant of type int) must not be negative
Length
arr := [10]int{}len(arr) // 10
Capacity
var arr [10]intcap(arr) // 10
Array comparison
arr1 := [...]int{1, 2, 3}arr2 := [...]int{1, 2, 3}// Except array whose element types ar incomparable typesarr1 == arr2 // truearr1 != arr2 // false// But [<, <=, >, >=] --> compilation error: invalid operation: arr1 > arr2 (operator > not defined on array)
Empty array
var arr [10]bytefmt.Println(unsafe.Sizeof(arr)) // 10 (size in bytes)
Zero array
var arr [0]bytefmt.Println(unsafe.Sizeof(arr)) // 0 (size in bytes)
Negative array
var arr [-1]int // compilation error: invalid array length -1 (untyped int constant)
Компилятору нужно при компиляции понимать, какого размера будет массив, поэтому размер может быть только константой или константным значением (константы известны компилятору на этапе компиляции).
make()
_ = make([10]) // compilation error: [10]int{} is not a type
append()
_ = append([10]int{}, 10) // compilation error: first argument to append must be a slice; have [10]int{} (value of type [10]int)
Array indexing mechanism in Go
arr[idx] = *(arr + idx * elemSize)
arr — указатель на начало idx — индекс элемента, к которому обращаешься elemSize — размер элемента, к которому обращаешься
Длина массива (как и размер элементов) является частью типа (не хранится рядом с данными).
Длина массива — информация, которая доступна у типа данных массива; другими словами, длина “зашита” в типе данных.
arr1 := [3]int{10, 20, 30}arr2 := [2]int{10, 20}arr1 = arr2 // compilation error: cannot use arr2 (variable of type [2]int) as [3]int value in assignment
arr2 нельзя присвоить arr1 потому, что это разные типы данных. 3 в [3]int — часть типа данных (ошибка компиляции как раз на это и указывает).
Вызов len() для массива вычисляется на этапе компиляции
Ибо длина известна компилятору на этапе компиляции и значение переменной length ниже будет вычислена уже на этапе компиляции (а не в рантайме):
arr := [4]int{1, 2, 3, 4}length := len(arr) // 4
Iteration over an array
C style
for i := 0; i < len(arr); i++ { // ...}
Используя С style loops стоить помнить про Off-by-one error. range как раз позволит избежать этой ошибки.
range
for _, val := range arr { // ...}
for idx, _ := range arr { // ...}// или просто:for idx := range arr { // ...}
for _, _ := range arr { // ...}// или просто:for range arr { // ...}
Examples
Modifying array elements during iteration
values := [...]int{100, 200, 300}// Also relevant for slicesfor idx, val := range values { val += 50 fmt.Printf("#1: %p #2: %p\n", unsafe.Pointer(&val), unsafe.Pointer(&values[idx]))}fmt.Printf("values: %v\n", values)/* #1: 0xc000010180 #2: 0xc00001c1b0 #1: 0xc0000101a0 #2: 0xc00001c1b8 #1: 0xc0000101a8 #2: 0xc00001c1c0 values: [100 200 300]*/
Как видно, val += 50 в теле цикла не изменил элементы values.
Так происходит потому, что в Go всё копируется, то есть, val += 50 изменяет локальную копию элемента, а не сам элемент. В выводе видно, что адрес val никак не связан с адресом элементов values.
Еще по выводу видно, что val создается не единожды (адрес в каждой итерации — разный).
В Go 1.22+ уникальный экземпляр будет создан для каждой итерационной переменной в каждой итерации цикла.
Correct way to modify array elements during iteration
Самый простой вариант — использование индексов для модификации элемента:
for idx := range values { values[idx] += 50}
Можно воспользоваться и указателями в некоторых случаях, но стоит учесть, что этот вариант будет медленее первого варианта с индексами:
type account struct { balance int}accounts := [...]*account{ {balance: 100}, {balance: 200}, {balance: 300},}for _, a := range accounts { a.balance++}