Con trỏ (Pointer) trong C /C++ (Part 1)
Một con trỏ là một biến mà trong đó giá trị của nó là địa chỉ của biến khác. Một vài tác vụ trong ngôn ngữ C /C++ được thực hiện dễ dàng hơn nhờ con trỏ, và những tác vụ khác trở nên linh hoạt hơn, như trong việc cấp phát bộ nhớ, không thể thực hiện mà không dùng con trỏ. Do đó rất cần thiết phải nắm vững con trỏ khi trở thành một lập trình viên C /C++ hoàn thiện.
Như bạn biết, mỗi biến trong một vùng nhớ nhất định và mỗi vùng nhớ này có địa chỉ có nó được định nghĩa để dễ dàng trong việc truy cập sử dụng toán tử (&) tương ứng với địa chỉ của nó trong bộ nhớ. Xem xét ví dụ dưới đây, sẽ in ra địa chỉ của biến được định nghĩa:
#include <iostream> using namespace std; int main () { int bien1; char bien2[10]; cout << "Dia chi cua bien1 la: "; cout << &bien1 << endl; cout << "Dia chi cua bien2 la: "; cout << &bien2 << endl; return 0; }
Chạy chương trình C /C++ trên sẽ cho kết quả như hình sau:
Con trỏ là gì?
Một con trỏ là một biến mà trong đó giá trị của nó là địa chỉ của biến khác. Ví dụ như địa chỉ của vùng nhớ. Giống như các biến và hằng số, bạn phải khai báo con trỏ trước khi bạn có thể sử dụng nó để lưu trữ bất kì địa chỉ của biến nào. Dạng tổng quát của việc khai báo con trỏ như sau:
kieu_du_lieu *ten_bien;
Ở đây, kieu_du_lieu là kiểu dữ liệu cơ bản con trỏ, nó là kiểu hợp lệ trong ngôn ngữ C và ten_bien là tên giá trị của con trỏ. Phần ký tự * sử dụng trong khai báo con trỏ giống như việc bạn sử dụng cho phép nhân. Mặc dù vậy, trong khai báo này, ký tự * được thiết kế để sử dụng các biến của con trỏ. Dưới đây là một số cách khai báo hợp lệ của con trỏ:
int *sv; // tro toi mot gia tri nguyen double *nv; // tro toi mot gia tri double float *luong; // tro toi mot gia tri float char *ten // tro toi mot ky tu
Kiểu dữ liệu thực sự của giá trị của tất cả các con trỏ, có thể là integer, float, character, hoặc kiểu khác, là giống như, một số long hexa biểu diễn một địa chỉ bộ nhớ. Điểm khác nhau duy nhất của các con trỏ của các kiểu dữ liệu khác nhau là kiểu dữ liệu của biến hoặc hằng số mà con trỏ chỉ tới.
Cách sử dụng con trỏ trong C /C++
Có một vài phép toán quan trọng, sẽ giúp chúng ta làm việc với con trỏ một cách thường xuyên: a) chúng ta định nghĩa biến con trỏ, b) gán địa chỉ của biến đến một con trở và c) cuối cùng truy cập các giá trị biến địa chỉ trong biến con trỏ. Điều này được thực hiện bởi toán tử * trả về giá trị các các biến chứa trong địa chỉ được xác định bởi toán tử này. Dưới đây là các sử dụng những phép toán trên:
#include <iostream> using namespace std; int main () { int bien1 = 15000; // khai bao bien. int *sv; // bien con tro sv sv = &bien1; // luu tru dia chi cua bien1 vao bien con tro sv cout << "Gia tri cua bien1 la: "; cout << bien1 << endl; // In dia chi duoc luu tru trong bien con tro sv cout << "Dia chi duoc luu tru trong bien con tro sv la: "; cout << sv << endl; // Truy cap gia tri co san tai dia chi cua bien con tro cout << "Gia tri cua *sv la: "; cout << *sv << endl; return 0; }
Chạy chương trình C /C++ trên sẽ cho kết quả như hình sau:
Con trỏ NULL trong C/C++
Nó luôn luôn là một bài thực hành tốt khi gán con trỏ NULL cho một biến con trỏ trong trường hợp bạn không biết chính xác địa chỉ để được gán. Điều này được thực hiện tại thời điểm khai báo biến. Một con trỏ mà được gán NULL được gọi là một con trỏ null.
Con trỏ NULL là một hằng với một giá trị là 0 được định nghĩa trong một vài thư viện chuẩn, gồm iostream. Bạn xét ví dụ sau:
#include <iostream> using namespace std; int main () { int *contro = NULL; cout << "Gia tri cua contro la " << ptr ; return 0; }
Khi code trên được biên dịch và thực thi, nó cho kết quả sau:
Gia tri cua contro la 0
Trên hầu hết Hệ điều hành, các chương trình không được cho phép để truy cập bộ nhớ tại địa chỉ 0, bởi vì, bộ nhớ đó được dự trữ bởi Hệ điều hành. Tuy nhiên, địa chỉ bộ nhớ 0 có ý nghĩa đặc biệt; nó báo hiệu rằng con trỏ không được trỏ tới một vị trí ô nhớ có thể truy cập. Nhưng theo qui ước, nếu một con trỏ chứa giá trị 0, nó được xem như là không trỏ tới bất cứ thứ gì.
Để kiểm tra một con trỏ null, bạn có thể sử dụng một lệnh if như sau:
if(contro) // true neu contro khong la NULL if(!contro) // true neu contro la NULL
Nếu tất cả con trỏ chưa được sử dụng được cung cấp giá trị null và bạn tránh sử dụng con trỏ null, bạn có thể tránh việc sử dụng sai của một con trỏ chưa được khởi tạo. Nhiều khi, các biến chưa được khởi tạo giữ một số giá trị tạp nham, và nó làm việc debug chương trình trở lên khó khăn hơn.
Con trỏ và Mảng trong C++
Con trỏ và Mảng có mối liên hệ chặt chẽ. Thực tế, con trỏ và mảng là có thể thay thế cho nhau trong một số trường hợp. Ví dụ, một con trỏ mà trỏ tới phần đầu mảng có thể truy cập mảng đó bởi sử dụng: hoặc con trỏ số học hoặc chỉ mục mảng. Bạn xét ví dụ sau:
#include <iostream> using namespace std; const int MAX = 3; int main () { int mang[MAX] = {10, 100, 200}; int *contro; // chung ta co mot mang dia chi trong con tro. contro = mang; for (int i = 0; i < MAX; i++) { cout << "Dia chi cua mang[" << i << "] = "; cout << contro << endl; cout << "Gia tri cua mang[" << i << "] = "; cout << *contro << endl; // tro toi vi tri tiep theo contro++; } return 0; }
Chạy chương trình C++ trên sẽ cho kết quả như hình sau:
Tuy nhiên, con trỏ và mảng không hoàn toàn thay thế được cho nhau. Ví dụ, bạn xét chương trình sau:
#include <iostream> using namespace std; const int MAX = 3; int main () { int mang[MAX] = {10, 100, 200}; for (int i = 0; i < MAX; i++) { *mang = i; // Day la cu phap chinh xac mang++; // cu phap nay la sai. Ban nen chu y. } return 0; }
Việc áp dụng toán tử con trỏ * tới biến mang là hoàn hảo, nhưng nó không hợp lệ khi sửa đổi giá trị biến mang. Lý do là biến mang là một constant mà trỏ tới phần đầu mảng và không thể được sử dụng như là l-value.
Bởi vì, một tên mảng tạo một hằng con trỏ, nó có thể vẫn được sử dụng trong các biểu thức con trỏ, miễn là nó không bị sửa đổi. Ví dụ sau là một lệnh hợp lệ mà gán mang[2] giá trị 500.
*(mang + 2) = 500;
Lệnh trên là hợp lệ và sẽ biên dịch thành công bởi vì mang không bị thay đổi.
Trên đây là một vài vấn đề cơ bản về con trỏ trong C/C++. Mời các bạn đón đọc các phần tiếp theo về Pointer trong C/C++ trên trang vncoder. Xin cảm ơn!
Bạn có thể đọc Part 2 tại: vncoder.vn
Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!