Pointer
Pointer là gì?
Pointer (con trỏ) lưu địa chỉ bộ nhớ của một biến thay vì lưu giá trị. Hãy tưởng tượng bộ nhớ là một dãy nhà — mỗi nhà có địa chỉ. Pointer là tờ giấy ghi địa chỉ đó.
x := 42
p := &x // & lấy địa chỉ của x → p là pointer
fmt.Println(x) // 42 — giá trị
fmt.Println(p) // 0xc000... — địa chỉ bộ nhớ
fmt.Println(*p) // 42 — giá trị TẠI địa chỉ đó (dereference)
*p = 100 // thay đổi giá trị thông qua pointer
fmt.Println(x) // 100 — x thay đổi theo!
Tại sao cần Pointer?
1. Thay đổi biến trong hàm khác:
// Không dùng pointer: hàm nhận bản sao, gốc không đổi
func tangX1(n int) { n++ }
// Dùng pointer: hàm nhận địa chỉ, thay đổi gốc
func tangX2(n *int) { *n++ }
x := 5
tangX1(x) // x vẫn = 5
tangX2(&x) // x = 6
2. Tránh copy dữ liệu lớn:
type BigStruct struct { data [1000]int }
// KHÔNG hiệu quả: copy 8000 bytes mỗi lần gọi
func processValue(s BigStruct) { ... }
// Hiệu quả: chỉ truyền 8 bytes (địa chỉ)
func processPointer(s *BigStruct) { ... }
3. Cho phép giá trị "không có" (nil):
// int không thể nil
// *int có thể nil — biểu diễn "không có giá trị"
type Config struct {
Port int
Timeout *int // nil = dùng timeout mặc định
}
new() — Cấp phát và trả về pointer
p := new(int) // cấp phát int mới, trị = 0, trả về *int
*p = 42
fmt.Println(*p) // 42
// Tương đương với:
val := 0
p := &val
Nil Pointer — Cạm bẫy phổ biến
var p *int // p == nil
// PANIC nếu dereference nil pointer:
fmt.Println(*p) // runtime error: invalid memory address
// Luôn kiểm tra nil trước khi dereference
if p != nil {
fmt.Println(*p)
}
Go KHÔNG có pointer arithmetic
// Trong C: *(ptr + 1) hợp lệ — rất nguy hiểm
// Trong Go: KHÔNG được làm vậy
p := &someArray[0]
p++ // lỗi compile: cannot take address of non-addressable value
// Dùng index để truy cập phần tử mảng
for i := range someArray {
process(&someArray[i])
}
Lưu ý: Trả về pointer đến biến local trong Go là an toàn — Go sẽ tự động cấp phát biến đó trên heap thay vì stack (khác với C!)
example.go
package main
import "fmt"
// Stack dùng pointer để thay đổi slice gốc
type Stack struct {
items []int
}
func (s *Stack) Push(item int) {
s.items = append(s.items, item)
}
func (s *Stack) Pop() (int, bool) {
if len(s.items) == 0 {
return 0, false
}
n := len(s.items) - 1
item := s.items[n]
s.items = s.items[:n]
return item, true
}
func (s *Stack) Peek() (int, bool) {
if len(s.items) == 0 {
return 0, false
}
return s.items[len(s.items)-1], true
}
func (s Stack) Size() int { return len(s.items) }
// Linked list — ví dụ pointer điển hình
type Node struct {
Value int
Next *Node
}
func newList(vals ...int) *Node {
if len(vals) == 0 {
return nil
}
head := &Node{Value: vals[0]}
cur := head
for _, v := range vals[1:] {
cur.Next = &Node{Value: v}
cur = cur.Next
}
return head
}
func printList(head *Node) {
for cur := head; cur != nil; cur = cur.Next {
if cur.Next != nil {
fmt.Printf("%d -> ", cur.Value)
} else {
fmt.Println(cur.Value)
}
}
}
func main() {
var s Stack
for i := 1; i <= 5; i++ {
s.Push(i * 10)
}
fmt.Println("Stack size:", s.Size())
for {
if v, ok := s.Pop(); ok {
fmt.Println("Pop:", v)
} else {
break
}
}
fmt.Println()
list := newList(1, 2, 3, 4, 5)
printList(list)
}