為了更好地理解 Go 語言中的 切片(slice),我們可以將它與 C++ 中的陣列或容器(如 std::vector
)進行比較,但要注意的是,它們之間有一些關鍵的區別。讓我們逐步將 Go 的切片與 C++ 中的概念進行對應:
1. 陣列 vs 切片
在 C++ 中,陣列(array) 是一種固定大小的資料結構,大小必須在編譯時確定,並且不能動態調整。例如:
int arr[5] = {1, 2, 3, 4, 5}; // C++ 中的靜態陣列,大小為5
Go 語言中的 陣列 也是固定大小的,一旦定義,其長度是不可變的。例如:
var arr [5]int = [5]int{1, 2, 3, 4, 5} // Go中的陣列,大小為5
但是 Go 中的 切片(slice) 是一種動態、可變長度的結構,相當於在 C++ 中使用 std::vector
。Go 的切片是對底層陣列的引用,可以在執行時動態調整大小。
2. 切片 vs std::vector
C++ 中的 std::vector
與 Go 語言中的 切片(slice) 比較類似。兩者都提供了可變長度的陣列功能,並且它們都可以動態增加或縮小。
- C++ 中的
std::vector
:
#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5}; // C++ 中的動態陣列
vec.push_back(6); // 動態增加元素
- Go 中的 切片:
slice := []int{1, 2, 3, 4, 5} // Go 中的切片
slice = append(slice, 6) // 動態增加元素
在 C++ 中,std::vector
可以動態調整大小,並自動管理底層記憶體。同樣地,Go 的切片也可以透過 append
函式動態增加元素。就像 std::vector
可以在其容量不足時自動分配更多記憶體,Go 的切片也是如此。
3. 長度和容量
-
在 C++ 的
std::vector
中,容量(capacity)指的是分配的記憶體空間大小,可以容納的元素數量,而長度(size)指的是實際儲存的元素個數。std::vector<int> vec = {1, 2, 3}; int size = vec.size(); // 當前儲存的元素個數,輸出: 3 int capacity = vec.capacity(); // 分配的記憶體容量
-
在 Go 中,切片也有類似的概念:
- 長度(len):切片中實際包含的元素個數。
- 容量(cap):從切片的起始位置到底層陣列末尾的元素數量。
slice := []int{1, 2, 3} fmt.Println(len(slice)) // 輸出:3 fmt.Println(cap(slice)) // 輸出:3
當向 std::vector
或切片追加元素時,容量不足時會重新分配更大的記憶體空間。
4. 底層陣列和記憶體共享
在 C++ 中,當你操作 std::vector
時,通常是一個獨立的記憶體塊,每次操作都不會影響到其他 vector
。然而,在 Go 中,切片是共享底層陣列的。如果從一個陣列或切片中建立另一個切片,多個切片可以共享同一底層陣列。這與 C++ 的陣列指標類似。
例如,假設我們有一個 C++ 的陣列指標:
int arr[] = {1, 2, 3, 4, 5};
int* ptr = arr + 1; // 指向陣列的第二個元素
ptr[0] = 100; // 修改陣列
這種情況下,修改 ptr
所指向的內容會影響到原始陣列。
在 Go 中,切片共享底層陣列的特性與此類似:
arr := [5]int{1, 2, 3, 4, 5}
slice1 := arr[1:4] // 建立一個從索引1到3的切片
slice1[0] = 100 // 修改 slice1 的第一個元素,影響了底層陣列
fmt.Println(arr) // 輸出: [1 100 3 4 5]
5. append
函式的行為
當我們在 Go 中向切片追加元素時,底層陣列的容量可能會重新分配。當容量不足時,Go 會建立一個新的底層陣列,並將現有的資料複製過去,這與 C++ 中 std::vector
的擴容機制類似。
- 在 C++ 中,當
std::vector
容量不足時,會自動分配更多記憶體,現有元素會被複制到新的記憶體空間中。 - 在 Go 中,
append
可能會導致重新分配底層陣列,類似地也會將現有資料複製到新的陣列中。
std::vector<int> vec = {1, 2, 3};
vec.push_back(4); // 如果容量不足,重新分配記憶體
Go 中的對應邏輯:
slice := []int{1, 2, 3}
slice = append(slice, 4) // 如果容量不足,重新分配底層陣列
6. C++ 中的指標 vs Go 中的切片
在 C++ 中,如果你透過指標運算元組的部分元素,或者傳遞陣列指標到函式內,這種行為類似於 Go 中的切片。例如:
- C++ 中透過指標運算元組:
void modifyArray(int* arr) {
arr[0] = 100;
}
int main() {
int arr[] = {1, 2, 3};
modifyArray(arr);
// arr[0] 現在是 100
}
- 在 Go 中,切片類似於陣列的引用,因此可以在函式中直接修改底層陣列:
func modifySlice(s []int) {
s[0] = 100
}
func main() {
arr := []int{1, 2, 3}
modifySlice(arr)
fmt.Println(arr) // 輸出: [100 2 3]
}
7. 總結:C++ vs Go 切片理解
- 切片與 C++ 的
std::vector
相似,因為兩者都可以動態擴充套件,並且容量和長度是分開的概念。 - 切片共享底層陣列,類似於在 C++ 中透過指標訪問陣列的某部分,多個切片可以引用同一塊記憶體。
- Go 切片的追加操作(
append
)與 C++ 中std::vector
的擴容機制類似,當容量不足時,會自動分配新的更大的底層陣列。 - 切片的指標行為 類似於 C++ 中透過指標運算元組元素的方式。
理解這些相似點和不同點,可以幫助你更快適應 Go 的切片操作,特別是在你熟悉 C++ 的陣列和 std::vector
時。