- Cấu trúc rẽ nhánh
6. Con trỏ và hằng (pointers and constants)
C++ cung cấp khái niệm hằng (const) để thể hiện rằng đối tượng nào đó là cố định, không thể thay đổi được. Điều này có những lợi ích nhất định tùy thuộc vào ngữ cảnh. Ví dụ: bằng việc khai báo hằng tượng trưng (symbolic constant) sẽ giúp việc viết chương trình được thuận lợi và dễ bảo trì hơn là việc “nhúng” hẳn các hằng trực tiếp vào trong code. Ví dụ:
C++ Code:
Lựa chọn code | Ẩn/Hiện code
const int PI=3.14; // như thế này tốt hơn là nhúng hẳn số 3.14 và từng đoạn code
Hoặc ví dụ trong một số trường hợp, khi ta truyền biến bằng tham chiếu cho hàm nhưng chỉ cho phép hàm đọc dữ liệu mà không cho phép chỉnh sửa dữ liệu thì tham số hình thức của hàm sẽ được khai báo là tham chiếu hằng hoặc con trỏ hằng. Sau đây là một số ví dụ khai báo hằng:
C++ Code:
Lựa chọn code | Ẩn/Hiện code
const int x=12; // x là hằng nguyên, giá trị 12
const char ch[]={‘I’, ‘H’, ‘A’, ‘T’, ‘E’, ‘U’}; // ch là con trỏ hằng (const pointer) trỏ tới phần tử đầu mảng
const int number; // Error: phải khởi tạo hằng ngay khi khai báo
Khi sử dụng con trỏ có hai đối tượng cần quan tâm: thứ nhất là bản thân con trỏ và thứ hai là đối tượng mà con trỏ trỏ tới. Ta phân biệt hai khái niệm: con trỏ hằng (const pointer, một số người thì gọi là hằng con trỏ, cái này tùy thuộc vào cách dịch) và con trỏ tới hằng (pointer to const).
Con trỏ hằng (const pointer): là con trỏ luôn luôn trỏ tới một địa chỉ cố định, và không thể thay
đổi được trong suốt thời gian thực thi chương trình. Mọi nỗ lực sử đổi như gán địa chỉ mới cho con trỏ hằng hay tăng giảm con trỏ hằng đều gây lỗi biên dịch. Ví dụ quen thuộc nhất về con trỏ hằng là tên mảng:
C++ Code:
Lựa chọn code | Ẩn/Hiện code
// tên mảng là một const pointer
char str[]=”I am first_pace, from congdongCViet”; // str là con trỏ hằng, nhưng các str[i] không phải hằng
char* p1; char* p2;
p1=str; // ok: vì p1 không phải con trỏ hằng nên được phép trỏ đi chỗ khác
str=p2; // error: vì str là con trỏ hằng nên không thể trỏ đi chỗ khác được
Con trỏ tới hằng (pointer to const): là con trỏ trỏ tới một đối tượng hằng, tức là với con trỏ đó,
đối tượng được "hiểu" là hằng. Con trỏ này có thể đọc (read) dữ liệu của đối tượng nhưng không được sửa đổi (write) nội dung của đối tượng. Mọi nỗ lực sửa đổi nội dung của đối tượng sẽ gây ra lỗi biên dịch. Con trỏ tới hằng khác con trỏ hằng ở hai điểm:thứ nhất, khi không thích trỏ tới đối tượng này nó có thể trỏ tới đối tượng khác, con trỏ hằng không được phép làm điều này. Thứ hai, con trỏ tới đối tượng hằng (pointer to constant) thì nó hiểu đối tượng là hằng, và không được phép “ghi” dữ liệu lên đối tượng, còn con trỏ hằng nó vẫn cho phép thao tác trên dữ liệu, nó hoàn toàn giống con trỏ bình thường ngoài việc nó chỉ trỏ tới một địa chỉ cố định.
Khi ta đặt từ khóa const trước một khai báo con trỏ thông thường thì điều đó khiến con trỏ “hiểu” đối tượng là hằng chứ không phải con trỏ là hằng. Nghĩa là nó chỉ có thể “đọc” dữ liệu từ đối tượng nhưng không thể “ghi” dữ liệu lên đối tượng. Trong trường hợp này nó là con trỏ đến đối tượng hằng (pointer to const). Tuy nhiên đối tượng không phải là trở thành hằng, mà nó chỉ được con trỏ đó “hiểu là hằng” (chỉ là là hằng đối với con trỏ đó), đối với con trỏ khác nó
có thể là biến. Để hiểu rõ hơn ta xem xét ví dụ sau: C++ Code:
Lựa chọn code | Ẩn/Hiện code
char str[]="Hello, Ajinomoto"; // str là con trỏ hằng, nhưng str[i] thì không
const char* p=str; // con trỏ p (pointer to const) trỏ đến đầu mảng str, và p “hiểu” str[i] là các hằng
p[3]='X'; // Error: không được, vì p chỉ được đọc dữ liệu chứ không được ghi dữ liệu lên str[i]
str[3]='X'; // Ok: vì str[i] chỉ được coi là hằng theo “cách hiểu” của p, với str thì các str[i] vẫn là biến
Để khai báo một con trỏ hằng ta phải sử dụng *const chứ không phải const. Xem xét ví dụ sau: C++ Code:
Lựa chọn code | Ẩn/Hiện code
int x, y;
int *const px=&x; // bây giờ px luôn luôn trỏ đến x, không thể thay đổi được
px=&y; // Error: px là con trỏ hằng, không được phép trỏ đi chỗ khác
Có thể tóm lại như trong ví dụ sau: C++ Code:
Lựa chọn code | Ẩn/Hiện code
const int* p1=&x; // con trỏ tới hằng int (pointer to const char)
int const* p2=&x; // hoàn toàn giống p1, con trỏ tới hằng int (pointer to const char)
int *const p3=&x; // con trỏ hằng tới biến char (pointer to const char)
Để dễ nhớ các bạn có thể đọc từ trái sang phải như sau: “p3 là con trỏ hằng tới kiểu int”.
Chú ý: một đối tượng hằng là cố định, vì vậy gán địa chỉ của nó cho một “con trỏ tự do” sẽ gây lỗi
biên dịch. Bởi vì thông qua con trỏ ta có thể thay đổi giá trị của hằng, và điều này là không được phép. C++ chỉ cho phép gán địa chỉ của một đối tượng hằng cho một con trỏ trỏ tới đối tượng hằng (pointer to const). Ví dụ chương trình sau:
C++ Code:
Lựa chọn code | Ẩn/Hiện code
const int x=100; // khai báo hằng nguyên x=100
const int* p1=&x; // Ok: con trỏ p1 trỏ tới hằng x
int* p2=&x; // Error: con trỏ p2 là “con trỏ tự do”, không được phép trỏ tới hằng x
Câu hỏi mở rộng nhé: việc gán địa chỉ của một đối tượng hằng cho con trỏ hằng (const pointer) là
có được phép hay không? Tại sao? Nhưng từ từ hãy post vội, đợi mình viết nốt loạt tut này đã
Hết bài 10