Trong bài viết này, chúng ta sẽ tìm hiểu về con trỏ trong ngôn ngữ lập trình C. Nội dung sẽ giúp bạn hiểu rõ về khái niệm và cách sử dụng con trỏ trong C. Đây là một phần kiến thức khá quan trọng, vì vậy bài viết này sẽ tập trung vào các khái niệm cơ bản về con trỏ. Các bài viết tiếp theo sẽ cung cấp thông tin chi tiết hơn về cách làm việc với mảng, cấp phát bộ nhớ và quản lý bộ nhớ. Tôi hy vọng rằng loạt bài viết về con trỏ trong C này sẽ giúp bạn trở nên tự tin hơn.
Địa chỉ của biến trong C
Để hiểu và sử dụng con trỏ trong C, trước tiên bạn cần hiểu khái niệm địa chỉ trong C. Nếu bạn đã theo dõi khóa học C của tôi từ đầu, bạn đã nghe tới khái niệm này. Phần này sẽ giúp bạn hiểu rõ hơn vấn đề này.
int number; printf("Nhập number = "); scanf("%d", &number); printf("number = %d", number);
Bạn hãy xem ví dụ trên. Tại sao khi sử dụng hàm scanf
chúng ta cần truyền vào &number
, trong khi đó hàm printf
lại không có dấu &
? Lý do là khi bạn muốn nhập giá trị cho biến, hàm scanf
cần biết địa chỉ của biến đó trong bộ nhớ.
Mỗi biến mà bạn khai báo đều có địa chỉ riêng và giá trị mà nó đang lưu trữ. Để xem địa chỉ của biến, bạn thêm dấu &
vào trước tên biến. Xem ví dụ dưới đây:
#include int main() { int number = 5; printf("Giá trị của number = %d", number); // Truy xuất địa chỉ bằng cách thêm & trước tên biến printf("\nĐịa chỉ của number = %d", &number); return 0; }
Kết quả khi chạy chương trình:
Giá trị của number = 5 Địa chỉ của number = 6487580
Chú ý:
- Bạn có thể nhận được các địa chỉ khác nhau mỗi khi chạy code trên.
- Để nhận giá trị địa chỉ là hexa như hình ở đầu bài, bạn thay
%d
bằng%x
.
Con trỏ trong C
Con trỏ là gì? Con trỏ trong C cũng chỉ là một biến, có thể khai báo, khởi tạo và lưu trữ giá trị và có địa chỉ của riêng nó. Nhưng con trỏ không lưu giá trị bình thường, nó là một biến trỏ tới một địa chỉ khác, tức là mang giá trị là một địa chỉ.
Chúng ta cùng thống nhất một số khái niệm khi làm việc với con trỏ nhé:
- Giá trị của con trỏ: địa chỉ mà con trỏ trỏ đến.
- Địa chỉ của con trỏ: địa chỉ của bản thân biến con trỏ đó.
- Giá trị của biến mà con trỏ đang trỏ tới.
- Địa chỉ của biến mà con trỏ đang trỏ tới = giá trị của con trỏ.
Chính vì con trỏ mang địa chỉ, nó là một biến đặc biệt có những quyền năng mà biến bình thường không có. Nhờ việc nó mang địa chỉ, nó có thể trỏ lung tung trong bộ nhớ. Điều này là một điểm mạnh nếu ta khai thác tốt, nhưng nếu quản lý không tốt thì lại là một tai hại.
Cách khai báo con trỏ
Con trỏ trong C cũng có thể khai báo giống như biến bình thường, tên biến là một định danh hợp lệ. Cú pháp như sau:
*
Trong đó:
- Kiểu dữ liệu có thể là: void, int, float, double,...
- Dấu
*
trước tên biến là ký hiệu báo cho trình biên dịch biết ta đang khai báo con trỏ.
int *p_i; // Khai báo con trỏ để trỏ tới biến kiểu nguyên int *p, val; // Khai báo con trỏ p kiểu int, biến val (không phải con trỏ) kiểu int float *p_f; // Khai báo con trỏ để trỏ tới biến kiểu thực char *p_char; // Khai báo con trỏ để trỏ tới biến kiểu ký tự void *p_v; // Con trỏ kiểu void (không kiểu)
Gán giá trị cho con trỏ
Sau khi khai báo con trỏ, bạn cần khởi tạo giá trị cho nó. Nếu con trỏ được sử dụng mà không được khởi tạo, giá trị của nó sẽ là giá trị rác, điều này sẽ làm chương trình của bạn chạy không đúng, thậm chí là nguy hiểm nếu giá trị rác đó chẳng may lại chính là địa chỉ của một biến nào đó bạn đang sử dụng.
int *p, value; value = 5; p = &value; // Khởi tạo giá trị cho con trỏ p là địa chỉ của value
Hoặc bạn cũng có thể khai báo và khởi tạo đồng thời:
int value = 5; int *p = &value; // Khai báo con trỏ p và khởi tạo giá trị cho con trỏ là địa chỉ của value
Lưu ý:
- Con trỏ khi khai báo nên được khởi tạo giá trị ngay.
- Con trỏ kiểu void là loại biến con trỏ tổng quát, nó có thể nhận địa chỉ của biến bất kỳ ở bất cứ kiểu dữ liệu nào.
int value, *p; // Sai! p cần địa chỉ cơ, // value không phải là cái địa chỉ đó. p = value; // Sai! *p là giá trị của biến mà con trỏ đang trỏ tới, // &value là địa chỉ. *p = &value; // Đúng rồi! p cần một địa chỉ, // &value là địa chỉ của biến value. p = &value; // Đúng! *p = value;
Tài liệu tham khảo
Mặc dù tôi đã cố gắng trình bày tỉ mỉ, nhưng có thể vẫn còn thiếu sót. Dưới đây là một số tài liệu bạn nên đọc thêm để hiểu sâu hơn về con trỏ trong C:
- Tìm hiểu bản chất của con trỏ từ cơ bản tới nâng cao
- C Pointers (With Example)