Con trỏ và tham chiếu trong C++
Hiểu toán tử lấy địa chỉ, con trỏ, giải tham chiếu, tham chiếu và cách truyền chúng vào hàm.
Bài thứ năm trong loạt C++ cơ bản. Biến sống trong bộ nhớ tại một địa chỉ. Con trỏ và tham chiếu là hai cách truy cập biến một cách gián tiếp — cả hai đều thiết yếu trong C++.
Địa chỉ của biến#
Mọi biến nằm tại một địa chỉ bộ nhớ. Lấy địa chỉ bằng &:
int a = 42;
std::cout << &a << '\n'; // kiểu như 0x7ffee2b3d5accppCon trỏ#
Con trỏ lưu một địa chỉ. Nó có kiểu cho biết nó trỏ tới gì:
int a = 42;
int* p = &a; // p trỏ tới acppGiải tham chiếu bằng * để đọc hoặc ghi qua con trỏ:
std::cout << *p << '\n'; // 42
*p = 100; // thay đổi a
std::cout << a << '\n'; // 100cppCon trỏ null#
Con trỏ có thể không giữ địa chỉ nào. Dùng từ khóa nullptr và luôn kiểm tra trước khi giải tham chiếu:
int* p = nullptr;
if (p != nullptr) {
std::cout << *p << '\n';
}cppGiải tham chiếu nullptr là crash — lỗi con trỏ null.
Tham chiếu#
Tham chiếu là bí danh của một biến đã tồn tại. Nó gắn một lần và không thể gắn lại:
int a = 42;
int& r = a; // r là tên khác của a
r = 100; // thay đổi a
std::cout << a << '\n'; // 100cppTham chiếu không thể null và không thể trỏ tới biến khác sau đó.
Truyền vào hàm#
Truyền tham chiếu để sửa biến của người gọi mà không cần sao chép:
void increment(int& x) {
++x;
}
int main() {
int a = 5;
increment(a);
std::cout << a << '\n'; // 6
}cppDùng const int& khi muốn tránh sao chép nhưng không sửa:
void describe(const std::string& s) {
std::cout << s.size() << '\n';
}cppCon trỏ so với tham chiếu#
| Con trỏ | Tham chiếu | |
|---|---|---|
| Có thể null | Có (nullptr) | Không |
| Đổi đích được | Có | Không |
| Phải giải tham chiếu | Có (*) | Ngầm định |
| Gán lại | p = &other | Không được |
Trong C++ hiện đại, ưu tiên tham chiếu cho tham số hàm. Dùng con trỏ khi “không có giá trị” là trạng thái hợp lệ hoặc khi cần gán lại.
Kết luận#
Con trỏ và tham chiếu đều truy cập biến gián tiếp. Ưu tiên tham chiếu cho an toàn, dùng con trỏ khi cần null hoặc gán lại. Bài tiếp theo trong loạt: lớp và đối tượng.