Cơ bản về con trỏ trong Go
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 19 trên 107.
Con trỏ là một biến lưu địa chỉ bộ nhớ của một biến khác. Thay vì lưu trực tiếp một giá trị, nó lưu vị trí của giá trị đó trong bộ nhớ. Con trỏ là nền tảng của Go và rất cần thiết để thao tác dữ liệu hiệu quả.
Để tạo con trỏ, hãy dùng toán tử & để lấy địa chỉ của biến, và toán tử * để khai báo kiểu con trỏ hoặc truy cập giá trị tại địa chỉ đó.
x := 42
p := &x // p lưu địa chỉ bộ nhớ của x
fmt.Println(p) // 0xc000018030 (địa chỉ bộ nhớ)
fmt.Println(*p) // 42 (giá trị tại địa chỉ đó)Toán tử & có nghĩa là “địa chỉ của” và trả về một con trỏ. Toán tử *, khi dùng với con trỏ, có nghĩa là “giá trị tại địa chỉ” và được gọi là giải tham chiếu. Bạn cũng có thể sửa đổi giá trị ban đầu thông qua con trỏ.
x := 10
p := &x
*p = 25 // thay đổi giá trị qua con trỏ
fmt.Println(x) // 25 (biến ban đầu đã thay đổi)Kiểu con trỏ được viết là *T, trong đó T là kiểu dữ liệu mà con trỏ trỏ đến. Con trỏ trỏ đến một int có kiểu *int.
var p *int // khai báo một con trỏ (pointer) trỏ đến kiểu int
x := 100
p = &x // p hiện trỏ đến xGiá trị không của con trỏ là nil, nghĩa là nó không trỏ đến đâu cả. Cố gắng giải tham chiếu một con trỏ nil sẽ gây ra lỗi panic khi chạy chương trình, vì vậy hãy luôn khởi tạo con trỏ trước khi sử dụng.
Thử thách
DễHãy tạo một hàm đơn giản để hoán đổi giá trị, qua đó minh họa sức mạnh của con trỏ trong việc sửa đổi biến thông qua địa chỉ bộ nhớ.
Bạn sẽ sắp xếp code trong hai tệp:
swap.go: Tạo một hàm có tênSwapValuesnhận hai con trỏ*intlàm tham số. Hàm này cần hoán đổi các giá trị mà những con trỏ này trỏ đến. Sau khi gọi hàm, mỗi biến sẽ giữ giá trị ban đầu của biến kia. Hàm không trả về gì, nó sửa đổi trực tiếp các giá trị thông qua con trỏ.main.go: Đọc hai số nguyên từ đầu vào, lưu chúng vào các biến, rồi dùng hàmSwapValuesđể hoán đổi giá trị của chúng. In cả hai giá trị trước và sau khi hoán đổi để thể hiện sự thay đổi.
Đầu vào sẽ gồm:
- Dòng 1: Số nguyên thứ nhất
- Dòng 2: Số nguyên thứ hai
Định dạng đầu ra:
Before: a=[first] b=[second]
After: a=[first] b=[second]Ví dụ, với 10 và 25, đầu ra của bạn sẽ là:
Before: a=10 b=25
After: a=25 b=10Nhớ dùng toán tử & khi truyền các biến vào hàm, và toán tử * bên trong hàm để truy cập và sửa đổi các giá trị thực tế.
Tự làm thử
package main
import "fmt"
func main() {
// Đọc hai số nguyên từ đầu vào
var a, b int
fmt.Scanln(&a)
fmt.Scanln(&b)
// In ra các giá trị trước khi hoán đổi
fmt.Printf("Before: a=%d b=%d\n", a, b)
// TODO: Gọi hàm (function) SwapValues với các con trỏ trỏ đến a và b
// Nhớ dùng toán tử & để truyền địa chỉ
// In ra các giá trị sau khi hoán đổi
fmt.Printf("After: a=%d b=%d\n", a, b)
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online