Like Share Discussion Bookmark Smile

J.J. Huang   2025-03-13   Getting Started Golang 05.結構   瀏覽次數:次   DMCA.com Protection Status

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
    23
    package 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 結構的指標,並初始化了 NameAge 欄位的值。這是一種快速且直接的方式。

    說明:

    pp2 是兩個獨立的結構實例。對 p2 進行修改並不會影響 p,因為它們是資料的副本。

2️⃣ 使用指標創建結構

當我們使用指標創建結構時,我們創建的是結構的指標,而不是資料的副本。這意味著所有對結構資料的修改會直接影響到原始結構,因為指標指向的是同一塊記憶體區域。

  • 範例:使用指標創建結構
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package 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