iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Modern Web

後端攻略筆記系列 第 8

Day 8 : Go 指標與結構 — 篇二:處理指標的 Nil 判斷與安全操作技巧

  • 分享至 

  • xImage
  •  

Go 指標與結構 — 篇二:處理指標的 Nil 判斷與安全操作技巧

當票箱不存在時:理解 nil 指標

回到我們的選舉場景 🚩
選舉當天,工作人員小王接到總部電話:

「小王,幫我查一下候選人 陳六 的票數。」

小王在投票所裡找了一圈,發現:根本沒有 陳六 的票箱!
這時候,小王要怎麼回報呢?

在程式世界裡,這種 「不存在」 的狀況就是 nil 指標


什麼是 nil 指標?

  • 指標本質上是一個地址,用來指向某個變數。
  • 如果一個指標沒有指向任何有效位置,它的值就是 nil

範例:

package main

import "fmt"

func main() {
    var missingCounter *int // 沒有初始化,因此是 nil

    fmt.Println(missingCounter) // <nil>
    // fmt.Println(*missingCounter)  // ❌ 會當場崩潰(因為取出 nil 的值)
}

執行結果:

<nil>

➡️ 嘗試對 nil 解參考(*nil)就像嘗試打開不存在的票箱 → 程式直接崩潰!


安全的 VoteCount 函式:防止系統崩潰

我們設計一個「安全查票」的函式:

// VoteCount 讀取票數,如果票箱不存在就回傳 0
func VoteCount(counter *int) int {
    if counter == nil {
        fmt.Println("⚠️ 票箱不存在,回傳 0 票")
        return 0
    }
    return *counter
}

示範:

func main() {
    // 情境1:存在的票箱
    votes := 10
    counter := &votes
    fmt.Println("正常票箱 →", VoteCount(counter)) // 10

    // 情境2:不存在的票箱
    var missingCounter *int
    fmt.Println("不存在的票箱 →", VoteCount(missingCounter)) // 0
}

封裝與安全性:為什麼不讓外部直接修改?

如果大家都能直接修改指標,會造成什麼問題?

  • 任何人都能隨意把票數改成 999
  • 誰加了票完全沒有紀錄
  • 系統失去可靠性

就像一個開放式的票箱,任何人經過都能偷偷塞票、拔票,非常不安全。

因此我們需要 封裝 (Encapsulation):限制外部亂改,提供「安全的操作方式」。


設計更安全的票箱:VoteBox

我們可以把候選人和票數包成一個結構體,並且只能透過方法來操作:

type VoteBox struct {
    candidate string
    votes     *int   // 真正票數放在指標裡
    logs      []string
}

// 建立新的候選人票箱
func NewVoteBox(name string) *VoteBox {
    initial := 0
    return &VoteBox{
        candidate: name,
        votes:     &initial,
        logs:      []string{},
    }
}

// 安全加票
func (vb *VoteBox) AddVote(operator string) {
    if vb.votes != nil {
        *vb.votes++
        vb.logs = append(vb.logs, fmt.Sprintf("%s 幫 %s 加票,目前:%d 票", operator, vb.candidate, *vb.votes))
    }
}

// 查票
func (vb *VoteBox) GetVoteCount() int {
    if vb.votes == nil {
        return 0
    }
    return *vb.votes
}

// 顯示投票紀錄
func (vb *VoteBox) ShowLogs() {
    fmt.Println("=== 投票紀錄 ===")
    for _, log := range vb.logs {
        fmt.Println(log)
    }
}

使用範例:

func main() {
    aliceBox := NewVoteBox("Alice")
    aliceBox.AddVote("王小明")
    aliceBox.AddVote("李小美")

    fmt.Println("Alice 目前票數:", aliceBox.GetVoteCount())
    aliceBox.ShowLogs()
}

輸出:

Alice 目前票數: 2
=== 投票紀錄 ===
王小明 幫 Alice 加票,目前:1 票
李小美 幫 Alice 加票,目前:2 票

初學者常見的指標誤區

1. 忘記初始化就直接用

var votes *int
*votes = 10  // ❌ 會崩潰,因為 votes 目前是 nil

👉 解法:要先建立或指向有效變數再用。


2. 忘記檢查 nil

func unsafeVoteCount(counter *int) int {
    return *counter  // 如果 counter 是 nil → 崩潰
}

👉 解法:永遠檢查 nil!

if counter == nil {
    return 0
}

3. 混淆「值」和「指標」

func main() {
    x := 5

    // 傳值 → 外面不會變
    func(y int) { y = 10 }(x)
    fmt.Println(x) // 5

    // 傳指標 → 外面會被改
    func(y *int) { *y = 10 }(&x)
    fmt.Println(x) // 10
}

小結

透過 nil 判斷與封裝技巧,我們學到:

  1. nil 的意義:代表不存在,就像沒有票箱
  2. 安全操作:避免直接操作 nil,確保系統穩定
  3. 封裝設計:用方法限制外部隨意修改,提升安全性
  4. 誤區提醒:初始化、nil 檢查、區分「值 vs 指標」

👉 在下一篇,我們要進一步學 struct,真正把候選人的「名字」和「票數」整合在一起,打造一個完整的選舉管理系統。



上一篇
Day 7 : Go 指標基礎 範例:選舉計票系統入門
下一篇
Day 9 : Go 指標與結構 — 篇三:結構體(struct)與選舉系統案例實作
系列文
後端攻略筆記13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言