1. Tai sao can con tro va vi sao quanh chung co nhieu truyen thuyet den vay
Neu ban tung nghe cau “con tro rat dang so”, thi ban khong phai la nguoi duy nhat: voi nhieu lap trinh vien, nghe den “pointer” la ben trong lai hoi ngo ve C/C++ voi nhung lan sut day kho hieu va “bo nho hong”. Nhung trong Go, y tuong nay yen tam va thuc te hon nhieu: con tro chi don gian la mot gia tri luu dia chi cua mot gia tri khac.
Chung ta se xem con tro nhu mot cong cu cho hai viec: (1) cho phep ham thay doi mot bien cu the “ben ngoai”, va (2) hieu duoc code co lien quan den dia chi (bao gom ca fmt.Scan(&x) quen thuoc).
Hay dung mot tu vung co ban, khong mau thuat:
- Gia tri — vi du so 10.
- Bien — “hop” chua gia tri (vi du x := 10).
- Dia chi — “toa do cua hop” trong bo nho.
- Con tro — bien ma ben trong no luu dia chi cua mot bien khac.
De khong bien dieu nay thanh triet hoc, phan sau se co nhieu vi du nho.
2. Kieu *T: cach doc dau sao trong kieu
Khi ban thay kieu *T, hay dung lai mot chut va doc to len cho dung: “con tro den T”. Nghia la day khong phai “phep nhan voi T” hay “dau sao cho dep”, ma la mot phan cua kieu.
Vi du:
- int — so nguyen thong thuong.
- *int — con tro den so nguyen, tuc la gia tri luu dia chi noi ma int nam.
- string — chuoi.
- *string — dia chi cua chuoi (chinh xac hon la dia chi cua bien luu gia tri chuoi).
Mot thoi quen huu ich cho nguoi moi la luon giu trong dau mot bang nho:
| Cach viet | Cach doc | Ben trong luu gi |
|---|---|---|
|
gia tri kieu T | chinh gia tri do (10, "hi", …) |
|
con tro den T | dia chi noi ma T nam |
Va them mot chi tiet quan trong: con tro cung la mot gia tri. Ban co the gan no, so sanh voi nil, truyen no vao ham. Chi co “noi dung” cua gia tri nay la dia chi.
3. Toan tu &: cach lay dia chi cua bien
Bay gio den “ma thuat” chinh cua nhung ngay truoc: &x. Toan tu nay duoc goi la “lay dia chi”. No lay dia chi cua mot gia tri co the lay dia chi va tra ve con tro.
Vi du don gian nhat:
package main
import "fmt"
func main() {
x := 10
p := &x // p co kieu *int
fmt.Println(x) // 10
_ = p
}
O day co mot dieu quan trong xay ra: bien p khong “sao chep x”, khong “nhin x theo mot cach ma thuat”. No chi don gian luu dia chi cua x.
Gia tri co the lay dia chi: ap dung & cho cai gi
Ky hieu & khong the gan vao dau cung duoc. No chi hoat dong o noi nao ton tai “mot o bo nho that su” co y nghia ve dia chi. Thuong la:
- bien (x := 10, vay &x la hop le),
- phan tu cua slice (&nums[i] la hop le),
- mot so thu khac ma sau nay chung ta se gap (vi du cac truong cua struct — nhung hien tai chua can den struct).
Con nhung truong hop sau thi & khong the ap dung (va dieu nay la tot, vi bo dich se dung ban lai):
package main
func main() {
x := 10
_ = &(x + 1) // khong duoc: (x + 1) la bieu thuc tam thoi, no khong co “o”
_ = &10 // khong duoc: literal khong phai gia tri co the lay dia chi
_ = x
}
Y nghia don gian: x la mot cai hop, no co dia chi. x + 1 la “tinh ngay luc do”, khong co hop, nen cung khong co dia chi.
4. Giai tham chieu *p: cach doc va cach thay doi gia tri theo dia chi
Bay gio den dau sao thu hai, thuong lam moi nguoi roi: *p trong bieu thuc. Day la phep giai tham chieu (dereference). Neu p luu dia chi, thi *p co nghia la: “lay gia tri nam tai dia chi do”.
Truoc tien la doc:
package main
import "fmt"
func main() {
x := 10
p := &x
fmt.Println(*p) // 10
}
Bay gio den phan thu vi nhat: giai tham chieu co the nam ben trai cua =, tuc la ban co the ghi gia tri moi vao dia chi:
package main
import "fmt"
func main() {
x := 10
p := &x
*p = 42
fmt.Println(x) // 42
}
Day la y tuong then chot: chung ta khong “doi con tro”, ma doi du lieu ma no tro toi.
Mot phep so sanh hay: p la mot manh giay co dia chi cua can ho. *p la thu nam ben trong can ho. Ban co the doc no (hien tai ben trong co gi), va cung co the dem do moi vao (ghi de gia tri).
5. Con tro nil: “khong tro den dau ca”, va vi sao dieu do quan trong
Moi kieu trong Go deu co zero value — gia tri mac dinh. Voi con tro, zero value la nil. Day la mot gia tri dac biet co nghia la: “khong co dia chi, khong tro den dau ca”.
Vi du:
package main
import "fmt"
func main() {
var p *int // zero value => nil
fmt.Println(p == nil) // true
}
Va day la diem an toan quan trong nhat: neu ban thu giai tham chieu nil, chuong trinh se bi panic. Day khong phai “loi bien dich”, ma la ket thuc bat thuong trong luc chay.
package main
import "fmt"
func main() {
var p *int // nil
fmt.Println(p == nil) // true
fmt.Println(*p) // panic: invalid memory address or nil pointer dereference
}
Nghe co ve dang so, nhung thuc ra day la mot quy tac huu ich: khong the “doc du lieu tu khong khi”.
He qua thuc te rat don gian: neu theo y nghia cua chuong trinh, con tro co the la nil, ban phai kiem tra truoc khi giai tham chieu bang cach dung p != nil.
6. Dia chi trong dau ra va so do trong bo nho
Cach “nhin thay” dia chi: fmt.Printf("%p", p)
Nguoi moi thuong thieu cam giac “thuc te” ve dieu dang dien ra: cung noi den “dia chi”, nhung no la gi? Trong Go co the in gia tri con tro (dia chi) thong qua dinh dang "%p".
package main
import "fmt"
func main() {
x := 7
p := &x
fmt.Printf("p=%p\n", p) // p=0x.... (dia chi se khac)
fmt.Printf("*p=%d\n", *p) // *p=7
}
O day can tranh nham lan:
- "%p" — in dia chi (tuc gia tri cua con tro),
- "%d", "%v" va tuong tu — in gia tri, vi vay voi con tro thuong se in *p, khong phai p.
Va dung vay, dia chi se khac nhau moi lan — do la binh thuong. Chung ta khong co gang nho dia chi nhu so dien thoai cua ban be; "%p" la cong cu de hieu va chan doan.
So do nho: cai gi nam o dau va ai dang nhin vao ai
Co khi chi can mot hinh la thay the nam doan. Hay tuong tuong tinh huong nay:
x := 10
p := &x
Co the bieu dien so hoa nhu sau:
┌───────────┐ ┌───────────┐
│ bien │ │ bien │
│ x │ │ p │
│ gia tri │ │ gia tri │
│ 10 │ │ dia chi x│
└───────────┘ └───────────┘
▲ │
└─────────────────┘
p tro den x
Va phep giai tham chieu *p la “di theo mui ten va lay cai dang nam trong x”.
7. Con tro trong ham: vi du voi so du
Bay gio den mot diem quan trong: chung ta khong chi hoc cu phap cho vui. Con tro bat dau co ich khi chung ta co ham.
Hay tiep tuc phong cach bai tap “ung dung CLI nho”, lam mot dieu de hieu: gia su co mot “ong tiet kiem” nho. Chung ta co so du (balance), va muon goi ham deposit de nap them vao so du.
Nap so du thong qua con tro
package main
import "fmt"
func deposit(balance *int, amount int) {
*balance += amount
}
func main() {
balance := 0
deposit(&balance, 50)
fmt.Println(balance) // 50
}
Tai sao o day dung con tro la hop ly? Vi deposit nhan dia chi cua bien cu the balance va thay doi no “ngay tai cho”. Khong co con tro, chung ta se phai tra ve gia tri moi va gan lai. Cach do cung hoan toan on, nhung hom nay muc tieu cua chung ta la hieu co che cua con tro.
Diem nguy hiem: se ra sao neu truyen nil
Neu ban goi deposit(nil, 50), ben trong se xay ra *balance += amount, ma balance lai la nil. Dieu nay se dan den panic. Vi vay trong code thuc te nguoi ta thuong them bao ve:
package main
import "fmt"
func deposit(balance *int, amount int) {
if balance == nil {
return
}
*balance += amount
}
func main() {
var p *int // nil
deposit(p, 50)
fmt.Println(p == nil) // true
}
Hay nho, neu con tro co the la nil, ham phai hoac kiem tra, hoac co hop dong “khong bao gio nil”. Chon cach nao con tuy vao bai toan, nhung ban than dieu do can duoc nhan thuc ro.
8. Vi sao fmt.Scan(&x) can &x
Chung ta da hen se giai thich “phep mau” cu trong viec nhap du lieu. Bay gio la luc he lo.
Khi ban viet:
fmt.Scan(&x)
thi dieu nay duoc hieu la: “ham Scan phai ghi gia tri da doc vao bien x”. De lam duoc dieu do, no can dia chi cua x. Neu chi truyen x, thi Scan se nhan duoc mot ban sao cua gia tri (vi du 0) va... no se ghi du lieu vao dau? Vao ban sao se bien mat sau do? Nhu vay la vo ich.
Hay nhin vao mot phep so sanh don gian — ham “ghi” mot so co the se hoat dong nhu the nao:
package main
import "fmt"
func setTo100(p *int) {
*p = 100
}
func main() {
x := 0
setTo100(&x)
fmt.Println(x) // 100
}
O day setTo100 lam cung co che ma Scan lam: nhan dia chi va ghi vao do.
9. Vi du ve cach doc code va tinh co the lay dia chi
Rat huu ich khi hoc cach doc con tro bang mat, vi sau nay ban se gap chung trong code thuc te.
Hai con tro cung tro den mot bien
package main
import "fmt"
func main() {
x := 5
p1 := &x
p2 := &x
*p1 = 9
fmt.Println(*p2) // 9
}
Dieu nay hoan toan binh thuong: nhieu con tro khac nhau co the tro den cung mot noi. Neu thay doi gia tri qua mot con tro, con lai se thay thay doi do.
Con tro khong phai la gia tri
package main
import "fmt"
func main() {
x := 5
p := &x
fmt.Println(p == &x) // true (co le la true, vi day la cung mot dia chi)
fmt.Println(*p == x) // true (cac gia tri cung bang nhau)
}
Phep so sanh thu nhat la so sanh dia chi, phep so sanh thu hai la so sanh du lieu. Day la hai muc khac nhau.
Co the lay dia chi: vi du voi slice
Ban da biet slice, va day la vi du tuyet voi ve tinh “co the lay dia chi” ma khong can them chu de moi.
package main
import "fmt"
func main() {
nums := []int{10, 20, 30}
p := &nums[1]
*p = 99
fmt.Println(nums) // [10 99 30]
}
O day nums[1] la mot phan tu that trong bo nho, no co dia chi, nen &nums[1] la hop le. Con &(nums[1] + 1) thi khong duoc, vi day la bieu thuc tam thoi.
10. Loi thuong gap khi lam viec voi &, *T va nil
Loi 1: nham lan *T (kieu) va *p (thao tac).
Suy nham pho bien nhat cua nguoi moi la “dau sao trong kieu” va “dau sao trong bieu thuc”. Trong kieu *int, dau sao co nghia la “con tro den int”. Trong bieu thuc *p, dau sao co nghia la “giai tham chieu con tro p”. De dau oc khong bi nong, hay tu nhac nho: “dau sao trong kieu” va “dau sao trong code”.
Loi 2: giai tham chieu nil va bi panic.
nil voi con tro la gia tri mac dinh binh thuong, va khong co gi xau ve dieu do. Van de xay ra khi code lam *p ma khong kiem tra p != nil. Neu theo y nghia chuong trinh p co the la nil, can hoac kiem tra, hoac dam bao khoi tao tu truoc va coi nil la loi hop dong.
Loi 3: co gang lay dia chi cua thu ma khong nen co dia chi.
Cac cau truc nhu &10, &(x+1) hoac &f() se khong bien dich — va day khong phai do Go “kho tinh”, ma la co che bao ve ban khoi nhung dia chi vo nghia. Chi co the lay dia chi cua “o bo nho song”: bien hoac phan tu cua cau truc du lieu, khong phai ket qua tinh toan tam thoi.
Loi 4: in con tro nhu mot so binh thuong roi toi ngac nhien.
Neu ban dung fmt.Println(p), ban se thay cai gi do nhu 0xc0000120a8. Nguoi moi doi khi nghi: “Oa, day la mot so rat lon, vay chac no la gia tri ben trong!” Khong, do la dia chi. Voi dia chi, hay dung "%p", con voi du lieu thi hay in *p (sau khi kiem tra nil neu can).
Loi 5: co gang ‘sua tat ca’ bang con tro chi vi chung xuat hien.
Con tro la mot cong cu, nhung khong phai gia vi “them vao cho co mui”. Doi khi viec tra ve gia tri tu ham la don gian hon nhieu so voi viec truyen *int. Doi khi slice va map da mang lai hanh vi cap nhat du lieu can thiet ma khong can den con tro. Dieu quan trong la khong bien code thanh “mot khu rung cua * va &”, noi ma sau nay khong con hieu ai dang sua cai gi.
GO TO FULL VERSION