array データを格納する基礎となる配列はポインターです。 #len スライス長 cap スライス容量の上限>=len #提到切片就不得不想到数组,可以这么理解:
切片是对数组的抽象,而数组则是切片的底层实现。 其实通过切片这个名字也不难看出,它就是从数组中切了一部分;相对于数组的固定大小,切片可以根据实际使用情况进行扩容。
所以切片也可以通过对数组"切一刀"获得:
x1:=[6]int{0,1,2,3,4,5}
x2 := x[1:4]
fmt.Println(len(x2), cap(x2)) ログイン後にコピー
其中 x1 的长度与容量都是6。
x2 的长度与容量则为3和5。
因为切片 x2 是对数组 x1 的引用,所以底层数组排除掉左边一个没有被引用的位置则是该切片最大的容量,也就是5。
同一个底层数组 以刚才的代码为例:
func TestAppendA(t *testing.T) {
x:=[]int{1,2,3}
appendA(x)
fmt.Printf("main %v\n", x)
}
func appendA(x []int) {
x[0]= 100
fmt.Printf("appendA %v\n", x)
} ログイン後にコピー
ログイン後にコピー
在函数传递过程中,main 中的 x 与 appendA 函数中的 x 切片所引用的是同个数组。
所以在函数中对 x[0]=100,main函数中也能获取到。
本质上修改的就是同一块内存数据。
值传递带来的误会 在上述例子中,在 appendB 中调用 append 函数追加数据后会发现 main 函数中并没有受到影响,这里我稍微调整了一下示例代码:
func TestAppendB(t *testing.T) {
//x:=[]int{1,2,3}
x := make([]int, 3,5)
x[0] = 1
x[1] = 2
x[2] = 3
appendB(x)
fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))
}
func appendB(x []int) {
x = append(x, 444)
fmt.Printf("appendB %v len=%v,cap=%v\n", x,len(x),cap(x))
} ログイン後にコピー
主要是修改了切片初始化方式,使得容量大于了长度,具体原因后续会说明。 输出结果如下:
appendB [1 2 3 444] len=4,cap=5
main [1 2 3] len=3,cap=5 ログイン後にコピー
main 函数中的数据看样子确实没有受到影响;但细心的朋友应该会注意到 appendB 函数中的 x 在 append() 之后长度 +1 变为了4。
而在 main 函数中长度又变回了3.
这个细节区别就是为什么 append() "看似" 没有生效的原因;至于为什么要说“看似”,再次调整了代码:
func TestAppendB(t *testing.T) {
//x:=[]int{1,2,3}
x := make([]int, 3,5)
x[0] = 1
x[1] = 2
x[2] = 3
appendB(x)
fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))
y:=x[0:cap(x)]
fmt.Printf("y %v len=%v,cap=%v\n", y,len(y),cap(y))
} ログイン後にコピー
在刚才的基础之上,以 append 之后的 x 为基础再做了一个切片;该切片的范围为 x 所引用数组的全部数据。
再来看看执行结果如何:
appendB [1 2 3 444] len=4,cap=5
main [1 2 3] len=3,cap=5
y [1 2 3 444 0] len=5,cap=5 ログイン後にコピー
会神奇的发现 y 将所有数据都打印出来,在 appendB 函数中追加的数据其实已经写入了数组中,但为什么 x 本身没有获取到呢?
看图就很容易理解了:
在appendB中确实是对原始数组追加了数据,同时长度也增加了。
但由于是值传递,所以 slice 这个结构体即便是修改了长度为4,也只是对复制的那个对象修改了长度,main 中的长度依然为3.
由于底层数组是同一个,所以基于这个底层数组重新生成了一个完整长度的切片便能看到追加的数据了。
所以这里本质的原因是因为 slice 是一个结构体,传递的是值,不管方法里如何修改长度也不会影响到原有的数据(这里指的是长度和容量这两个属性)。
切片扩容 还有一个需要注意:
刚才特意提到这里的例子稍有改变,主要是将切片的容量设置超过了数组的长度;
如果不做这个特殊设置会怎么样呢?
func TestAppendB(t *testing.T) {
x:=[]int{1,2,3}
//x := make([]int, 3,5)
x[0] = 1
x[1] = 2
x[2] = 3
appendB(x)
fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))
y:=x[0:cap(x)]
fmt.Printf("y %v len=%v,cap=%v\n", y,len(y),cap(y))
}
func appendB(x []int) {
x = append(x, 444)
fmt.Printf("appendB %v len=%v,cap=%v\n", x,len(x),cap(x))
} ログイン後にコピー
输出结果:
appendB [1 2 3 444] len=4,cap=6
main [1 2 3] len=3,cap=3
y [1 2 3] len=3,cap=3 ログイン後にコピー
这时会发现 main 函数中的 y 切片数据也没有发生变化,这是为什么呢?
这是因为初始化 x 切片时长度和容量都为3,当在 appendB 函数中追加数据时,会发现没有位置了。
这时便会进行扩容:
同样的由于是值传递,所以 appendB 中的切片换了底层数组对 main 函数中的切片没有任何影响,也就导致最终 main 函数的数据没有任何变化了。
传递切片指针 有没有什么办法即便是在扩容时也能对外部产生影响呢?
func TestAppendC(t *testing.T) {
x:=[]int{1,2,3}
appendC(&x)
fmt.Printf("main %v len=%v,cap=%v\n", x,len(x),cap(x))
}
func appendC(x *[]int) {
*x = append(*x, 4)
fmt.Printf("appendC %v\n", x)
} ログイン後にコピー
输出结果为:
appendC &[1 2 3 4]
main [1 2 3 4] len=4,cap=6 ログイン後にコピー
这时外部的切片就能受到影响了,其实原因也很简单;
刚才也说了,因为 slice 本身是一个结构体,所以当我们传递指针时,就和平时自定义的 struct 在函数内部通过指针修改数据原理相同。
最终在 appendC 中的 x 的指针指向了扩容后的结构体,因为传递的是 main 函数中 x 的指针,所以同样的 main 函数中的 x 也指向了该结构体。
总结
所以总结一下:
切片是对数组的抽象,同时切片本身也是一个结构体。
参数传递时函数内部与外部引用的是同一个数组,所以对切片的修改会影响到函数外部。
如果发生扩容,情况会发生变化,同时扩容会导致数据拷贝;所以要尽量预估切片大小,避免数据拷贝。
スライスまたは配列を再生成する場合、それらは同じ基礎となる配列を共有するため、データが相互に影響を与えるため、注意が必要です。
スライスはポインタを渡すこともできますが、シナリオが少なく、不要な誤解を招く可能性があるため、長さと容量がかからないため、単に値を渡すことをお勧めしますたくさんのメモリをアップします。
スライスを使用したことがある場合は、これが Java の ArrayList に非常に似ていることがわかると思いますが、これは配列の実装に基づいており、データの展開とコピーも行います。言語は上位層の選択にすぎないようですが、一般的な基礎となる実装は誰とでも似ています。
現時点で、タイトルの []*T *[]T *[]*T を見ると、それらの間に関連性はないことがわかりますが、見た目は非常に似ており、簡単に理解できます。はったりの人。
必要に応じて、golong チュートリアル
を読んでください。以上が大規模混乱シーンの[]*Tとは何ですか? *[]Tとは何ですか? *[]*Tそれは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
2021-08-13 18:04:58
2021-08-13 17:36:16
2021-08-13 16:16:35
2021-08-13 15:33:28
2021-08-13 14:30:39
2021-08-13 11:57:56
2021-08-13 11:32:54
2021-08-13 10:45:02
2021-08-24 10:52:19
2023-02-18 07:56:01