Code Tu Tam

Con trỏ trong C++

Rate this post

Trong bài viết này sẽ giới thiệu cơ bản cho các bạn về con trỏ trong C/C++. Đây là 1 trong những đặc trưng của ngôn ngữ lập trình này mà ở các ngôn ngữ khác như Java, C#… bạn sẽ không thấy.

Mình cũng chưa có nhiều kinh nghiệm về C++, tuy vậy kiến thức dưới đây là do tìm hiểu và tổng hợp lại trong quá trình nghiên cứu cho công việc. Nếu có gì chưa chính xác mọi người hỗ trợ giúp mình nhé!

Biến trong C++ (variable)

Biên là 1 ô nhớ đơn lẻ, hoặc một vùng nhớ được hệ điều hành cấp phát nhằm lưu trữ giá trị trong vùng nhớ đó. Thông thường khi thao tác chúng ta cũng không quan tâm đến vùng nhớ của biến. Các thao tác liên quan sẽ thông qua tên định danh của biến đó – hay còn gọi là tên biến.

Khi thao tác với các biến thông thường, chúng ta không cần quan tâm đến địa chỉ vùng nhớ của biến. Khi cần truy xuất giá trị của biến, chúng ta chỉ cần gọi định danh (hay thường gọi là tên biến).

int a = 1000;
cout << "Gia Tri Cua a: " << a << endl; // Giá trị của a

Tham chiếu trong C++

Tham chiếu dùng để tạo ra biến mới nhưng có cùng địa chỉ với biến được tham chiếu đến.

Bất kì sự thay đổi nào ở vùng địa chỉ nhớ này đều tác động tới các biến tham chiếu tới vùng nhớ đó

Việc sử dụng tham chiếu này bạn có thể thấy trong lập trình PHP. Tuy vậy trong các Framework mới bạn sẽ ít thấy điều này hơn!

int v1 = 999;
int &v2 = v1;
cout << "v1: " << v1 << endl; // Trả về 999
cout << "&v1: " << &v1 << endl; // Trả về vùng nhớ của biến v1
cout << "v2: " << v2 << endl; // Trả về 999
cout << "&v2: " << &v2 << endl;// Trả về vùng nhớ của v2, đây cũng là vùng nhớ biến v1

Dưới đây là kết quả:

Con trỏ trong C++

Và dưới đây là ví dụ về việc thay đổi giá trị của biến

int v1 = 999; 
int &v2 = v1;
cout << "v1 = " << v1 << " - v2 ="<< v2 << endl;
v1 = 888;
cout << "v1 = " << v1 << " - v2 =" << v2 << endl;
v2 = 777;
cout << "v1 = " << v1 << " - v2 =" << v2 << endl;

Kết quả là

Dereference operator

Toán tử Dereference operator (trỏ đến) hay còn gọi là indirection operator (toán tử điều hành gián tiếp) sử dụng trong C++ với ký tự ” * “. Toán tử này sử dụng với mục đích lấy giá trị của 1 địa chỉ  nhớ.

Khác với việc tham chiếu, Dereference operator không tạo ra biến mới mà thao tác trực tiếp với vùng nhớ đã xác định đó.

Ví dụ:

int v1 = 999; 
cout << "v1: " << v1 << endl; // Trả về 999 
cout << "&v1: " << &v1 << endl; // Trả về vùng nhớ của biến v1 
cout << "*&v1 " << *&v1 << " = *(&v1) " << *(&v1) << endl; // Trả về giá trị lưu tại 1 địa chỉ nhớ
*&v1 = 888;
cout << "v1: " << v1 << endl;
cout << "&v1: " << &v1 << endl; // Vùng nhớ không thay đổi, chỉ có giá trị thay đổi

Kết quả tương ứng như sau

v1: 999
&v1: 00F3FC60
*&v1 999 = *(&v1) 999
v1: 888
&v1: 00F3FC60
Press any key to continue . . .

Như ta thấy

Có thể gán trực tiệp giá trị tương ứng với vùng nhớ đã xác định dưỡi dạng *&v1 = 888; Vùng nhớ sẽ không bị thay đổi, chỉ có giá trị là thay đổi.

Con trỏ (Pointer)

Một con trỏ ( apointer) là một biến được dùng để lưu trữ địa chỉ của biến khác.

Khác với tham chiếu, con trỏ là một biến có địa chỉ độc lập so với vùng nhớ mà nó trỏ đến, nhưng giá trị của vùng nhớ của con trỏ chính là địa chỉ của biến mà nó trỏ tới.

int v1 = 999; // Khai báo biến v1 có giá trị 999;
int *v2 = &v1; // Biến v2 sẽ có giá trị là địa chỉ của biến v1
cout << "v1: " << v1 << " - &v1: " << &v1 << endl;
cout << "v2: " << v2 << " - &v2: " << &v2 << endl;

Khi đó kết quả là

v1: 999 - &v1: 00B9FE88
v2: 00B9FE88 - &v2: 00B9FE7C

Như ta thấy

Giá trị của biến v2 là địa chỉ vùng nhớ của v1 và bằng: 00B9FE88

Còn địa chỉ vùng nhớ của v2 là 00B9FE7C

Kiểu dữ liệu của v2 khi này là int * thay vì là int

Chúng ta có thể dùng lệnh dưới để kiểm tra

cout << "Kieu du lieu cua v2: " << typeid(v2).name() << endl;

Kết quả là

Kieu du lieu cua v2: int *

Để gán giá trị chúng ta dùng toán tử Dereference Operator:

int v1 = 999; // Khai báo biến v1 có giá trị 999;
int *v2 = &v1; // Biến v2 sẽ có giá trị là địa chỉ của biến v1
cout << "v1: " << v1 << " - v2: " << v2 << endl;
*v2 = 888;
cout << "v1: " << v1 << " - v2: " << v2 << endl;

Kết quả là

v1: 999 - v2: 010FFDB4
v1: 888 - v2: 010FFDB4

Ví dụ tổng quát lại

int a = 1000; // Giá giá trị 1000 cho a
cout << "Gia Tri Cua a: " << a << endl; // Giá trị của a = 1000
int* p_a = &a;  // lấy địa chỉ của a gán vào biến p_a
cout << "Dia chi cua bien a " << &a << " = p_a = "<<p_a << endl;
int** p_p_a = &p_a; // Lấy địa chỉ của p_a gán vào p_p_a
cout << "Dia chi cua &p_a " << &p_a << " = p_p_a ="<<p_p_a << endl;
cout << "Gia tri cua *p_p_a " << *p_p_a << " = p_a "<< p_a << endl;
cout << "Gia tri a =" << a << " =  gia tri *p_a = "<<*p_a << " = gia tri cua gia tri p_p_a = " << **p_p_a << " = "<< *(*p_p_a) << endl;

Kết quả là

Gia Tri Cua a: 1000
Dia chi cua bien a 010FFEB8 = p_a = 010FFEB8
Dia chi cua &p_a 010FFEAC = p_p_a =010FFEAC
Gia tri cua *p_p_a 010FFEB8 = p_a 010FFEB8
Gia tri a =1000 =  gia tri *p_a = 1000 = gia tri cua gia tri p_p_a = 1000 = 1000

Khi đó kiểu dữ liệu của p_p_a là

cout << " p_p_a co kieu gia tri: "<<typeid(p_p_a).name() << endl;

Kiểu dữ liệu của p_p_a là int * *

p_p_a co kieu gia tri: int * *

Từ ví dụ trên ta cũng có thể để gán giá trị lại cho biến a ban đầu thong qua p_p_a sẽ như sau

int a = 1000; // Giá giá trị 1000 cho a
int* p_a = &a;  // lấy địa chỉ của a gán vào biến p_a
int** p_p_a = &p_a; // Lấy địa chỉ của p_a gán vào p_p_a
cout << "a = " << a <<endl;
*p_a = 999;
cout << "a = " << a << endl;
**p_p_a = 888;
cout << "a = " << a << endl;

Kết quả như sau

a = 1000
a = 999
a = 888

Từ những ví dụ trên đây ta có 1 kiểu viết khá hài hước và cũng khó hiểu trong C++

int a = 1000; 
cout << *&*&*&*&*&*&*&*&a << endl;

Theo bạn thì kết quả được in ra là bao nhiêu???

Nếu bạn nói 1000, thì chính xác là như vậy rồi đấy ạ!

Con trỏ và mảng

Phần này mình sẽ nói về việc con trỏ với kiểu dữ liệu mảng, vì có 1 chút sự khác biệt so với các kiến thức bên trên. Bạn có thể đọc nhiều hơn về mảng tại bài viết Cấu trúc dữ liệu mảng.

Xem ví dụ dưới đây

int arr[5] = { 1,3,4,5,6 };
for (int i = 0; i < 5; i++)
{
	cout << "arr[" << i << "] = " << arr[i] << " - dia chi "<< &arr[i] << endl;
}
cout << arr << endl;

Kết quả như dưới đây

arr[0] = 1 - dia chi 00D6F848
arr[1] = 3 - dia chi 00D6F84C
arr[2] = 4 - dia chi 00D6F850
arr[3] = 5 - dia chi 00D6F854
arr[4] = 6 - dia chi 00D6F858
00D6F848

Các bạn để ý sẽ thấy

+ Mảng là sự liên tục các vùng nhớ: 00D6F848 + 4 = 00D6F84C . 4 là kích thươc vùng nhớ của kiểu int

+ Khi cout arr thì kết quả trả về là 1 địa chỉ vùng nhớ, và địa chỉ này là địa chỉ của phần tử đầu tiên của mảng

Kết luận

Trên đây là toàn bộ các thông tin mà mình muốn gửi tới các bạn trong bài viết về con trỏ trong C++. Hi vọng với bài viết này bạn sẽ tự tin hơn khi học và tìm hiểu về C++. Nếu có góp ý hoặc thắc mắc gì đừng ngại ngần mà đặt câu hỏi và gửi thông tin cho mình nhé!

Exit mobile version