Con trỏ trong C++

Các bạn thân mến! Ở các bài về hàm, mảng trong C++ mình đã giới thiệu ở các bài trước cũng có đề cập đến con trỏ. Trong bài học hôm nay chúng ta sẽ cùng tìm hiểu về con trỏ (pointer) trong C++ là gì nhé. 

1. Con trỏ trong C++

Trong C++ con trỏ (pointer) là một biến mà giá trị của nó là địa chỉ của một biến khác. Chúng ta cùng xem hình minh họa dưới đây:

Cú pháp

Con trỏ trong C++ cũng giống như biến hoặc hằng khác, chúng ta muốn sử dụng nó thì phải khai báo trước khi sử dụng. Cú pháp khai báo con trỏ trong C++ như sau:

Cú pháp
KieuDuLieu *TenBien;

Trong đó KieuDuLieu là kiểu dữ liệu hợp lệ trong C++ như int, double, float, char... còn TenBien là tên của biến con trỏ, các bạn có thể đặt tùy ý miễn sao không trùng với từ khóa của C++. 

Ví dụ một số khai báo con trỏ trong C++ như sau:

Ví dụ
int    *a;    
double *b;
float  *c; 
char   *d;

Sử dụng con trỏ

Trong C++ có hổ trợ các toán tử sau:

  • &: Dùng để lấy địa chỉ của một biến
  • *: Dùng để lấy giá trị của một địa chỉ

Các bạn mới bắt đầu học lập trình nghe có vẻ khó hiểu đúng không? Chúng ta cùng xem tiếp các ví dụ phía sau để hiểu rõ hơn về con trỏ trong C++ nhé.

Ví dụ

Ví dụ 1: Chúng ta cùng xem một ví dụ đơn giản về việc sử dụng con trỏ trong C++ như sau:

Ví dụ
#include <iostream>  
using namespace std;  
int main() {  
    int a = 10;    
    int *p;      
    p = &a; //con tro p tro den bien a    
    cout << "Dia chi cua bien a la: " << &a << endl;    
    cout << "Gia tri cua con tro p la: " << p << endl;
    cout << "Dia chi cua con tro p la: " << &p << endl;
    cout << "Gia tri cua bien a: " << a << endl; 
    cout << "Gia tri cua bien a (*p): " << *p << endl;    
   return 0;  
}

Và kết quả sau khi thực thi đoạn code trên như sau:

Mình xin giải thích kết quả trên một tý nhé. Chúng ta thấy chương trình chúng ta có 2 biến đó là a, và biến con trỏ p. Chúng ta trỏ con trỏ p đến biến a bằng cú pháp p = &a, như vậy giá trị của biến con trỏ p là địa chỉ của biến a, và p vẫn là một biến khác ở trong bộ nhớ nó cũng có địa chỉ riêng, để truy xuất giá trị biến a chúng ta có thể truy xuất thông qua con trỏ p đó là *p.

Như vậy đến ví dụ này các bạn chắc đã hiểu cách sử dụng của 2 toán tử  đó là & và * rồi nhé.

Ví dụ 2: Hoán đổi giá trị của 2 biến không sử dụng biến tạm thứ 3

Chúng ta thường giải quyết bài toán hoán đổi giá trị của 2 biến bằng cách sử dụng biến tạm thứ 3, trong ví dụ sau đây chúng ta sử dụng con trỏ sẽ không cần đến biến tạm thứ 3.

Ví dụ
#include <iostream>  
using namespace std;  
int main() {
    int a = 5;
    int b = 20;
    int *pa;
    int *pb;
    pa = &a; //con tro pa tro den bien a
    pb = &b; //con tro pb tro ben bien b
     
    cout << "Truoc khi hoan doi: *pa = " << *pa << ", *pb = " << *pb <<  endl;
    cout << "Truoc khi hoan doi:   a = " << a << ",   b = " << b <<  endl;
    
    *pa = *pa + *pb;
    *pb = *pa - *pb;
    *pa = *pa - *pb;

    cout << "Sau khi hoan doi: *pa = " << *pa << ", *pb = " << *pb << endl;
    cout << "Sau khi hoan doi:   a = " << a << ",   b = " << b << endl;
    return 0;  
}   

Và kết quả sau khi thực thi đoạn code trên:

 

2. Con trỏ NULL

Trong C++ con trỏ được gán giá trị bằng NULL, ta gọi nó là con trỏ NULL. Mục đích của việc sử dụng con trỏ null là khi chúng ta không biết chính xác địa chỉ mà con trỏ sẽ trỏ đến.

Con trỏ null có giá trị là 0 được định nghĩa hầu hết các thư viện chuẩn, bao gồm iostream. Chúng ta cùng xem ví dụ sau

Ví dụ
#include <iostream>  
using namespace std;
int main () {
   int  *p = NULL;
   cout << "Gia tri cua con tro p la: " << p;
   return 0;
}

Và kết quả sau khi thực thi đoạn code trên:

Hầu hết các hệ điều hành, các chương trình không được phép truy cập bộ nhớ tại địa chỉ 0 vì bộ nhớ đó được dành cho hệ điều hành. 

Tuy nhiên, bộ nhớ có địa chỉ là 0 có ý nghĩa đặc biệt vì nó báo hiệu rằng con trỏ không có ý định trỏ đến một vị trí bộ nhớ có thể truy cập. Theo quy ước, con trỏ có địa chỉ 0 tức là không trỏ đến gì cả.

Trong C++ cho phép chúng ta kiểm tra con trỏ là null hay không như sau:

Ví dụ
#include <iostream>  
using namespace std;
int main () {
   int  *p = NULL;
   if(p) {
       cout << "con tro khong null" << endl;
   }    
   if(!p)  {
       cout << "con tro null" << endl;
   } 
   return 0;
}

3. Con trỏ trỏ tới con trỏ

Thông thường giá trị của con trỏ là địa chỉ của một biến khác, còn con trỏ trỏ tới con trỏ trong C++ đó là giá trị của con trỏ là địa chỉ của một con trỏ khác.

Cú pháp

Cú pháp khai báo của con trỏ trỏ tới con trỏ trong C++ như sau:

KieuDuLieu **TenConTro;

Trong đó dấu * có thể thêm nhiều hơn 2 tùy vào con trỏ của bạn. (Ví dụ khai báo **pb tức là con trỏ trỏ **pb tới con trỏ *pa, ***pc tức con trỏ ***pc trỏ tới con trỏ **pb, con trỏ **pb trỏ tới con trỏ *pa)

Ví dụ

Chúng ta cùng xem một ví dụ đơn giản về con trỏ trỏ tới con trỏ trong C++ như sau:

Ví dụ
#include <iostream>
 
using namespace std;
 
int main () {
   int  a;
   int  *p1;
   int  **p2;
   int ***p3;
   a = 2;
   p1 = &a;
   p2 = &p1;
   p3 = &p2;
   cout << "Gia tri cua bien a: " << a << endl;
   cout << "Gia tri cua bien a tai con tro p1: " << *p1 << endl;
   cout << "Gia tri cua bien a tai con tro p2: " << **p2 << endl;
   cout << "Gia tri cua bien a tai con tro p3: " << ***p3 << endl;
   return 0;
}

Và kết quả sau khi thực thi đoạn code trên như sau:

4. Kết luận

Như vậy con trỏ trong C++ không quá khó đúng không nào các bạn. Chỉ cần chúng ta nghĩ đơn giản đó là bản chất của con trỏ cũng như các biến thông thường khác trong C++, chẳng qua giá trị của nó là địa chỉ của biến khác. Chúng ta có thể truy cập giá trị của biến mà con trỏ trỏ đến bằng cách sử dụng toán tử *.

Thật ra còn rất nhiều vấn đề liên quan đến con trỏ trong C++  nữa, tuy nhiên bài học hôm nay chúng ta chỉ đề cập các vấn đề đã nêu trên thôi nhé. Mình nghĩ như vậy là khá nhiều so với các bạn mới bắt đầu rồi. Mình kết thúc bài học này ở đây nhé. Cám ơn các bạn đã đọc bài viết.

Khóa học nên xem

Nguồn: freetuts.net