# golang slice

myonlyzzy · · 2034 次点击 · · 开始浏览

• slice的实现原理,理解slice的实现原理是理解slice的一些很奇葩注意点的关键.一图胜千言.
image.png

• example1
``````func main() {
var s []int
for i:=1;i<7;i++{
s=append(s, i)
//fmt.Printf("%p\n",s)
}
s2:=s[:]
s1:=s[:]
s1=append(s1,100)
fmt.Printf("s1:%p len:%d,cap:%d\n",s1, len(s1), cap(s1))
s2=append(s2, 100,200,300)
fmt.Printf("s:%p len:%d,cap:%d\n",s, len(s), cap(s))
fmt.Printf("s2:%p len:%d,cap:%d\n",s2, len(s2), cap(s2))
}
``````

``````len cap  s[i]         s
0     0    0
1     1    1       0xc42001a050
2     2    2       0xc42001a070
3     4    3       0xc420016140
4     4    4       0xc420016140
5     8    5       0xc420018100
6     8    6       0xc420018100
``````

``````s1:0xc420018100 len:7,cap:8
s:0xc420018100 len:6,cap:8
s2:0xc42008c000 len:9,cap:16

Process finished with exit code 0
``````

s2 指向的地址和s不一样了,append3个元素超出了s的cap所以重新分配了一个数组

• example2
``````func main() {
var s []int
for i:=1;i<7;i++{
s=append(s, i)
}
fmt.Printf("s point:%p  s len:%d s cap:%d %v\n",s,s, len(s), cap(s))
reverse1(s)
fmt.Printf("s point:%p  s len:%d s cap:%d %v\n",s,s, len(s), cap(s))
}
func reverse1(s[]int){
for i,j:=0,len(s);i<j;i++{
j=len(s)-(i+1)
s[i],s[j]=s[j],s[i]
}

}

func reverse2(s[]int){
s=append(s, 8888,7777)
for i,j:=0,len(s);i<j;i++{
j=len(s)-(i+1)
s[i],s[j]=s[j],s[i]
}

}
func reverse3(s[]int){
s=append(s, 8888,7777,1000)
for i,j:=0,len(s);i<j;i++{
j=len(s)-(i+1)
s[i],s[j]=s[j],s[i]
}

}
``````

reverse1的输出

``````s point:0xc420018100  s len:[1 2 3 4 5 6] s cap:6 8
s point:0xc420018100  s len:[6 5 4 3 2 1] s cap:6 8
``````

reverse2 的输出

``````s point:0xc420018100  s len:[1 2 3 4 5 6] s cap:6 8
s point:0xc420018100  s len:[7777 8888 6 5 4 3] s cap:6 8
``````

reverse3 的输出

``````s point:0xc42008a040  s len:[1 2 3 4 5 6] s cap:6 8
s point:0xc42008a040  s len:[1 2 3 4 5 6] s cap:6 8
``````

1. reverse1 只是将s中的元素顺序逆转了一下,只改变了元素的值。没有改变len cap.
2. reverse2 中给s append了一个元素,并且append之后的slice长度还是小于cap所有没有重新分配数组。注意这里append元素是不会改变main里面的s的len和cap的.
3 .reverse3 中append 3个元素超过cap,重新分配底层数组,所以reverse中的s2 指向的地址和main中的s分别指向2个不同的数组。所以逆转操作是发生在另外一个数组元素上而不是main的传进来的slice对应的数组上。

• golang中的值传递
golang官方中明确指出golang中的所有的函数传参都是值传递.什么意思,即所有的传进函数方法的参数都是原来变量的拷贝.包括slice也是,有人说slice map channel是引用传递,其实是错误的,但是可以在函数中修改传入的参数的内容啊。
如同刚才的slice的例子,传进来的s只是main总的一份拷贝, reverse1 中的s和main中的s得内存地址是不一样的。
``````s point:0xc42008a040  s len:[1 2 3 4 5 6] s cap:6 8
0xc420096020
0xc420096080
s point:0xc42008a040  s len:[6 5 4 3 2 1] s cap:6 8
``````

• 相似的类型
``````type A struct {
Ptr1 *B
Ptr2 *B
Val B
}

type B struct {
Str string
}

func main() {
a := A{
Ptr1: &B{"ptr-str-1"},
Ptr2: &B{"ptr-str-2"},
Val: B{"val-str"},
}
fmt.Println(a.Ptr1)
fmt.Println(a.Ptr2)
fmt.Println(a.Val)
demo(a)
fmt.Println(a.Ptr1)
fmt.Println(a.Ptr2)
fmt.Println(a.Val)
}

func demo(a A) {
// Update a value of a pointer and changes will persist
a.Ptr1.Str = "new-ptr-str1"
// Use an entirely new B object and changes won't persist
a.Ptr2 = &B{"new-ptr-str-2"}
a.Val.Str = "new-val-str"
}
``````

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

0 回复

• 请尽量让自己的回复能够对别人有帮助
• 支持 Markdown 格式, **粗体**、~~删除线~~、``单行代码``
• 支持 @ 本站用户；支持表情（输入 : 提示），见 Emoji cheat sheet
• 图片支持拖拽、截图粘贴等方式上传

• slice的实现原理,理解slice的实现原理是理解slice的一些很奇葩注意点的关键.一图胜千言.
image.png

• example1
``````func main() {
var s []int
for i:=1;i<7;i++{
s=append(s, i)
//fmt.Printf("%p\n",s)
}
s2:=s[:]
s1:=s[:]
s1=append(s1,100)
fmt.Printf("s1:%p len:%d,cap:%d\n",s1, len(s1), cap(s1))
s2=append(s2, 100,200,300)
fmt.Printf("s:%p len:%d,cap:%d\n",s, len(s), cap(s))
fmt.Printf("s2:%p len:%d,cap:%d\n",s2, len(s2), cap(s2))
}
``````

``````len cap  s[i]         s
0     0    0
1     1    1       0xc42001a050
2     2    2       0xc42001a070
3     4    3       0xc420016140
4     4    4       0xc420016140
5     8    5       0xc420018100
6     8    6       0xc420018100
``````

``````s1:0xc420018100 len:7,cap:8
s:0xc420018100 len:6,cap:8
s2:0xc42008c000 len:9,cap:16

Process finished with exit code 0
``````

s2 指向的地址和s不一样了,append3个元素超出了s的cap所以重新分配了一个数组

• example2
``````func main() {
var s []int
for i:=1;i<7;i++{
s=append(s, i)
}
fmt.Printf("s point:%p  s len:%d s cap:%d %v\n",s,s, len(s), cap(s))
reverse1(s)
fmt.Printf("s point:%p  s len:%d s cap:%d %v\n",s,s, len(s), cap(s))
}
func reverse1(s[]int){
for i,j:=0,len(s);i<j;i++{
j=len(s)-(i+1)
s[i],s[j]=s[j],s[i]
}

}

func reverse2(s[]int){
s=append(s, 8888,7777)
for i,j:=0,len(s);i<j;i++{
j=len(s)-(i+1)
s[i],s[j]=s[j],s[i]
}

}
func reverse3(s[]int){
s=append(s, 8888,7777,1000)
for i,j:=0,len(s);i<j;i++{
j=len(s)-(i+1)
s[i],s[j]=s[j],s[i]
}

}
``````

reverse1的输出

``````s point:0xc420018100  s len:[1 2 3 4 5 6] s cap:6 8
s point:0xc420018100  s len:[6 5 4 3 2 1] s cap:6 8
``````

reverse2 的输出

``````s point:0xc420018100  s len:[1 2 3 4 5 6] s cap:6 8
s point:0xc420018100  s len:[7777 8888 6 5 4 3] s cap:6 8
``````

reverse3 的输出

``````s point:0xc42008a040  s len:[1 2 3 4 5 6] s cap:6 8
s point:0xc42008a040  s len:[1 2 3 4 5 6] s cap:6 8
``````

1. reverse1 只是将s中的元素顺序逆转了一下,只改变了元素的值。没有改变len cap.
2. reverse2 中给s append了一个元素,并且append之后的slice长度还是小于cap所有没有重新分配数组。注意这里append元素是不会改变main里面的s的len和cap的.
3 .reverse3 中append 3个元素超过cap,重新分配底层数组,所以reverse中的s2 指向的地址和main中的s分别指向2个不同的数组。所以逆转操作是发生在另外一个数组元素上而不是main的传进来的slice对应的数组上。

• golang中的值传递
golang官方中明确指出golang中的所有的函数传参都是值传递.什么意思,即所有的传进函数方法的参数都是原来变量的拷贝.包括slice也是,有人说slice map channel是引用传递,其实是错误的,但是可以在函数中修改传入的参数的内容啊。
如同刚才的slice的例子,传进来的s只是main总的一份拷贝, reverse1 中的s和main中的s得内存地址是不一样的。
``````s point:0xc42008a040  s len:[1 2 3 4 5 6] s cap:6 8
0xc420096020
0xc420096080
s point:0xc42008a040  s len:[6 5 4 3 2 1] s cap:6 8
``````

• 相似的类型
``````type A struct {
Ptr1 *B
Ptr2 *B
Val B
}

type B struct {
Str string
}

func main() {
a := A{
Ptr1: &B{"ptr-str-1"},
Ptr2: &B{"ptr-str-2"},
Val: B{"val-str"},
}
fmt.Println(a.Ptr1)
fmt.Println(a.Ptr2)
fmt.Println(a.Val)
demo(a)
fmt.Println(a.Ptr1)
fmt.Println(a.Ptr2)
fmt.Println(a.Val)
}

func demo(a A) {
// Update a value of a pointer and changes will persist
a.Ptr1.Str = "new-ptr-str1"
// Use an entirely new B object and changes won't persist
a.Ptr2 = &B{"new-ptr-str-2"}
a.Val.Str = "new-val-str"
}
``````

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