Самое горячее сегодня это выпуск WeChat 7.0, который добавляет короткие видео и оптимизирует такие функции, как просмотр.Давайте поговорим о языке Go (golang).Похоже, что если вы хотите стать квалифицированным селф-медиа, вам не следует ограничиваться , Есть долгий путь.
Два дня назад друг (Вилин) оставил сообщение на моем официальном аккаунте, и статья, оставившая сообщение, - вот эта.Боевые заметки на языке Go (5) | Go slice, это очень ранняя статья про язык Go (golang) Slice (слайс). Сообщение этого друга не о его собственной проблеме, а объяснение сообщения другого друга (Dreamerque).
Причина оставить сообщение
Чтобы связно объяснить проблему, давайте сначала посмотрим на сообщение друга Dreamerque от 17 марта 2018 года:
Существует проблема: Рассмотрите возможность использования среза ссылочного типа в качестве пользовательского приемника и привяжите метод следующим образом:
Вопрос: В настоящее время емкость пространства слайса достаточна, и его адрес не меняется до и после вызова метода, так почему же внутренние члены слайса после добавления не меняются? Копия копии по умолчанию — это ссылка на срез, которая должна иметь возможность изменять или добавлять элементы в соответствии с ожиданиями. .
type Slice []int
func (A Slice)Append(value int) {
A = append(A, value)
}
func main() {
mSlice := make(Slice, 10, 20)
mSlice.Append(5)
fmt.Println(mSlice)
}
Через код, я думаю, каждый может понять, что выше проблема и путаница Dreamerque. Мой ответ Dreamerque в то время заключался в том, что приведенные источники данных несовместимы, и я попросил его сослаться на мойРазница между new и make в языке GoЭта статья .
Затем всего два дня назад я получил сообщение от Weelin:
Привет безжалостный, я понимаю, что источник данных mslice не должен меняться. Из-за копирования значения единственное, что связано со срезами до и после метода Append, — это массив, на который указывает базовый слой.Результат печати отличается, потому что исходный срез слишком короткий. Это также может создать новый фрагмент (длиной больше 5) и распечатать подтверждение после выполнения метода Append.
Сообщение Weelin более подробное, а анализ более точный.В этот раз я понял, что мой ответ немного вводит Dreamerque в заблуждение, и можно понять, что источник данных, который я упомянул, является массивом данных более низкого уровня.
анализ проблемы
Из приведенного выше вывода мы действительно можем видетьmSlice
Ничего не изменилось, только методAppend
Ничего не сделал. Путаница Dreamerque заключается в том, что Slice является ссылочным типом, и его также следует изменить, если он модифицируется.На самом деле мы знаем, что измененная ссылка находится вAppend
В методе уход не работает.
На самом деле ничего из вышеперечисленного не является принципиальным, это то, о чем вообще упоминал Вилин,append
Последний Slice больше не является оригинальным Slice. В это время некоторые друзья могут снова запутаться.append
Указатель возвращенного среза совпадает с указателем исходного среза, как он может быть другим? Давайте проверим это один раз, изменив код следующим образом:
func (A Slice)Append(value int) {
A1 := append(A, value)
fmt.Printf("%p\n%p\n",A,A1)
}
мы используемA1
место храненияappend
Slice, возвращенный методом, затем распечатайте результатA1
ВахараA
Адрес указателя оказывается точно таким же. Вы можете попробовать это сами. На самом деле мыmake
При использовании слайса обнаружится, что он может иметь три параметра, один — данные, другой — длина, а третий — емкость, то есть слайс — это такая структура, и теперь она должно быть нашимSliceHeader
Время появиться.
Дебют SliceHeader
SliceHeader — это конкретная производительность среды выполнения Slice, и его структура определяется следующим образом:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Это соответствует трем элементам Slice,Data
Указывает на конкретный базовый массив источников данных,Len
обозначает длину,Cap
представляет емкость.
Поскольку Slice — это SliceHeader, давайте преобразуем Slice в SliceHeader, посмотримA
а такжеA1
Внутренние специальные значения полей, поэтому, чтобы судить о том, согласуются ли они, мы модифицируемAppend
Методы, как показано ниже:
//blog:www.flysnow.org
//wechat:flysnow_org
func (A Slice)Append(value int) {
A1 := append(A, value)
sh:=(*reflect.SliceHeader)(unsafe.Pointer(&A))
fmt.Printf("A Data:%d,Len:%d,Cap:%d\n",sh.Data,sh.Len,sh.Cap)
sh1:=(*reflect.SliceHeader)(unsafe.Pointer(&A1))
fmt.Printf("A1 Data:%d,Len:%d,Cap:%d\n",sh1.Data,sh1.Len,sh1.Cap)
}
пройти черезunsafe.Pointer
Стрелки отлиты, оunsafe.Pointer
знание может относиться к моемуБоевые заметки о языке Go (27) | Go unsafe PointerЭта статья.
преобразуются в*reflect.SliceHeader
После типа мы выводим их соответствующиеData
,Len
,Cap
поле, теперь мы смотрим на результат вывода.
A Data:824634204160,Len:10,Cap:20
A1 Data:824634204160,Len:11,Cap:20
Теперь все понимают, что ихLen
Не то же самое, не Slice, так что используйтеappend
метод не изменяет оригиналA
, но недавно созданныйA1
, даже если друг Dreamerque передаст следующий кодA = append(A, value)
сделать копию, тоже простоmSlice
копияA
меняется, и этоA
только вAppend
эффективен в рамках метода,mSlice
сам по себе не изменился, поэтому выводmSlice
Ничего не изменить.
Здесь правильно сделать, чтобыAppend
вернутьappend
результат после. На самом деле, для встроенных функцийappend
Использование языка Go (golang) официально объяснено, и возвращаемое значение следует сохранить.
Append returns the updated slice. It is therefore necessary to store the result of append
В приведенном выше примере друга Dreamerque Len установлен на 10, а Cap — на 20. Поскольку Cap достаточно велик, встроенная функцияappend
Новый базовый массив не создается, теперь мы меняем Cap на 10.
type Slice []int
func (A Slice)Append(value int) {
A1 := append(A, value)
sh:=(*reflect.SliceHeader)(unsafe.Pointer(&A))
fmt.Printf("A Data:%d,Len:%d,Cap:%d\n",sh.Data,sh.Len,sh.Cap)
sh1:=(*reflect.SliceHeader)(unsafe.Pointer(&A1))
fmt.Printf("A1 Data:%d,Len:%d,Cap:%d\n",sh1.Data,sh1.Len,sh1.Cap)
}
func main() {
mSlice := make(Slice, 10, 10)
mSlice.Append(5)
fmt.Println(mSlice)
}
Запустив код, мы найдем два срезаData
Уже не тот.
A Data:824633835680,Len:10,Cap:10
A1 Data:824634204160,Len:11,Cap:20
Это потому, что вappend
когда это было найденоCap
Недостаточно, был создан новыйData
Массив, используемый для хранения новых данных и одновременно расширяемыйCap
емкость.
резюме
В конце концов, я еще раз ответил Dreamerque и поблагодарил Weelin, а потом я подумал о такой проблеме, может быть много друзей, которые столкнутся с ней, поэтому я написал статью, чтобы разобрать суть Slice, то есть SliceHeader, Я надеюсь, что это может помочь Всем, язык Go, golang, это действительно грубо, SliceHeader очень скользкий.
Эта статья является оригинальной статьей, перепечатайте и укажите источник, добро пожаловать на сканирование кода и обратите внимание на общедоступный номер
flysnow_org
или сайтwww.flysnow.org/, и впервые прочитал последующие замечательные статьи. Если вы считаете, что это хорошо, пожалуйста, нажмите «Хорошо выглядит» в правом нижнем углу статьи, спасибо за вашу поддержку.