iT邦幫忙

2024 iThome 鐵人賽

DAY 12
1
Modern Web

Go 快 Go 高效: 從基礎語法到現代Web應用開發系列 第 12

【Day12】Golang 核心語法 | 指標與記憶體管理(Pointers & Memory Management)

  • 分享至 

  • xImage
  •  

在介紹 Golang 中的指標之前,我們可以先對比一下它和 C 語言中的指標有何不同。這樣有助於理解 Golang 如何優化和簡化指標的使用。

Golang 與 C 語言中的指標差異

1. 記憶體操作的安全性

  • C 語言:C 語言中的指標非常靈活,允許你直接操作記憶體地址,甚至能執行危險的操作,如指向一個未經初始化或已釋放的記憶體區塊。這樣的靈活性伴隨著高風險,容易引發緩衝區溢位(buffer overflow)或段錯誤(segmentation fault)。
  • Golang:Golang 刻意簡化了指標的使用,避免了直接操作記憶體地址。Golang 中不支持指標運算(如 p++ 或 p--),這樣設計是為了降低錯誤發生的可能性,提高程式的安全性。

2. 指標的零值

  • C 語言:C 中的指標在未初始化時,可能指向隨機的記憶體地址,容易導致未定義的行為。
  • Golang:Golang 的指標在宣告時默認為 nil,並且在使用指標前進行 nil 檢查是一種常見的模式,從而減少了潛在的崩潰風險。

3. 垃圾回收

  • C 語言:C 中使用指標時,程式員必須手動管理記憶體,這意味著你需要自己負責釋放不再需要的記憶體(例如使用 free() 函數)。如果記憶體管理不當,可能會導致記憶體洩漏或重複釋放錯誤。
  • Golang:Golang 使用自動垃圾回收機制(Garbage Collection, GC),程式員不需要顧慮手動釋放記憶體。這減少了記憶體管理的負擔,讓程式碼更為簡潔和安全。

指標的宣告與初始化

指標(Pointer)是存放變數記憶體地址的變數。在 Golang 中,指標的宣告語法如下:

  • 指標宣告
var p *int
  • *int 代表這是一個指向 int 型態的指標變數。
  • 當一個指標變數被宣告但未初始化時,它的值為 nil,表示它還沒有指向任何變數的地址。
  • 指標的初始化與賦值

在 Golang 中,我們可以通過取地址符號 & 來取得一個變數的記憶體地址,並將這個地址賦值給指標:

var a int = 42   // 宣告一個整數變數 a,並賦值為 42
var p *int = &a  // 宣告指標變數 p,並將 a 的地址賦值給它
  • &a 表示取變數 a 的地址,並將這個地址賦值給指標 p

  • 使用範例:

package main

import "fmt"

func main() {
    var a int = 42   // 宣告變數 a 並賦值
    var p *int       // 宣告指標變數 p

    p = &a           // 將 a 的地址賦值給 p

    fmt.Println("a 的值是:", a)
    fmt.Println("p 指向的值是:", *p)

    *p = 100         // 修改 p 所指向的變數值
    fmt.Println("修改後 a 的值是:", a)
}
</* Output: */>
a 的值是: 42
p 指向的值是: 42
修改後 a 的值是: 100

動態分配記憶體

new 函數會分配記憶體空間並返回指向這塊記憶體的指標。使用 new 時,記憶體會自動初始化為該型別的零值(zero value)。它適合在你不需要立即賦值,只想初始化一個變數並分配指標的情況下使用。

var p *int = new(int)  // 分配一個 int 的記憶體並返回指向它的指標
*p = 42                // 為這個指標指向的內存賦值
  • 這裡 new(int) 創建了一個 int 型態的變數,並返回該變數的指標,這個變數的初始值為零值 0

記憶體管理與性能提升

在處理大型數據結構時,指標可以顯著提高性能。傳遞指標而不是值,減少了記憶體的複製,並且能提升執行效率。

func modifySlice(s *[]int) {
    for i := range *s {
        (*s)[i] *= 2  // 這裡使用指標直接修改 slice 內容
    }
}

func main() {
    slice := []int{1, 2, 3, 4, 5}
    modifySlice(&slice)  // 傳遞 slice 的指標
    fmt.Println(slice)
}
</* Output: */>
[2, 4, 6, 8, 10]

在這個範例中,modifySlice 函數透過指標直接修改了 slice 的內容。這樣做避免了創建 slice 副本,從而減少了記憶體開銷。


總結

在這次的介紹中,我們探討了 Golang 中指標的使用及其與 C 語言的不同之處。Golang 的指標設計更加安全,避免了直接的記憶體操作和指標運算,同時透過自動垃圾回收簡化了記憶體管理。我們學到了如何正確宣告、初始化和操作指標,以及如何使用 new 函數進行動態記憶體分配。此外,透過傳遞指標可以有效提升性能,特別是在處理大型數據結構時。我們還學習了如何避免過度使用指標並正確處理 nil 指標,以確保程式的穩定性和一致性。


上一篇
【Day11】Golang 核心語法 | 映射(Map)與鍵值對操作
下一篇
【Day13】Golang 管理程式碼邏輯 | 函數與方法(Functions & Methods)
系列文
Go 快 Go 高效: 從基礎語法到現代Web應用開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言