Con trỏ (Pointer) cơ bản
Con trỏ là một trong những khái niệm đặc trưng và quan trọng nhất của C++. Hiểu con trỏ là chìa khóa để hiểu cách bộ nhớ máy tính thực sự hoạt động.
Bộ nhớ và địa chỉ
Phần tiêu đề “Bộ nhớ và địa chỉ”Mỗi biến trong chương trình được lưu ở một vị trí cụ thể trong bộ nhớ RAM, gọi là địa chỉ (address). Bình thường, khi làm việc với biến, bạn chỉ quan tâm đến giá trị của nó mà không cần biết nó nằm ở đâu trong bộ nhớ:
int age = 25;// "age" là tên bạn đặt cho một ô nhớ đang chứa giá trị 25// Ô nhớ đó có một địa chỉ cụ thể, ví dụ: 0x7ffee3a1c05cCon trỏ là gì?
Phần tiêu đề “Con trỏ là gì?”Con trỏ (pointer) là một biến đặc biệt, không lưu giá trị dữ liệu thông thường mà lưu địa chỉ của một biến khác. Có thể hình dung: nếu biến là một “ngôi nhà” chứa giá trị, thì con trỏ là một “tờ giấy ghi địa chỉ” của ngôi nhà đó.
#include <iostream>
int main() { int age = 25; int* ptr = &age; // ptr là con trỏ, lưu ĐỊA CHỈ của biến age
std::cout << age << std::endl; // 25 (giá trị của age) std::cout << &age << std::endl; // Địa chỉ của age (ví dụ: 0x7ffee3a1c05c) std::cout << ptr << std::endl; // Cũng chính là địa chỉ đó - vì ptr đang lưu nó
return 0;}Cú pháp khai báo con trỏ
Phần tiêu đề “Cú pháp khai báo con trỏ”int* ptr; // Con trỏ trỏ đến một biến kiểu intdouble* dptr; // Con trỏ trỏ đến một biến kiểu doublechar* cptr; // Con trỏ trỏ đến một biến kiểu charDấu * khi đứng trong khai báo kiểu dữ liệu có nghĩa là “đây là một con trỏ”. Kiểu dữ liệu đứng trước * cho biết con trỏ này trỏ đến biến kiểu gì - điều này quan trọng vì nó quyết định số byte cần đọc/ghi tại địa chỉ đó.
Hai toán tử quan trọng: & và *
Phần tiêu đề “Hai toán tử quan trọng: & và *”&bien: lấy địa chỉ của một biến (toán tử address-of)*con_tro: truy cập giá trị tại địa chỉ mà con trỏ đang trỏ đến (toán tử dereference)
#include <iostream>
int main() { int age = 25; int* ptr = &age;
std::cout << *ptr << std::endl; // 25 - "đi tới" địa chỉ ptr đang giữ, lấy giá trị ở đó
*ptr = 30; // Thay đổi giá trị TẠI địa chỉ mà ptr đang trỏ đến std::cout << age << std::endl; // 30 - age đã thay đổi theo!
return 0;}Đây là điểm mấu chốt: ptr và age cùng “chỉ về” một ô nhớ, nên thay đổi thông qua *ptr cũng chính là thay đổi age.
Tại sao cần con trỏ?
Phần tiêu đề “Tại sao cần con trỏ?”Con trỏ nghe có vẻ trừu tượng lúc đầu, nhưng chúng giải quyết những vấn đề thực tế quan trọng:
- Cho phép một hàm thay đổi trực tiếp biến bên ngoài (thay vì chỉ nhận bản sao)
- Cho phép cấp phát bộ nhớ linh hoạt lúc chạy chương trình (không cần biết trước kích thước dữ liệu)
- Là nền tảng để xây dựng các cấu trúc dữ liệu phức tạp (danh sách liên kết, cây, đồ thị…)
- Giúp làm việc hiệu quả với mảng lớn mà không cần sao chép dữ liệu
Các bài tiếp theo sẽ đi sâu vào từng khía cạnh này: con trỏ với mảng, cấp phát bộ nhớ động, con trỏ null, và cách con trỏ liên hệ với tham chiếu.
Tổng kết
Phần tiêu đề “Tổng kết”- Con trỏ là một biến lưu địa chỉ bộ nhớ của một biến khác, không lưu giá trị trực tiếp
- Khai báo:
KieuDuLieu* ten_con_tro; &bienlấy địa chỉ của biến;*con_trotruy cập giá trị tại địa chỉ đó- Thay đổi giá trị thông qua
*con_trosẽ ảnh hưởng trực tiếp đến biến gốc mà nó đang trỏ đến