D
DevStart

Pointer trong C++: hiểu con trỏ từ đầu

22 phútTrung bình

Pointer trong C++ là gì?

Pointer trong C++ là biến dùng để lưu địa chỉ bộ nhớ của biến khác. Nói đơn giản, thay vì cầm trực tiếp giá trị 10, pointer giữ thông tin về nơi giá trị đó đang nằm trong bộ nhớ. Khi hiểu pointer trong C++ là gì, bạn sẽ dễ học hơn các phần như reference, mảng động, cấp phát bộ nhớ, và truyền dữ liệu vào hàm theo cách mạnh hơn.

Với người mới, phần khó nhất không phải cú pháp mà là cách nghĩ. Bình thường bạn quen với biến và giá trị. Với pointer, bạn cần thêm một lớp nữa là địa chỉ. Hãy tách rõ ba thứ sau:

  • Biến gốc, ví dụ x

  • Giá trị của biến, ví dụ 10

  • Địa chỉ nơi biến được lưu trong bộ nhớ


Pointer sẽ lưu phần thứ ba. Khi nhìn như vậy, con trỏ không còn quá đáng sợ nữa.

Cú pháp cơ bản của pointer trong C++

cpp
#include <iostream>
using namespace std;

int main() {
int x = 10;
int* p = &x;

cout << x << endl;
cout << &x << endl;
cout << p << endl;
return 0;
}

Trong ví dụ trên:

  • &x là địa chỉ của biến x

  • int* p nghĩa là p là con trỏ tới kiểu int

  • p đang lưu địa chỉ của x


Bạn có thể hình dung như sau:
  • x là chiếc hộp chứa số 10

  • &x là địa chỉ căn phòng nơi chiếc hộp đó nằm

  • p là mảnh giấy ghi lại địa chỉ căn phòng


Điều quan trọng là kiểu dữ liệu của pointer phải khớp với kiểu dữ liệu của biến mà nó trỏ tới. Nếu xint thì nên dùng int*. Nếu biến là double thì dùng double*. Việc này giúp C++ biết phải đọc bao nhiêu byte dữ liệu ở địa chỉ đó.

Bạn cũng nên biết rằng khi cout << p, chương trình thường in ra một địa chỉ dạng số hoặc mã hexa. Giá trị này có thể khác nhau mỗi lần chạy và đó là chuyện bình thường.

Cách dùng toán tử * và & trong C++

Toán tử & lấy địa chỉ, còn * lấy giá trị tại địa chỉ đó.

cpp
#include <iostream>
using namespace std;

int main() {
int x = 25;
int* p = &x;

cout << *p << endl;

*p = 50;
cout << x << endl;
return 0;
}

Khi bạn thay đổi *p, giá trị thật của x cũng thay đổi vì p đang trỏ trực tiếp vào x.

Đây là điểm mấu chốt nhất khi học cách dùng pointer trong C++. p*p là hai thứ hoàn toàn khác nhau:

  • p là địa chỉ

  • *p là giá trị nằm tại địa chỉ đó


Hãy xem thêm ví dụ này:

cpp
#include <iostream>
using namespace std;

int main() {
int diem = 8;
int* p = &diem;

cout << "Gia tri cua diem: " << diem << endl;
cout << "Dia chi cua diem: " << &diem << endl;
cout << "Gia tri trong p: " << p << endl;
cout << "Gia tri tai dia chi p: " << *p << endl;

return 0;
}

Ở đây, &diemp sẽ giống nhau vì chúng cùng là địa chỉ của biến diem. Còn diem*p sẽ giống nhau vì chúng cùng là giá trị thực đang được lưu.

Người mới thường nhầm ký hiệu * ở hai chỗ:

  • Trong int* p, dấu * là một phần của khai báo con trỏ

  • Trong *p, dấu * là thao tác lấy giá trị tại địa chỉ đó


Ký hiệu giống nhau nhưng ý nghĩa phụ thuộc vào vị trí xuất hiện.

Khi nào pointer trong C++ hữu ích?

  • Khi cần làm việc trực tiếp với địa chỉ bộ nhớ
  • Khi truyền dữ liệu vào hàm để thay đổi biến gốc
  • Khi làm việc với mảng, cấp phát động, hoặc cấu trúc dữ liệu nâng cao
Ở giai đoạn này, bạn chưa cần đi quá sâu. Chỉ cần hiểu pointer là cầu nối giữa biến và địa chỉ của nó.

Ví dụ, nếu bạn muốn viết hàm đổi giá trị của một biến từ bên ngoài, pointer là một cách làm quen thuộc:

cpp
#include <iostream>
using namespace std;

void capNhatGiaTri(int* p) {
*p = 99;
}

int main() {
int x = 15;
capNhatGiaTri(&x);
cout << x << endl;
return 0;
}

Hàm capNhatGiaTri không nhận bản sao của x. Nó nhận địa chỉ của x, sau đó đi tới đúng vị trí đó để sửa dữ liệu. Đây là lý do pointer rất hữu ích khi cần thao tác trực tiếp với dữ liệu gốc.

Trong các bài học sau, bạn sẽ thấy con trỏ còn xuất hiện khi làm việc với mảng, chuỗi ký tự kiểu cũ, hoặc bộ nhớ động. Nhưng ở bước đầu, chỉ cần nắm vững quan hệ giữa biến, địa chỉ, và giá trị là đủ.

Những lỗi thường gặp với pointer trong C++

  • Dùng con trỏ chưa gán địa chỉ hợp lệ.
  • Nhầm giữa p*p.
  • Trỏ sai kiểu dữ liệu.
  • Quên rằng thay đổi *p là đang thay đổi biến gốc.
  • In hoặc dùng *p khi p đang là nullptr.
Ví dụ cần tránh:
cpp
int* p;

// Sai vi p chua tro den dia chi hop le
// cout << *p << endl;

Hãy luôn đảm bảo pointer đã được gán đúng trước khi dereference.

Một cách an toàn hơn khi mới học là khởi tạo con trỏ ngay lúc khai báo:

cpp
int x = 5;
int* p = &x;

Hoặc nếu chưa có gì để trỏ tới, có thể cho con trỏ bằng nullptr:

cpp
int* p = nullptr;

Khi đó bạn cần kiểm tra trước khi dùng:

cpp
if (p != nullptr) {
    cout << *p << endl;
}

Một lỗi khác của người mới là viết như sau:

cpp
int x = 10;
int* p = x;

Đoạn này sai vì p cần một địa chỉ, còn x chỉ là giá trị. Cách đúng là int* p = &x;.

Bài tập thực hành

Tạo một biến so = 100, sau đó tạo pointer trỏ tới biến này. Hãy:

  • In giá trị của so

  • In địa chỉ của so

  • In giá trị thông qua pointer

  • Dùng pointer để đổi giá trị của so thành 200


Gợi ý khởi đầu:

cpp
#include <iostream>
using namespace std;

int main() {
int so = 100;
int* p = &so;

// Viet tiep tai day

return 0;
}

Sau khi làm xong, hãy tự trả lời hai câu hỏi:

  • Vì sao p&so giống nhau?

  • Vì sao đổi *p thì so cũng đổi theo?


Câu hỏi thường gặp về pointer trong C++

p*p khác nhau thế nào?

p là địa chỉ mà con trỏ đang lưu. *p là giá trị nằm tại địa chỉ đó. Đây là khác biệt quan trọng nhất khi mới học pointer.

Vì sao pointer bị xem là khó?

Vì pointer liên quan trực tiếp đến bộ nhớ và địa chỉ, trong khi người mới thường quen nghĩ ở mức biến và giá trị. Khi bạn tách rõ “địa chỉ” và “giá trị”, pointer sẽ dễ hiểu hơn rất nhiều.

Có phải lúc nào cũng nên dùng pointer không?

Không. Ở nhiều tình huống cơ bản, biến thường hoặc reference sẽ dễ đọc hơn. Pointer chỉ nên dùng khi bạn thật sự cần làm việc với địa chỉ, cần truyền khả năng thay đổi dữ liệu gốc, hoặc phải xử lý cấu trúc dữ liệu yêu cầu con trỏ.

Dấu & trong pointer có giống & trong reference không?

Không hoàn toàn giống. Trong biểu thức như &x, dấu & có nghĩa là lấy địa chỉ của biến x. Còn trong khai báo int& y = x;, dấu & là cú pháp tạo reference. Ký hiệu giống nhau nhưng ngữ cảnh khác nhau.

Tóm tắt

Bạn đã hiểu pointer trong C++ là gì, cách khai báo con trỏ, cách dùng & để lấy địa chỉ và * để lấy giá trị tại địa chỉ đó. Bạn cũng đã thấy vì sao pointer có thể thay đổi trực tiếp biến gốc và những lỗi phổ biến như dereference con trỏ chưa hợp lệ hoặc nhầm giữa p với *p.

Nếu bạn nắm chắc bài này, bài kế tiếp sẽ nhẹ hơn nhiều, vì reference trong C++ cũng liên quan đến việc làm việc trực tiếp với dữ liệu gốc nhưng có cú pháp gọn và dễ đọc hơn.