指標基礎#

x := 42
p := &x      // 取址
*p = 100     // 透過指標修改值
fmt.Println(x)  // 100

// new 函式
ptr := new(int)  // 配置記憶體並回傳指標
*ptr = 42

可定址性規則#

不是所有值都可以取址。以下值不可定址

  • 常數
  • 字面量(除了複合字面量)
  • 函式呼叫的回傳值
  • 型別轉換的結果
  • 算術運算結果
// 不可定址
// _ = &10        // 錯誤:常數不可定址
// _ = &"hello"   // 錯誤:字串字面量不可定址
// _ = &getValue() // 錯誤:函式回傳值不可定址

// 可定址
var x int = 10
_ = &x           // 正確:變數可定址

s := []int{1, 2, 3}
_ = &s[0]        // 正確:切片元素可定址

// 注意:陣列字面量元素不可定址
// arr := [3]int{1, 2, 3}
// _ = &arr[0]   // 正確:變數陣列的元素可定址
// _ = &([3]int{1, 2, 3}[0])  // 錯誤:字面量陣列的元素不可定址

unsafe.Pointer#

unsafe.Pointer 是通用指標型別,可以在不同指標型別間轉換:

import "unsafe"

var x int64 = 1

// 取得 x 的位址並轉換為 unsafe.Pointer
ptr := unsafe.Pointer(&x)

// 轉換為 *int64 並存取
y := *(*int64)(ptr)
fmt.Println(y)  // 1

// 指標運算
// 取得 x 後面一個位元組的位址
nextByte := unsafe.Pointer(uintptr(ptr) + 1)

unsafe.Pointer 繞過了 Go 的型別安全機制,要謹慎使用。錯誤的使用可能導致記憶體損壞或未定義行為。

常見的 unsafe 操作模式
// 模式 1:不同指標型別轉換
var f float64 = 1.5
bits := *(*uint64)(unsafe.Pointer(&f))

// 模式 2:指標運算
type Header struct {
    Length int
    Data   [10]byte
}

h := &Header{Length: 5}
dataPtr := unsafe.Pointer(uintptr(unsafe.Pointer(h)) +
                          unsafe.Offsetof(h.Data))

// 模式 3:與 C 程式碼互動(cgo)
// 把 Go 指標轉換為 C 可用的指標

指標 vs 值#

// 回傳指標的函式
func NewPerson(name string) *Person {
    return &Person{Name: name}  // Go 會自動把變數分配到堆上
}

// 回傳值的函式
func CreatePoint(x, y int) Point {
    return Point{x, y}  // 小型結構體,複製成本低
}

用指標

  1. 需要修改接收者/參數
  2. 結構體較大(通常 > 64 位元組)
  3. 需要表示「無值」的狀態(nil)

用值

  1. 小型、不可變的資料
  2. 基本型別(int、string 等)
  3. 不需要共用狀態