Форум программистов, компьютерный форум, киберфорум
Go (Golang)
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/3: Рейтинг темы: голосов - 3, средняя оценка - 5.00
29 / 22 / 8
Регистрация: 16.04.2020
Сообщений: 89

Не понимаю поведение слайсов

01.02.2025, 22:13. Показов 1203. Ответов 7
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Вот есть программа:
Go
1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
  var x []int
  x = append(x, 1)
  x = append(x, 2)
  x = append(x, 3)
  y := x
  x = append(x, 4)
  y = append(y, 5)
  x[0] = 0
 
  fmt.Println(x)
  fmt.Println(y)
}
Я перечитал кучу статей, но так и не понял: почему при добавлении 4 в x меняется только x, но y нет (раз оба слайса ссылаются на один базовый массив)? А когда мы добавляем 5 в y, то изменения отражаются в обоих слайсах
1
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
01.02.2025, 22:13
Ответы с готовыми решениями:

Какая логика в адресации слайсов?
package main import "fmt" func main() { primes := int{2, 3, 5, 7, 11, 13} var s int = primes fmt.Println(s) ...

Найти пересечение двух неупорядоченных слайсов любой длины
Найти пересечение двух неупорядоченных слайсов любой длины

Не понимаю поведение while
Имеется слудующий код: int gcd(int a, int b) { assert(a > 0 && b > 0); while (a != 0 && b != 0) { if (a...

7
307 / 268 / 77
Регистрация: 17.04.2022
Сообщений: 871
Записей в блоге: 8
02.02.2025, 06:12
IG2, Хороший вопрос.

Посмотрите https://freshman.tech/snippets/go/copy-slices/
0
 Аватар для alhaos
1919 / 536 / 152
Регистрация: 20.02.2019
Сообщений: 2,633
Записей в блоге: 65
02.02.2025, 10:07
Pre-sciptum, лично мое видение вопроса, без претензий на истину, могу что то не так понимать субъективно, и объективно в разных версиях может работать по разному.

Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main
 
import (
    "fmt"
)
 
func main() {
 
    var x []int
    fmt.Println("init x")
    printInfo("x", x)
    x = append(x, 1)
    fmt.Println("append 1 to x")
    printInfo("x", x)
    x = append(x, 2)
    fmt.Println("append 2 to x")
    printInfo("x", x)
    x = append(x, 3)
    fmt.Println("append 3 to x")
    printInfo("x", x)
    y := x
    fmt.Println("y = x")
    printInfo("x", x)
    printInfo("y", y)
    x = append(x, 4)
    fmt.Println("append 4 to x")
    printInfo("x", x)
    printInfo("y", y)
    y = append(y, 5)
    fmt.Println("append 5 to y")
    printInfo("x", x)
    printInfo("y", y)
}
 
func printInfo(name string, sl []int) {
    fmt.Printf("name: %s, capacity: %d, len: %d, data: %+v, pointer: %p", name, cap(sl), len(sl), sl, sl)
    fmt.Println()
}
IG2,
Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
init x
name: x, capacity: 0, len: 0, data: [], pointer: 0x0 // пока еще нет указателья
append 1 to x
name: x, capacity: 1, len: 1, data: [1], pointer: 0xc00000a120
// инициировался базовый массив его длинна 1, емкость 1
// инициировался слайс на его основе этого массива его длинна 1, емкость 1
append 2 to x
name: x, capacity: 2, len: 2, data: [1 2], pointer: 0xc00000a130
// обратите внимание поинтер сменился
// так как append добавила данные которые выходят за пределы базового массива
// тут инициировался новый базовый массив
// с длинной 2 емкостью 2
// слайс x теперь ссылается на его
append 3 to x
name: x, capacity: 4, len: 3, data: [1 2 3], pointer: 0xc0000161c0
// еще раз поинтер сменился
// так как append добавила данные которые выходят за пределы базового массива
// тут инициировался новый базовый массив с длинной 4
// слайс x теперь ссылается на его, но его длина 3, а емкость 4
y = x
name: x, capacity: 4, len: 3, data: [1 2 3], pointer: 0xc0000161c0
name: y, capacity: 4, len: 3, data: [1 2 3], pointer: 0xc0000161c0
// тут мы уже имеем два слайса с ссылкой на один базовый массив который сейчас
// [1,2,3,0] (0 дефолтное значение для int)
// дальше у нас пойнтер не меняется
append 4 to x
name: x, capacity: 4, len: 4, data: [1 2 3 4], pointer: 0xc0000161c0
name: y, capacity: 4, len: 3, data: [1 2 3], pointer: 0xc0000161c0
// вот тут не происходит смена поинтера мы все так же ссылаемся на на [0-3] элемент базового массива
append 5 to y
name: x, capacity: 4, len: 4, data: [1 2 3 5], pointer: 0xc0000161c0
name: y, capacity: 4, len: 4, data: [1 2 3 5], pointer: 0xc0000161c0
// добавляя 5 мы не выходим за пределы базового массива поэтому переинициализация этого массива не происходит просто
3 элемент этого массива становится равен 5
// Но на него же ссылается слайс x и это изменение затрагивает и его
Для меня тут важно понимать, то слайс это ссылка на последовательность элементов базового массива, и при выходе за пределы базового массива он переинициализуется.
2
5 / 40 / 1
Регистрация: 08.12.2024
Сообщений: 221
02.02.2025, 11:42
Цитата Сообщение от IG2 Посмотреть сообщение
почему при добавлении 4 в x меняется только x, но y нет (раз оба слайса ссылаются на один базовый массив)?
Ссылаются-то они ссылаются, но длина у каждого - своя хранится.
0
643 / 397 / 76
Регистрация: 21.09.2008
Сообщений: 1,362
02.02.2025, 13:46
IG2, немного изменим Ваш пример для целей более подробного просмотра значений срезов и указателей на них.
Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main
 
import (
    "fmt"
)
 
func Show(id string, s []int) {
    fmt.Printf("len=%d cap=%d %s=%v %p\n", len(s), cap(s), id, s, s)
}
 
func main() {
  var x []int
  x = append(x, 1)
Show("x", x)
  x = append(x, 2)
Show("x", x)
  x = append(x, 3)
Show("x", x)
  y := x
fmt.Println("y := x")
Show("y", y)
  x = append(x, 4)
Show("x", x)
  y = append(y, 5)
Show("y", y)
  x[0] = 0
Show("x", x)
  fmt.Println("x", x)
  fmt.Println("y", y)
}
Для go1.23.5 windows/amd64 "выхлоп" работы примера в консоль:
len=1 cap=1 x=[1] 0xc00000a138
len=2 cap=2 x=[1 2] 0xc00000a170
len=3 cap=4 x=[1 2 3] 0xc00000e1a0
y := x
len=3 cap=4 y=[1 2 3] 0xc00000e1a0
len=4 cap=4 x=[1 2 3 4] 0xc00000e1a0
len=4 cap=4 y=[1 2 3 5] 0xc00000e1a0
len=4 cap=4 x=[0 2 3 5] 0xc00000e1a0
x [0 2 3 5]
y [0 2 3 5]
Анализируя его, можно сделать выводы:
1. Присваивание одного среза другому не создаёт новый срез с копированием. Для копирования необходимо использовать системную функцию copy(), встроенную в язык Go, помня про два нюанса: целевой срез не расширяется автоматически, если он меньше исходного; если целевой срез больше исходного, то функция copy() не очищает элементы целевого, которые не были скопированы.
2. Значения указателей в структурах, описывающих срез, одинаковы, но значение длины в срезе x[] отличается от длины среза y[] после добавления числа 4. Далее добавляется в срез x[] число 5, но учитывая, что длина среза x[] меньше на единицу длины среза y[], число 5 затирает число 4.
Напомню описание типа структуры среза в пакете reflect (https://golang.org/pkg/reflect/#SliceHeader):
Go
1
2
3
4
5
type SliceHeader struct {
  Data uintptr
  Len int
  Cap int
}
Надеюсь, ответил на Ваш вопрос:
Цитата Сообщение от IG2 Посмотреть сообщение
почему при добавлении 4 в x меняется только x, но y нет (раз оба слайса ссылаются на один базовый массив)? А когда мы добавляем 5 в y, то изменения отражаются в обоих слайсах
0
1125 / 706 / 113
Регистрация: 10.03.2012
Сообщений: 4,583
02.02.2025, 16:21
Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main
 
import "fmt"
 
func main() {
    var x []int
    x = append(x, 1)
    x = append(x, 2)
    x = append(x, 3)
    
    fmt.Println("x:", x) // x: [1 2 3]
    
    y := x
    fmt.Println("y:", y) // y: [1 2 3]
    
    x = append(x, 4)
    fmt.Println("x after adding 4:", x) 
    fmt.Println("y after x append:", y) 
    
    y = append(y, 5)
    fmt.Println("y after adding 5:", y) 
    
    x[0] = 0
    fmt.Println("x after setting x[0] to 0:", x) 
    fmt.Println("y after modifying x:", y)
}
0
Эксперт функциональных языков программированияЭксперт Java
 Аватар для korvin_
4564 / 2760 / 489
Регистрация: 28.04.2012
Сообщений: 8,699
05.02.2025, 10:42
Go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package main
 
import "fmt"
 
func main() {
    fmt.Println("---- INIT x")
    var x []int
    printSlice("x", x)
 
    x = append(x, 1)
    printSlice("x", x)
    x = append(x, 2)
    printSlice("x", x)
    x = append(x, 3)
    printSlice("x", x)
 
    y := x
    printSlices("y := x", x, y)
 
    x = append(x, 4)
    printSlices("append(x, 4)", x, y)
 
    y = append(y, 5)
    printSlices("append(y, 5)", x, y)
 
    x[0] = 0
    printSlices("x[0] = 0", x, y)
 
    x = append(x, 6)
    printSlices("append(x, 6)", x, y)
 
    y = append(y, 7)
    printSlices("append(y, 7)", x, y)
}
 
func printSlices(label string, x, y []int) {
    fmt.Printf("---- %s\n", label)
    printSlice("x", x)
    printSlice("y", y)
}
 
func printSlice(name string, s []int) {
    var v = fmt.Sprint(s)
    fmt.Printf("%s: %-10s\t    [len=%d cap=%d *] -> %p\n", name, v, len(s), cap(s), s)
}
-- https://go.dev/play/p/1wOOTpTNHew

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---- INIT x
x: []               [len=0 cap=0 *] -> 0x0
x: [1]              [len=1 cap=1 *] -> 0xc000102050
x: [1 2]            [len=2 cap=2 *] -> 0xc000102070
x: [1 2 3]          [len=3 cap=4 *] -> 0xc00011c020
---- y := x
x: [1 2 3]          [len=3 cap=4 *] -> 0xc00011c020
y: [1 2 3]          [len=3 cap=4 *] -> 0xc00011c020
---- append(x, 4)
x: [1 2 3 4]        [len=4 cap=4 *] -> 0xc00011c020
y: [1 2 3]          [len=3 cap=4 *] -> 0xc00011c020
---- append(y, 5)
x: [1 2 3 5]        [len=4 cap=4 *] -> 0xc00011c020
y: [1 2 3 5]        [len=4 cap=4 *] -> 0xc00011c020
---- x[0] = 0
x: [0 2 3 5]        [len=4 cap=4 *] -> 0xc00011c020
y: [0 2 3 5]        [len=4 cap=4 *] -> 0xc00011c020
---- append(x, 6)
x: [0 2 3 5 6]      [len=5 cap=8 *] -> 0xc00011e040
y: [0 2 3 5]        [len=4 cap=4 *] -> 0xc00011c020
---- append(y, 7)
x: [0 2 3 5 6]      [len=5 cap=8 *] -> 0xc00011e040
y: [0 2 3 5 7]      [len=5 cap=8 *] -> 0xc00011e080
0
643 / 397 / 76
Регистрация: 21.09.2008
Сообщений: 1,362
18.02.2025, 20:56
Готовим слайсы в Go: подробно о динамических массивах, строчках и ускорении / Хабр
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
18.02.2025, 20:56
Помогаю со студенческими работами здесь

Не понимаю поведение wget
Есть два разных скрипта, в одном wget обращается с пост запросом к странице сайта, а в другом качает zip архив. Оба эти скрипта...

Не понимаю поведение указателей в классе
Имеется следующий код. Создается очередь Т2 из 10 элементов и очередь Т3 из 5 элементов с неким значением. Вывожу Т2. Однако, помимо...

Php7.2 абстрактные классы, не понимаю поведение кода
Смотрю курс на ютубе по ООП в PHP. Чуть чуть изменил учебный пример и код ведёт себя странно. abstract class User{ ...

Не понимаю поведение мат платы M3A32-MVP DELUXE
Здравствуйте! На руки попала плата от ASUS M3A32-MVP DELUXE, решил недавно протестить её на работоспособность, зашел на офицалку посмотрел...

Сколько узлов в цепи и какие именно считать узлами? (вроде понимаю, но и не понимаю)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Новые блоги и статьи
Как создать стек в Python
AI_Generated 05.06.2025
Как архитектор с более чем десятилетним опытом работы с Python, я неоднократно убеждался, что знание низкоуровневых механизмов работы стеков дает конкурентное преимущество при решении сложных задач. . . .
Server-Sent Events (SSE) в Node.js
run.dev 05.06.2025
Потоковая передача данных с сервера прямо в браузер стала повседневной потребностью - от биржевых графиков и спортивных трансляций до чатов и умных дашбордов. Много лет разработчики полагались на. . .
Создаем RESTful API на Golang с Fiber
golander 04.06.2025
Я перепробовал десятки фреймворков для создания RESTful API за последние годы, и когда впервые столкнулся с Fiber, понял, что это совсем другой уровень. Нет, я не собираюсь рассказывать сказки о. . .
Как работать с куки в ASP.NET Core
UnmanagedCoder 04.06.2025
Когда я впервые начал работать с куки в ASP. NET Core, меня поразило, насколько отличается работа с ними от классического ASP. NET. В Core все стало более декомпозированным - больше нет удобного. . .
Рисование коллайдеров физического движка Box2D-WASM v3 на Three.js
8Observer8 04.06.2025
Erin Catto (автор Box2D) переписал с нуля Box2D v2 с С++ на Си и появилась версия Box2D v3. Birch-san собрал Box2D v3 в WebAssembly (WASM), чтобы можно было использовать Box2D v3 на JavaScript. В. . .
Worker Threads и многопоточность в Node.js
Reangularity 03.06.2025
Если вы когда-нибудь посещали собеседования на позицию Node. js разработчика, почти наверняка слышали заезженную фразу: "Node. js - однопоточная платформа". Звучит как неоспоримый факт, который. . .
Event-Driven CQRS на C# с паттерном Outbox
stackOverflow 03.06.2025
В традиционной модели происходит примерно следующее: вы получаете команду, обрабатываете ее, сохраняете результат в базу данных и затем пытаетесь опубликовать событие в брокер сообщений. Но что если. . .
OwenLogic: перенос сетевых переменных в панель Weintek (EasyBuilder Pro)
ФедосеевПавел 03.06.2025
ВВЕДЕНИЕ ПЕРЕД ЭКСПЕРИМЕНТАМИ - СОЗДАЙТЕ РЕЗЕРВНЫЕ КОПИИ ПРОЕКТОВ На момент написания статьи (02 июня 2025 г. ) самыми актуальными версиями ПО являются: OwenLogic v. 2. 10. 366 EasyBuilder Pro. . .
Dev-c++5.11 Покорение вершины
russiannick 02.06.2025
С утра преследовала одна мысль - вот бы выучить С++. Сказано-сделано. Окончив смену, скачал в интернете бестселлер Дэвиса Dev-C++ для чайников. Книга оказалась интересной и я скачал среду, на примере. . .
Тестирование Pull Request в Kubernetes с GitHub Actions и GKE
Mr. Docker 02.06.2025
Мы все знаем, что тестирование на локальной машине или в изолированном CI-окружении — это не совсем то же самое, что тестирование в реальном кластере Kubernetes. Контекстно-зависимые ошибки, проблемы. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru
OSZAR »