blog.dopana

Back

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ư 0x7ffee2b3d5ac
cpp

Con 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 a
cpp

Giả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';    // 100
cpp

Con 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';
}
cpp

Giả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';   // 100
cpp

Tham 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
}
cpp

Dù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';
}
cpp

Con trỏ so với tham chiếu#

Con trỏTham chiếu
Có thể nullCó (nullptr)Không
Đổi đích đượcKhông
Phải giải tham chiếuCó (*)Ngầm định
Gán lạip = &otherKhô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.

Tài liệu tham khảo#