[Basic] Một số vấn đề về con trỏ trong C++

Con trỏ null (null pointer)

Null pointer là con trỏ không trỏ vào bất cứ địa chỉ nào.

Trong C++ ta có thể gán giá trị null cho con trỏ theo các cách sau

int* ptr = 0;

int* ptr1 = NULL;

int* ptr2 = nullptr; // C++ 11

Sử dụng NULL hay nullptr?

  • NULL là macro được định nghĩa có giá trị 0
#define NULL 0
  • nullptr là tử khóa mới trong C++ 11 thể hiện giá trị null của con trỏ.

Ta xét ví dụ sau :

#include <iostream>

using namespace std;

void Func(int val)
{ 

   cout << "This is function with parameter is int" << endl;

}

void Func(int* ptr)
{ 

   cout << "This is function with parameter is pointer" << endl;

}

int main()
{ 

   Func(NULL); 

   Func(nullptr); 

   return 0;

}

Kết quả:

This is function with parameter is int
This is function with parameter is pointer

Ta có 2 hàm Func với 2 tham số khác nhau :

  • Lần gọi hàm thứ nhất ta truyền vào đối số là NULL. Thực chất khi truyền NULL lúc này Func có dạng Func(0); Vì 0 là int literal nên hàm Func có tham số kiểu int sẽ được gọi.
  • Lần gọi hàm thứ 2 ta truyền vào đối số là nullptr. Chương trình sẽ gọi hàm Func có tham số kiểu con trỏ.

=> Nên sử dụng nullptr cho con trỏ null để tránh nhập nhằng giữa con trỏ và kiểu int.

Các phép toán trên con trỏ

Trong C++ cho phép ta thực hiện phép toán cộng (+) và phép toán trừ (-) của con trỏ với 1 số nguyên.Kết quả trả vể là đia chỉ cách vị trí con trỏ 1 khoảng (so_nguyen * sizeof(kieu_du_lieu_con_tro)) bytes.

Ví dụ:

#include <iostream>

using namespace std;

int main()
{ 

   int val = 10; 

   int* ptr = &val; 

   cout << ptr << endl; 

   cout << (ptr + 1) << endl; 

   cout << (ptr + 2) << endl; 

   cout << (ptr + 3) << endl; 

   return 0;

}

Kết quả:

00C4FA58
00C4FA5C
00C4FA60
00C4FA64

Ở ví dụ trên con trỏ ptr có kiểu int, nên khoảng cách giữa ptr và ptr + 1 là 4 bytes

Chú ý:

  • Trong C++ KHÔNG hỗ trợ phép cộng hai con trỏ
  • Trong C++ hỗ trợ phép trừ 2 con trỏ cùng kiểu dữ liệu. Kết quả trả về là số nguyên thể hiện giữa 2 con trỏ cách bao nhiêu phần tử có cùng kiểu dữ liệu.

Ví dụ:

#include <iostream>

using namespace std;

int main()
{ 

   int val1 = 10; 

   int val2 = 20; 

   int* ptr1 = &val1; 

   int* ptr2 = &val2; 

   cout << "ptr1 : " << ptr1 << endl; 

   cout << "ptr2 : " << ptr2 << endl; 

   cout << "ptr1 - ptr2: " << ptr1 - ptr2 << endl; 

   return 0;

}

Kết quả:

ptr1 : 0053FD44
ptr2 : 0053FD38
ptr1 - ptr2: 3

Ví dụ trên ptr1 và ptr2 cách nhau 3 phần tử kiểu int (12 bytes).

Con trỏ hằng và hằng con trỏ

Con trỏ hằng

Ta không thể sử dụng con trỏ hằng để thay đổi giá trị tại địa chỉ mà nó trỏ vào.

Cú pháp 

const kieu_du_lieu* ten_con_tro

Hoặc

kieu_du_lieu const *ten_con_tro

Chú ý:

Khi khai báo con trỏ, từ khóa const trước dấu ‘*’ là con trỏ hằng.

Ví dụ

#include <iostream>

using namespace std;

int main()
{ 

   int x = 10; 

   const int* ptr = &x; 

   cout << *ptr << endl; 

   return 0;

}

Kết quả

10

ptr là con trỏ hằng trỏ vào địa chỉ biến x, ta không thể dùng con trỏ ptr thay đổi giá trị tại biến x.

*ptr = 20;   //Error

Vì x không phải là hằng số nên ta vẫn có thể thay đổi giá trị của x, khi đó giá trị của *ptr cũng được thay đổi.

Ví dụ

#include <iostream>

using namespace std;

int main()
{ 

   int x = 10; 

   const int* ptr = &x; 

   cout << *ptr << endl; 

   x = 20; 

   cout << *ptr << endl; 

   return 0;

}

Kết quả :

10
20

Có thể giải thích dễ hiểu hơn giống như ta đi thuê phòng trọ. Biến x giống như chủ nhà trọ. Biến con trỏ ptr giống như anh sinh viên đi thuê phòng trọ. Anh sinh viên (ptr) không thể sửa sang hay đập phá phòng trọ. Chủ nhà trọ (x) thì có thể sửa sang, xây dựng lại phòng trọ,khi đó anh sinh viên (ptr) sẽ được hưởng ké. Chủ trọ (x) sửa phòng trọ thế nào thì anh sinh viên (ptr) được hưởng thế đó.

Hằng con trỏ

Ta không thể thay đổi được địa chỉ mà hằng con trỏ trỏ vào.

Cú pháp :

kieu_du_lieu* const ten_con_tro

Chú ý:

  • Khi khai báo con trỏ, từ khóa const sau dấu ‘*’ là hằng con trỏ.
  • Phải khởi tạo giá trị cho hằng con trỏ lúc khai báo.

Ví dụ

#include <iostream>

using namespace std;

int main()
{ 

   int x = 10; 

   int* const ptr = &x; 

   cout << *ptr << endl; 

   *ptr = 20;   // Có thể thay đổi giá trị tại địa chỉ hằng con trỏ trỏ vào 

   cout << x << endl; 

   return 0;

}

Kết quả

10
20
  • Không thể thay đổi địa chỉ mà hằng con trỏ trỏ vào.

Ví dụ

int x = 10;

int* const ptr = &x;

int y = 20;

ptr = &y;  //Error

Khai báo:

 const kieu_du_lieu* const ten_con_tro

=> Ta không thể thay đổi dữ liệu tại địa chỉ con trỏ trỏ vào và cũng không thể thay đổi đia chỉ mà con trỏ trỏ vào

Ví dụ:

int x = 10;

const int* const ptr = &x;

*ptr = 20;  // Error

int y = 20;

ptr = &y;  //Error

Con trỏ void (void pointer)

Con trỏ có void có thể hiểu là kiểu con trỏ tổng quát, là 1 loại con trỏ đặc biệt có thể trỏ vào đối tượng của tất cả các kiểu dữ liệu.

Ví dụ

int iVal = 10;

float fVal = 10.0f; 

void* iPtr = &iVal; //OK

void* fPtr = &fVal; //OK

Chú ý:

Ta không thể dereference (*) trực tiếp con trỏ void, mà phải ép sang kiểu dữ liệu cụ thể.

Ví dụ:

#include <iostream>

using namespace std;

int main()
{ 

   int val = 10; 

   void* ptr = &val; 

   cout << *static_cast<int*>(ptr); 

   return 0;

}

Kết quả:

10

Was this article helpful?

Leave A Comment?