回到我們的選舉場景 🚩
選舉當天,工作人員小王接到總部電話:
「小王,幫我查一下候選人 陳六 的票數。」
小王在投票所裡找了一圈,發現:根本沒有 陳六 的票箱!
這時候,小王要怎麼回報呢?
在程式世界裡,這種 「不存在」 的狀況就是 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
}
如果大家都能直接修改指標,會造成什麼問題?
就像一個開放式的票箱,任何人經過都能偷偷塞票、拔票,非常不安全。
因此我們需要 封裝 (Encapsulation):限制外部亂改,提供「安全的操作方式」。
我們可以把候選人和票數包成一個結構體,並且只能透過方法來操作:
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 票
var votes *int
*votes = 10 // ❌ 會崩潰,因為 votes 目前是 nil
👉 解法:要先建立或指向有效變數再用。
func unsafeVoteCount(counter *int) int {
return *counter // 如果 counter 是 nil → 崩潰
}
👉 解法:永遠檢查 nil!
if counter == nil {
return 0
}
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 判斷與封裝技巧,我們學到:
👉 在下一篇,我們要進一步學 struct,真正把候選人的「名字」和「票數」整合在一起,打造一個完整的選舉管理系統。