Почему емкость среза изменяется, когда вы удаляете первые n элементов, но не последние n элементов?

#go

#Вперед

Вопрос:

Я просматриваю обзор Go и хотел бы знать следующее:

 package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Drop its last two values
    s = s[:len(s)-2]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %vn", len(s), cap(s), s)
}
  

Результат:

 len=6 cap=6 [2 3 5 7 11 13]
len=4 cap=6 [2 3 5 7]
len=2 cap=4 [5 7]
  

Почему емкость среза остается неизменной, когда вы удаляете последние 2 элемента, но изменяется, когда вы удаляете первые 2?

https://play.golang.org/p/ZNKwOYKDqOi

Комментарии:

1. blog.golang.org/go-slices-usage-and-internals

Ответ №1:

Фрагменты Go реализованы в виде структуры:

src/runtime/slice.go :

 type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}
  

Измените свою printSlice функцию, чтобы показать указатель на базовый массив:

 package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Drop its last two values
    s = s[:len(s)-2]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

func printSlice(s []int) {
    var ptr *int
    if cap(s) >= 1 {
        ptr = amp;s[:cap(s)][0]
    }
    fmt.Printf("ptr=%p len=%d cap=%d %vn", ptr, len(s), cap(s), s)
}
  

Игровая площадка: https://play.golang.org/p/pk3cpE_LsUV

Вывод:

 ptr=0x450000 len=6 cap=6 [2 3 5 7 11 13]
ptr=0x450000 len=4 cap=6 [2 3 5 7]
ptr=0x450008 len=2 cap=4 [5 7]
  

Посмотрите, как ваши операции среза регулируют указатель, длину и емкость. Фрагмент — это просто вид или окно в базовый массив.


Ссылки:

Блог Go: Срезы Go: использование и внутренности

Спецификация языка программирования Go:

Ответ №2:

Фрагмент имеет буфер под обложками. Емкость — это размер этого буфера. Удаление элементов с конца не изменяет размер буфера.

Вы создаете новый фрагмент из существующего фрагмента, но начинаете индекс нового фрагмента с 2. Новый фрагмент по-прежнему указывает на тот же базовый буфер (но смещен на 2). Таким образом, размер буфера (и емкость) для этого нового фрагмента на 2 меньше.

Емкость имеет смысл, когда вы видите, как работает append . Append пытается повторно использовать тот же базовый буфер — но если емкость заполнена, выполняется перераспределение и копирование.