Go言語の配列とスライスの違いは何ですか?
配列とスライスの違い: 1. スライスはポインタ型、配列は値型; 2. 配列の代入形式は値転送、スライスの代入形式は参照転送; 3. 長さ配列の長さは固定されており、スライスの長さは任意に調整できます (スライスは動的配列です); 4. 配列の長さは固定ですが、スライスの長さは任意に調整できます (スライスは動的配列です)。
このチュートリアルの動作環境: Windows 7 システム、GO バージョン 1.18、Dell G3 コンピューター。
Go 言語の配列は C/C の配列とほぼ同等で、サイズが固定されており、動的にサイズを拡張することはできませんが、スライスは C/C のベクトルとほぼ同等です。 C は動的にサイズを拡張でき、サイズが容量を超えた場合にメモリを再割り当てし、新しいメモリ領域にデータをコピーします。 golang の配列とスライスをより深く理解するために、いくつかの質問に答えてみましょう。
1. 配列
Go のスライスは配列の上にある抽象データ型であるため、配列を理解する前にまず配列を理解する必要があります。スライス 。
1. 配列を宣言する 3 つの方法
- var identifier [len]type
- var identifier = [len]type{value1 , value2, … , valueN}
- var identifier = […]type{value1, value2, … , valueN}
対応する:
- identifier : = [len]type{}
- identifier := [len]type{value1, value2, … , valueN}
- identifier := […]type{value1, value2, …, valueN }
例:
var iarray1 [5]int32 var iarray2 [5]int32 = [5]int32{1, 2, 3, 4, 5} iarray3 := [5]int32{1, 2, 3, 4, 5} iarray4 := [5]int32{6, 7, 8, 9, 10} iarray5 := [...]int32{11, 12, 13, 14, 15} iarray6 := [4][4]int32{{1}, {1, 2}, {1, 2, 3}} fmt.Println(iarray1) fmt.Println(iarray2) fmt.Println(iarray3) fmt.Println(iarray4) fmt.Println(iarray5) fmt.Println(iarray6)
結果:
[0 0 0 0 0] [1 2 3 4 5] [1 2 3 4 5] [6 7 8 9 10] [11 12 13 14 15] [[1 0 0 0] [1 2 0 0] [1 2 3 0] [0 0 0 0]]
配列 iarray1 を調べますが、これは宣言されただけで割り当てられていません。Go 言語を使用すると、自動的に割り当てることができます。値は 0 です。 iarray2 と iarray3 をもう一度見てみると、Go 言語の宣言で型を示すかどうかを指定できることがわかります。var iarray3 = [5]int32{1, 2, 3, 4, 5} もまったく問題ありません。
2. 配列の容量と長さ
配列の容量と長さは同じです。 cap() 関数と len() 関数はどちらも、配列の容量 (つまり長さ) を出力します
3. Type
配列は値型です。ある配列を別の配列に代入するとき、渡された コピーです。スライスは参照型ですが、スライスによってラップされた配列は、スライスの基になる配列と呼ばれます。次の例を見てください:
//a是一个数组,注意数组是一个固定长度的,初始化时候必须要指定长度,不指定长度的话就是切片了 a := [3]int{1, 2, 3} //b是数组,是a的一份拷贝 b := a //c是切片,是引用类型,底层数组是a c := a[:] for i := 0; i < len(a); i++ { a[i] = a[i] + 1 } //改变a的值后,b是a的拷贝,b不变,c是引用,c的值改变 fmt.Println(a) //[2,3,4] fmt.Println(b) //[1 2 3] fmt.Println(c) //[2,3,4]
2. スライス
Go 言語では、スライスは可変長で固定容量の同一の要素シーケンスです。 Go 言語のスライスの本質は配列です。配列の長さが固定されており、スライスの容量が隠し配列の長さになるため、容量も固定されます。可変長とは、配列の長さの範囲内で可変であることを指します。
1. スライスを作成する 4 つの方法
- var slide1 = make([]int,5,10)
- var slide2 = make([]int,5)
- var スライス 3 = []int{}
- var スライス 4 = []int{1,2,3,4,5}
同様に:
- slice1 := make([]int,5,10)
- slice2 := make([]int,5)
- slice3 := []int{}
- slice4 := []int{1,2,3,4,5}
上記の対応する出力
[0 0 0 0 0] [0 0 0 0 0] [] [1 2 3 4 5]
3) と 4) からわかるように、スライスの作成と配列の作成の唯一の違いは、Type の前の "[]" に数字があるかどうかです。空の場合、スライスを表します。 、それ以外の場合は配列を表します。スライスは可変長であるため、
2. 隠し配列
Go のスライスは配列の上にある抽象データ型であるため、作成されたスライスには常に配列が含まれます。存在します。
例:
slice0 := []string{"a", "b", "c", "d", "e"} slice1 := slice0[2 : len(slice0)-1] slice2 := slice0[:3] fmt.Println(slice0, slice1, slice2) slice2[2] = "8" fmt.Println(slice0, slice1, slice2)
出力:
[a b c d e] [c d] [a b c] [a b 8 d e] [8 d] [a b 8]
スライスslice0、slice1、slice2が同じ基になる配列への参照であるため、slice2が変更され、その他のスライスが変更されたことも示しています。 2 つすべてが変わります
3. append はスライスを追加します
組み込み関数 append は、同じ型の 1 つ以上の他の値をスライスに追加できます。スライス。追加された要素の数が元のスライスの容量を超える場合、最終的に返されるのは、新しい配列内の新しいスライスです。それを超えない場合、最終的に返されるのは、元の配列内のまったく新しいスライスです。いずれの場合も、追加は元のスライスには影響しません。
例:
slice1 := make([]int, 2, 5) fmt.Println(len(slice1), cap(slice1)) for k := range slice1{ fmt.Println(&slice1[k]) } slice1 = append(slice1,4) fmt.Println(len(slice1), cap(slice1)) for k := range slice1{ fmt.Println(&slice1[k]) } slice1 = append(slice1,5,6,7) fmt.Println(len(slice1), cap(slice1)) for k := range slice1{ fmt.Println(&slice1[k]) }
出力:
2 5 //长度和容量 0xc420012150 0xc420012158 3 5 //第一次追加,未超出容量,所以内存地址未发生改变 0xc420012150 0xc420012158 0xc420012160 6 10 //第二次追加,超过容量,内存地址都发生了改变,且容量也发生了改变,且是原来的2倍 0xc4200100f0 0xc4200100f8 0xc420010100 0xc420010108 0xc420010110 0xc420010118
別の例を見てください:
slice1 := make([]int, 2, 5) slice2 := slice1[:1] fmt.Println(len(slice1), cap(slice1)) for k := range slice1{ fmt.Println(&slice1[k]) } fmt.Println(len(slice2), cap(slice2)) for k := range slice2{ fmt.Println(&slice2[k]) } slice2 = append(slice2,4,5,6,7,8) fmt.Println(len(slice1), cap(slice1)) for k := range slice1{ fmt.Println(&slice1[k]) } fmt.Println(len(slice2), cap(slice2)) for k := range slice2{ fmt.Println(&slice2[k]) }
上記の出力:
2 5 //slice1的长度和容量 0xc4200700c0 0xc4200700c8 1 5 //slice2的长度和容量 0xc4200700c0 2 5 //slice2追加后,slice1的长度和容量、内存都未发生改变 0xc4200700c0 0xc4200700c8 6 10 //slice2追加后,超过了容量,所以slice2的长度和容量、内存地址都发生改变。 0xc42007e000 0xc42007e008 0xc42007e010 0xc42007e018 0xc42007e020 0xc42007e028
3. GO配列とスライスの違い
以下では主に、配列とスライスの違いを説明するためにいくつかの実際的な例を見ていきます。
1. 配列とスライスの代入形式
例 1
arr1 := [3] int {1,2,3} arr2 := arr1 for k := range arr1 { fmt.Printf("%v ",&arr1[k]); } fmt.Println(""); for k := range arr2 { fmt.Printf("%v ",&arr2[k]); } fmt.Println("\n================="); slice1 := [] int{1,2,3} slice2 := slice1 for k := range slice1 { fmt.Printf("%v ",&slice1[k]); } fmt.Println(""); for k := range slice2 { fmt.Printf("%v ",&slice2[k]); } 输出结果: 0xc420014140 0xc420014148 0xc420014150 0xc420014160 0xc420014168 0xc420014170 ================= 0xc4200141a0 0xc4200141a8 0xc4200141b0 0xc4200141a0 0xc4200141a8 0xc4200141b0
この例からわかるように、array割り当ては値のコピーであり、まったく新しい配列です。スライスの割り当ては参照 です。別の例を見てみましょう。
例 2:
arr1 := [3] int {1,2,3} arr2 := arr1 fmt.Printf("%v %v ",arr1,arr2); arr1[0] = 11 arr2[1] = 22 fmt.Printf("\n%v %v ",arr1,arr2); fmt.Println("\n================"); slice1 := [] int{1,2,3} slice2 := slice1 fmt.Printf("%v %v ",slice1,slice2); slice1[0] = 11 slice2[1] = 22 fmt.Printf("\n%v %v ",slice1,slice2); 输出结果: [1 2 3] [1 2 3] [11 2 3] [1 22 3] ================ [1 2 3] [1 2 3] [11 22 3] [11 22 3]
この例は、配列が代入およびコピーであるのに対し、スライスは単なる参照であることをもう一度示します。例 1 と 2 のスライス宣言では、隠し配列が使用されています。非表示でない配列を使用した例 3 をもう一度見てみましょう。
例 3:
arr1 := [5] int {1,2,3} slice1 := arr1[0:3] slice2 := slice1[0:4] fmt.Printf("len: %d cap: %d %v\n",len(arr1),cap(arr1),arr1); //打印出非隐藏数组 for k := range arr1 { fmt.Printf("%v ",&arr1[k]); } fmt.Println(""); fmt.Printf("len: %d cap: %d %v\n",len(slice1),cap(slice1),slice1); //打印切片1 for k := range slice1 { fmt.Printf("%v ",&slice1[k]); } fmt.Println(""); fmt.Printf("len: %d cap: %d %v\n",len(slice2),cap(slice2),slice2); //打印切片2 for k := range slice2 { fmt.Printf("%v ",&slice2[k]); } fmt.Println("\n================="); arr1[0] = 11 //非隐藏数组、切片1、切片2各自发生更改 slice1[1] = 22 slice2[2] = 33 fmt.Printf("len: %d cap: %d %v\n",len(arr1),cap(arr1),arr1); //再次打印非隐藏数组 for k := range arr1 { fmt.Printf("%v ",&arr1[k]); } fmt.Println(""); fmt.Printf("len: %d cap: %d %v\n",len(slice1),cap(slice1),slice1); //再此打印切片1 for k := range slice1 { fmt.Printf("%v ",&slice1[k]); } fmt.Println(""); fmt.Printf("len: %d cap: %d %v\n",len(slice2),cap(slice2),slice2); //再次打印切片2 for k := range slice2 { fmt.Printf("%v ",&slice2[k]); } 输出结果: len: 5 cap: 5 [1 2 3 0 0]0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170 len: 3 cap: 5 [1 2 3]0xc420012150 0xc420012158 0xc420012160 len: 4 cap: 5 [1 2 3 0]0xc420012150 0xc420012158 0xc420012160 0xc420012168 ================= len: 5 cap: 5 [11 22 33 0 0] 0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170 len: 3 cap: 5 [11 22 33] 0xc420012150 0xc420012158 0xc420012160 len: 4 cap: 5 [11 22 33 0] 0xc420012150 0xc420012158 0xc420012160 0xc42001216
上記の 3 つの例を要約すると、スライスは、非表示の配列および非表示の配列への参照を含む、配列への参照であることがわかります。
2. 配列はパラメータとして使用され、関数
によって呼び出されます。func Connect() { arr1 := [5] int {1,2,3} fmt.Printf("%v ",arr1); for k := range arr1 { fmt.Printf("%v ",&arr1[k]); } fmt.Println(""); f1(arr1) fmt.Println(""); f2(&arr1) } func f1(arr [5]int) { fmt.Printf("%v ",arr); for k := range arr { fmt.Printf("%v ",&arr[k]); } } func f2(arr *[5]int) { fmt.Printf("%v ",arr); for k := range arr { fmt.Printf("%v ",&arr[k]); } } 输出结果: [1 2 3 0 0] 0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170 [1 2 3 0 0] 0xc4200121b0 0xc4200121b8 0xc4200121c0 0xc4200121c8 0xc4200121d0 &[1 2 3 0 0] 0xc420012150 0xc420012158 0xc420012160 0xc420012168 0xc420012170
从上面的例子可以看出,数组在参数中,可以使用值传递和引用传递。值传递会拷贝新数组,引用传递则使用原数组。
func Connect() { slice1 := [] int {1,2,3} fmt.Printf("%v ",slice1); for k := range slice1 { fmt.Printf("%v ",&slice1[k]); } fmt.Println(""); f1(slice1) } func f1(slice []int) { fmt.Printf("%v ",slice); for k := range slice { fmt.Printf("%v ",&slice[k]); } } 输出结果: [1 2 3] 0xc420014140 0xc420014148 0xc420014150 [1 2 3] 0xc420014140 0xc420014148 0xc42001415
从这个例子中可以看出,切片在参数中传递本身就引用。
总结:
切片是指针类型,数组是值类型
数组的赋值形式为值传递,切片的赋值形式为引用传递
数组的长度是固定的,而切片长度可以任意调整(切片是动态的数组)
数组只有长度一个属性,而切片比数组多了一个容量(cap)属性
以上がGo言語の配列とスライスの違いは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Go Crawler Collyのキュースレッドの問題は、Go言語でColly Crawler Libraryを使用する問題を調査します。 �...

大企業または有名なオープンソースプロジェクトによって開発されたGOのどのライブラリが開発されていますか? GOでプログラミングするとき、開発者はしばしばいくつかの一般的なニーズに遭遇します...

GO言語で構造を定義する2つの方法:VARとタイプのキーワードの違い。構造を定義するとき、GO言語はしばしば2つの異なる執筆方法を見ます:最初...

redisstreamを使用してGo言語でメッセージキューを実装する問題は、GO言語とRedisを使用することです...

Go言語での文字列印刷の違い:printlnとstring()関数を使用する効果の違いはGOにあります...

マルチプロセスのログライティングの並行性セキュリティの問題を効率的に処理します。複数のプロセスが同じログファイルを同時に書き込みます。並行性が安全で効率的であることを確認する方法は?これは...

VSCODEユーザーのGolang Generic Function Typeの制約の自動削除は、VSCODEを使用してGolangコードを書くときに奇妙な問題に遭遇する可能性があります。いつ...
