Зачем использовать массивы вместо ломтиков?
Я читал "Go" и задумался над этим фундаментальным вопросом.
В Go достаточно ясно, что срезы более гибкие и обычно могут использоваться вместо массивов, когда вам нужна последовательность данных.
Читая большую часть документации, они, похоже, поощряют разработчиков просто использовать срезы вместо массивов. У меня такое впечатление, что создатели могли просто спроектировать массивы, чтобы их можно было изменить размер и сделать без раздела всех секций. Фактически, такой дизайн сделал бы язык еще более понятным и, возможно, даже поощрял более идиоматический код.
Итак, почему создатели разрешили массивы в первую очередь? Когда будут использоваться массивы вместо ломтиков? Существует ли когда-либо ситуация, когда использование массивов над срезами будет убедительным?
Когда я обратился к официальной документации (http://golang.org/doc/effective_go.html#arrays), единственной полезной частью, которую я нашел, было следующее:
Массивы полезны при планировании детальной планировки памяти и иногда могут помочь избежать выделения, но в первую очередь они являются строительным блоком для срезов.
Они продолжили разговор о том, как массивы дороги как ценности, и как имитировать поведение стиля С с помощью указателя. Даже тогда они закончили раздел массива с четкой рекомендацией:
Но даже этот стиль не является идиоматическим Go. Вместо этого используйте срезы.
Итак, каковы некоторые реальные примеры "планирования детального макета памяти" или "помочь избежать выделения", что фрагменты не подходят для?
Ответы
Ответ 1
Как сказал Акавалл, массивы хешируются. Это означает, что они могут использоваться как ключ к карте.
Они также проходят по значению. Каждый раз, когда вы передаете его функции в функцию или назначаете ее другой переменной, она делает полную ее копию.
Они могут быть сериализованы путем кодирования/двоичного кода.
Они также могут использоваться для управления макетом памяти. Поскольку это не ссылка, когда она помещается в структуру, она будет выделять столько памяти как часть структуры вместо того, чтобы помещать эквивалент указателя там, как срез.
В нижней строке, не используйте массив, если вы не знаете, что делаете.
Hashable/serializable хороши, но я не уверен, действительно ли они убедительны иметь
Что бы вы сделали, если бы хотели иметь карту хешей md5? Нельзя использовать байтовый срез, поэтому вам нужно будет сделать что-то подобное, чтобы обойти систему типов:
// 16 bytes
type hashableMd5 struct {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p byte}
Затем создайте для него функцию сериализации. Hashable массивы означают, что вы можете просто называть его байтом [16].
Похоже на приближение к C malloc, sizeof
Нет, это не имеет ничего общего с malloc или sizeof. Это распределить память и получить размер переменной.
Однако для этого используется CGo. Команда cgo создает типы, которые имеют тот же макет памяти, что и соответствующие им типы C. Для этого иногда требуется вставить неназванные массивы для заполнения.
Если проблемы могут быть решены с помощью... nil/незначительного штрафа за производительность с помощью срезов...
Массивы также предотвращают косвенные действия, делающие определенные типы кода быстрее. Конечно, это такая незначительная оптимизация, что почти во всех случаях это незначительно.
Ответ 2
Одно практическое отличие состоит в том, что arrays
являются хешируемыми, а slices
не являются.
Ответ 3
Чтобы дополнить ответ Стивена Вайнберга:
Итак, каковы некоторые реальные примеры "планирования детального макета памяти" или "помочь избежать выделения", что фрагменты не подходят для?
Вот пример для "планирования детальной планировки памяти". Существует много форматов файлов. Обычно формат файла выглядит следующим образом: он начинается с "magic number" , затем следует информационный заголовок, структура которого обычно фиксируется. Этот заголовок содержит информацию о содержимом, например, в случае файла изображения он содержит информацию, такую как размер изображения (ширина, высота), формат пикселя, используемое сжатие, размер заголовка, смещение данных изображения и тому подобное (в основном описывает остальную часть файла и как интерпретировать/обрабатывать его).
Если вы хотите реализовать формат файла в Go, простым и удобным способом является создание struct
, содержащего поля заголовка в формате. Если вы хотите прочитать файл такого формата, вы можете использовать метод binary.Read()
, чтобы прочитать весь заголовок struct
в переменной, и аналогичным образом, если вы хотите записать файл этого формата, вы можете использовать binary.Write()
, чтобы записать полный заголовок за один шаг в файл ( или куда вы отправляете данные).
Заголовок может содержать даже десятки или сотни полей, вы все равно можете прочитать/записать его одним вызовом метода.
Теперь, как вы можете почувствовать, "макет памяти" заголовка struct
должен точно соответствовать макету байта, поскольку он сохраняется (или должен быть сохранен) в файле, если вы хотите сделать все это за один шаг.
А где на экране появляются массивы?
Многие форматы файлов обычно сложны, потому что они хотят быть общими и поэтому позволяют использовать широкий спектр применений и функциональных возможностей. И много раз вы не хотите внедрять/обрабатывать все, что поддерживает формат, потому что либо вам все равно (потому что вы просто хотите извлечь какую-либо информацию), либо вам не нужно, потому что у вас есть гарантии, что вход будет только используйте подмножество или фиксированный формат (из многих случаев формат файла полностью поддерживается).
Итак, что вы делаете, если у вас есть спецификация заголовка со многими полями, но вам нужно только несколько из них? Вы можете определить структуру, которая будет содержать нужные вам поля, а между полями вы можете использовать массивы с размером полей, которые вам просто не нужны/не нужны. Это гарантирует, что вы все равно можете прочитать весь заголовок с помощью одного вызова функции, и массивы будут в основном быть заполнителем неиспользуемых данных в файле. Вы также можете использовать blank идентификатор в качестве имени поля в определении заголовка struct
, если вы не будете использовать данные.
Теоретический пример
В качестве простого примера давайте внедрить формат, в котором магией является "TGI" (теоретическое изображение Go), а заголовок содержит такие поля: 2 зарезервированных слова (по 16 бит), 1 ширину изображения dword, 1 высоту изображения dword, теперь наступает 15 "не заботятся", а затем время сохранения изображения как 8-байтовое число наносекунд с 1 января 1970 года по UTC.
Это может быть смоделировано с такой структурой (исключение магического числа):
type TGIHeader struct {
_ uint16 // Reserved
_ uint16 // Reserved
Width uint32
Height uint32
_ [15]uint32 // 15 "don't care" dwords
SaveTime int64
}
Чтобы прочитать файл TGI и распечатать полезную информацию:
func ShowInfo(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
magic := make([]byte, 3)
if _, err = f.Read(magic); err != nil {
return err
}
if !bytes.Equal(magic, []byte("TGI")) {
return errors.New("Not a TGI file")
}
th := TGIHeader{}
if err = binary.Read(f, binary.LittleEndian, &th); err != nil {
return err
}
fmt.Printf("%s is a TGI file,\n\timage size: %dx%d\n\tsaved at: %v",
name, th.Width, th.Height, time.Unix(0, th.SaveTime))
return nil
}