Go | 結構型別與指標的差異
💬 簡介
在 Go 語言中,結構型別(struct
)是用來組織不同資料類型的集合,並且提供了許多在實際開發中常見的應用方式。在操作結構型別時,經常會遇到是否應該使用指標來創建結構的問題。本篇文章將解釋結構型別與指標之間的差異,以及如何根據不同需求選擇使用指標或不使用指標來創建結構實例。
圖片來源:Gophers
🔍 結構型別與指標的差異
1️⃣ 不使用指標創建結構
當我們不使用指標創建結構實例時,我們會創建結構的副本。這表示對結構資料的修改不會影響到原本的結構實例。
- 範例:不使用指標創建結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package main
import "fmt"
// 定義結構
type Person struct {
Name string
Age int
}
func main() {
// 不使用指標創建結構實例
p := Person{"Alice", 30}
fmt.Println(p) // 輸出: {Alice 30}
// 將 p 賦值給另一個變數
p2 := p
p2.Name = "Bob" // 改變 p2 的 Name 欄位
// 查看原始結構 p 和 p2
fmt.Println(p) // 輸出: {Alice 30} (原始結構未改變)
fmt.Println(p2) // 輸出: {Bob 30} (p2 的 Name 欄位被修改)
}📝 在這個範例中,我們使用
&Person{"Alice", 30}
創建了一個Person
結構的指標,並初始化了Name
和Age
欄位的值。這是一種快速且直接的方式。說明:
p
和p2
是兩個獨立的結構實例。對p2
進行修改並不會影響p
,因為它們是資料的副本。
2️⃣ 使用指標創建結構
當我們使用指標創建結構時,我們創建的是結構的指標,而不是資料的副本。這意味著所有對結構資料的修改會直接影響到原始結構,因為指標指向的是同一塊記憶體區域。
- 範例:使用指標創建結構
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package main
import "fmt"
// 定義結構
type Person struct {
Name string
Age int
}
func main() {
// 使用指標創建結構實例
p := &Person{"Alice", 30} // 創建結構的指標
fmt.Println(p) // 輸出: &{Alice 30}
// 通過指標修改結構
p.Name = "Bob" // 修改結構中的 Name 欄位
// 查看結構 p
fmt.Println(p) // 輸出: &{Bob 30} (指標已修改)
}說明:
p := &Person{"Alice", 30}
創建了一個指向Person
結構的指標。
修改p.Name
會直接影響結構的內容,因為p
是指向結構的指標。
🧐 為什麼要使用指標?
1. 效率
如果結構包含大量欄位或複雜資料結構,傳遞副本會花費較多的記憶體和時間。因此,當結構較大時,使用指標來傳遞結構能提高效能,因為我們只傳遞記憶體地址,而不是整個結構的複製。
2. 修改資料
使用指標讓我們能夠在函式內修改原始結構的資料。當我們傳遞結構的指標時,對指標資料的修改會直接影響到原始資料,這在需要修改結構資料的情況下非常有用。
⚖️ 使用指標與不使用指標的比較
⚖️ 特性 | 不使用指標創建結構 | 使用指標創建結構 |
---|---|---|
資料傳遞方式 | 傳遞的是結構的副本,修改不會影響原結構 | 傳遞的是結構的指標,修改會影響原結構 |
效率 | 如果結構很大,傳遞副本可能會有性能問題 | 傳遞指標比較高效,尤其是結構很大時 |
可修改性 | 修改副本不會改變原始資料 | 修改指標指向的資料會影響原始結構 |
適用場景 | 結構較小或不需要修改資料的情況 | 需要修改結構或避免複製資料時 |
🚀 何時使用 指標結構 或 不使用指標結構?
📌 何時使用指標結構
- 結構需要被修改
- 當你希望修改結構的內容並且這些變更應該影響原始結構時,使用指標結構是必須的。
- 例如,當你將結構作為函式參數傳遞時,如果希望函式內部對結構的修改能反映在原始結構上,就必須使用指標。
- 避免資料複製 (效率考量)
- 當結構很大時(例如,包含大量欄位或大資料型別),傳遞指標會比傳遞副本高效得多。
- 如果不使用指標,Go 會將結構的副本傳遞給函式或方法,這可能會導致性能問題,特別是結構很大時。
- 實現共享資料
- 當你希望多個地方共享同一個資料並能夠進行修改時,使用指標會使得資料在不同的地方能夠直接互動。
- 這對於需要全局或多個函式共享狀態的情況非常有用。
- 避免不必要的資料複製
- 當你的結構比較大,且你不需要複製資料,只是想在函式或方法中進行資料操作時,使用指標會減少不必要的資料複製。
📌 何時不使用指標結構
- 結構較小且不需要修改
- 如果你的結構比較小(例如,只有少數欄位或簡單型別),而且不需要在函式內進行修改,那麼使用非指標結構會更簡潔。
- 這樣會避免額外的指標解引用操作,並且程式碼會更簡潔易懂。
- 避免指標帶來的額外複雜性
- 使用指標結構可能需要額外的空間來儲存指標,並且在解引用指標時,可能會引入額外的錯誤(如空指標錯誤)。如果不需要修改結構,使用非指標結構會避免這些額外的複雜性。
- 傳遞資料不需要被修改
- 如果函式或方法不需要修改傳遞的資料,且資料結構比較小,傳遞結構的副本會更清晰且簡單。
- 避免因為指標引起的錯誤
- 當不需要修改結構時,使用指標可能會引入空指標錯誤(
nil pointer dereference
)。如果你不需要修改資料,使用副本可以避免這種風險。
- 當不需要修改結構時,使用指標可能會引入空指標錯誤(
📊 簡單的應用場景比較
📊 簡單的應用場景比較 | 使用指標結構的場景 | 不使用指標結構的場景 |
---|---|---|
當結構需要在多個地方被修改 | 當結構需要在多個地方被修改 | 當結構較小且不需要修改資料 |
當結構很大,傳遞副本會影響效能 | 當結構很大,傳遞副本會影響效能 | 當結構很小,傳遞副本對效能影響不大 |
當多個函式需要共享資料並修改資料 | 當多個函式需要共享資料並修改資料 | 當你只需要傳遞資料而不修改 |
當你希望避免不必要的資料複製 | 當你希望避免不必要的資料複製 | 當結構不大,且資料不需要修改 |
🎯 總結
- 不使用指標:適用於結構較小且不需要修改的情況,資料會被複製,原始結構不會受到影響。
- 使用指標:適用於結構較大或需要修改資料的情況,指標傳遞可以提高效能並且允許在函式中修改原始結構的資料。
最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。
註:以上參考了
Go