// Упрощённое представление структуры слайса в Go
type sliceHeader struct {
Pointer uintptr // ← указатель на базовый массив
Length int // ← текущая длина слайса
Capacity int // ← ёмкость базового массива
} package main
import (
"fmt"
"unsafe"
)
func main() {
// Создаём слайс
numbers := []int{10, 20, 30, 40, 50}
fmt.Printf("Слайс: %v\n", numbers)
fmt.Printf("Длина: %d\n", len(numbers))
fmt.Printf("Ёмкость: %d\n", cap(numbers))
// Получаем доступ к внутреннему представлению (только для демонстрации!)
header := (*[3]uintptr)(unsafe.Pointer(&numbers))
fmt.Printf("Указатель: %d\n", header[0])
fmt.Printf("Длина: %d\n", header[1])
fmt.Printf("Ёмкость: %d\n", header[2])
} package main
import "fmt"
func main() {
// Создаём массив
array := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Printf("Исходный массив: %v\n", array)
// Создаём разные слайсы из одного массива
slice1 := array[2:6] // [2, 3, 4, 5]
slice2 := array[0:4] // [0, 1, 2, 3]
slice3 := array[4:] // [4, 5, 6, 7]
fmt.Printf("slice1: %v (len=%d, cap=%d)\n", slice1, len(slice1), cap(slice1))
fmt.Printf("slice2: %v (len=%d, cap=%d)\n", slice2, len(slice2), cap(slice2))
fmt.Printf("slice3: %v (len=%d, cap=%d)\n", slice3, len(slice3), cap(slice3))
// Изменяем элемент в одном из слайсов
slice1[1] = 999 // изменяем элемент с индексом 1 в slice1 (это array[3])
fmt.Printf("\nПосле изменения slice1[1] = 999:\n")
fmt.Printf("slice1: %v\n", slice1) // [2, 999, 4, 5]
fmt.Printf("slice2: %v\n", slice2) // [0, 1, 2, 999] ← тоже изменился!
fmt.Printf("slice3: %v\n", slice3) // [4, 5, 6, 7]
fmt.Printf("array: %v\n", array) // [0, 1, 2, 999, 4, 5, 6, 7]
} package main
import "fmt"
func processData() {
// Создаём большой слайс
bigData := make([]byte, 1_000_000) // 1MB данных
// Берём маленький срез
smallSlice := bigData[:100] // только 100 байт
// Но smallSlice удерживает весь bigData в памяти!
process(smallSlice)
// bigData не будет собран сборщиком мусора, пока smallSlice существует
}
func process(data []byte) {
fmt.Printf("Обрабатываем %d байт\n", len(data))
}
func main() {
processData()
} func processData() {
bigData := make([]byte, 1_000_000)
// Создаём полную копию нужной части
smallSlice := make([]byte, 100)
copy(smallSlice, bigData[:100])
process(smallSlice)
// Теперь bigData может быть собран сборщиком мусора
} package main
import "fmt"
func main() {
// Вариант 1: make([]тип, длина)
// Создаёт слайс с length = capacity
slice1 := make([]int, 5)
fmt.Printf("slice1: %v (len=%d, cap=%d)\n", slice1, len(slice1), cap(slice1))
// Вывод: slice1: [0 0 0 0 0] (len=5, cap=5)
// Вариант 2: make([]тип, длина, ёмкость)
// Создаёт слайс с заданными length и capacity
slice2 := make([]int, 3, 10)
fmt.Printf("slice2: %v (len=%d, cap=%d)\n", slice2, len(slice2), cap(slice2))
// Вывод: slice2: [0 0 0] (len=3, cap=10)
// Все элементы инициализируются нулевыми значениями типа
slice3 := make([]string, 2, 5)
fmt.Printf("slice3: %v (len=%d, cap=%d)\n", slice3, len(slice3), cap(slice3))
// Вывод: slice3: [ ] (len=2, cap=5)
} // Если заранее знаем количество элементов
slice := make([]int, 0, 1000) // length=0, capacity=1000
for i := 0; i < 1000; i++ {
slice = append(slice, i) // Реаллокаций не будет
} // Создаём слайс нужного размера
numbers := make([]int, 5) // length=5, capacity=5
for i := 0; i < 5; i++ {
numbers[i] = i * 10 // Обращаемся по индексу
} package main
import "fmt"
func main() {
// Создаём слайс с небольшой ёмкостью
slice := make([]int, 3, 5) // длина 3, ёмкость 5
slice[0], slice[1], slice[2] = 10, 20, 30
fmt.Printf("Начальный: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
// Добавляем элемент - ёмкость позволяет
slice = append(slice, 40)
fmt.Printf("После append 40: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
// Добавляем ещё один элемент - ёмкость позволяет
slice = append(slice, 50)
fmt.Printf("После append 50: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
// Добавляем элемент - ёмкость не позволяет, произойдёт реаллокация
slice = append(slice, 60)
fmt.Printf("После append 60: %v (len=%d, cap=%d)\n", slice, len(slice), cap(slice))
// Теперь слайс указывает на новый базовый массив!
} package main
import "fmt"
func main() {
// Наблюдаем за ростом ёмкости
slice := []int{}
for i := 0; i < 20; i++ {
oldCap := cap(slice)
slice = append(slice, i)
newCap := cap(slice)
if oldCap != newCap {
fmt.Printf("Элемент %d: ёмкость изменилась с %d на %d\n", i, oldCap, newCap)
}
}
} Элемент 0: ёмкость изменилась с 0 на 1
Элемент 1: ёмкость изменилась с 1 на 2
Элемент 2: ёмкость изменилась с 2 на 4
Элемент 4: ёмкость изменилась с 4 на 8
Элемент 8: ёмкость изменилась с 8 на 16 slice = append(slice, element) // ✅ Правильно
append(slice, element) // ❌ Неправильно - изменения потеряются package main
import (
"fmt"
"time"
)
func main() {
// ❌ Плохо: многократные реаллокации
start := time.Now()
var bad []int
for i := 0; i < 100000; i++ {
bad = append(bad, i)
}
fmt.Printf("Плохой подход: %v, время: %v\n", len(bad), time.Since(start))
// ✅ Хорошо: предварительное выделение памяти
start = time.Now()
good := make([]int, 0, 100000) // заранее выделяем ёмкость
for i := 0; i < 100000; i++ {
good = append(good, i)
}
fmt.Printf("Хороший подход: %v, время: %v\n", len(good), time.Since(start))
}