Con trỏ trong C là gì? Hướng dẫn cách khai báo và gán giá trị

Con trỏ trong C là một khái niệm khá quen thuộc với bất kỳ ai muốn học về ngôn ngữ C. Trong bài này, IoTZone sẽ giới thiệu đến bạn khái niệm con trỏ trong ngôn ngữ C là gì, cách sử dụng con trỏ trong chương trình và một số lưu ý cần thiết khi dùng chúng. Cùng theo dõi nhé!

Con trỏ trong C là gì

Con trỏ trong C (C Pointers) thực tế cũng chỉ là một biến có chứa giá trị, cho phép chúng ta khai báo, gán giá trị và địa chỉ riêng vào trong nó.

Tuy nhiên, điểm khác biệt là con trỏ không lưu các giá trị bình thường như những biến khác, mà chúng lưu giá trị gồm 1 địa chỉ nào đó của các biến khác hoặc một vị trí trong bộ nhớ RAM. Do đó, chúng ta có thể truy cập và sử dụng dữ liệu được lưu trữ tại vị trí đó thông qua con trỏ trong C.

Giới thiệu con trỏ trong C là gì?

Ví dụ: Mình sẽ dùng chương trình sau làm ví dụ:

// Khai báo một giá trị n là số nguyên
// Lưu giá trị n trong bộ nhớ tại vị trí 0x0074
int n;

Khi chạy chương trình này, một vị trí bộ nhớ trong RAM sẽ được dùng để lưu trữ giá trị n (tại vị trí 0x0074). Do đó, mỗi khi chương trình cần dùng đến biến n, thì chương trình sẽ đi tới vị trí 0x0074 để lấy giá trị của nó.

Nói ngắn gọn, con trỏ trong ngôn ngữ C là một biến, bên trong có chứa địa chỉ vị trí và giá trị mà nó đang lưu trữ.

Cách làm việc với con trỏ trong C

Khi làm việc với con trỏ, chúng ta sẽ có một số thao tác chính như sau:

Khai báo con trỏ

Để khai báo con trỏ, bạn có thể sử dụng cú pháp như khi khai báo các biến bình thường, cụ thể như sau:

kiểu dữ liệu*tên biến

Trong đó:

  • Kiểu dữ liệu là một trong các loại như float, void, int, double, char,…
  • Dấu * trong cú pháp thể hiện việc chúng ta đang khai báo con trỏ
  • Với tên biến thì bạn có thể đặt một tên nào đó bất kỳ mình thích

Ví dụ, nếu cần khai báo con trỏ tới biến có kiểu dữ liệu là kiểu nguyên, bạn dùng cú pháp int *p_i. Nếu cần dùng con trỏ để trỏ tới các biến thực, bạn dùng float *p_f,…. Tương tự với các biến khác.

Lưu ý quan trọng: Kiểu dữ liệu của con trỏ trong C phải trùng khớp với kiểu dữ liệu giá trị của biến mà nó đang trỏ đến. Bạn có thể xem ví dụ sau để hiểu rõ hơn:

int a = 5;
char b = 3;

int *px = &a   //Chương trình ĐÚNG vì x và a cùng kiểu dữ liệu và đều là int
int *px = &b   //Chương trình SAI vì x và b không cùng kiểu dữ liệu

Gán địa chỉ trong ngôn ngữ C

Sau khi khai báo con trỏ, việc thứ 2 bạn cần làm là gán địa chỉ cho nó. Nếu không thực hiện bước này, thì giá trị bên trong con trỏ có thể xem là giá trị rác, có thể ảnh hưởng đến chương trình (chẳng hạn như làm chương trình xảy ra lỗi hoặc không hoạt động đúng chẳng hạn).

Để gán địa chỉ, bạn làm như bên dưới:

int *x, value;
value = 4;
x = &value;    //Gán địa chỉ cho con trỏ x là địa chỉ của value

Ngoài ra, bạn cũng có thể lựa chọn khởi tạo và gán địa chỉ cho con trỏ cùng lúc, ví dụ như sau:

int value = 5;
int *x = &value   //Khai báo con trỏ x và gán địa chỉ cho con trỏ là địa chỉ của value

Trong trường hợp bạn chỉ tạo con trỏ để đó chứ chưa cần sử dụng ngay, bạn có thể sử dụng NULL như bên dưới:

int *x_int = NULL;

Cách xem giá trị con trỏ trong C đang tham chiêu

Khi chúng ta gán giá trị địa chỉ một vị trí nào đó cho con trỏ, chúng ta có thể truy cập vào vị trí đó để xem hoặc sửa đổi giá trị được lưu trữ.

Để làm điều đó, chúng ta vẫn sử dụng ký hiệu dấu hoa thị (*), tuy nhiên lần này chúng ta đặt ký hiệu này trước biến con trỏ.

Ví dụ: Để in giá trị mà con trỏ x đang trỏ tới, chúng ta dùng cấu trúc như sau:

printf("%d\n", *x);

Có thể tham chiếu con trỏ A đến con trỏ B được không?

Một con trỏ cũng có thể trỏ tới một biến con trỏ khác, điều này hoàn toàn có thể. Chúng ta chỉ cần sử dụng 2 dấu hoa thị là được, chẳng hạn như:

int a = 51;
int *b = &a;
int **c = &b;

Trong chương trình trên, c là một con trỏ đang tham chiếu đến một con trỏ khác là b. Con trỏ b lại tham chiếu đến giá trị của biến a.

Vai trò của con trỏ trong C

Thay đổi giá trị mà con trỏ đang trỏ đến

Với con trỏ, chúng ta có thể thay đổi giá trị của biến mà chúng đang tham chiếu đến.

Ví dụ, mình đã tạo một biến là a:

int a = 52;   // Khởi tạo giá trị ban đầu của a là 52

Dĩ nhiên, chúng ta có thể thay đổi trực tiếp giá trị của biến a, bằng cách dùng cú pháp a = 621 chẳng hạn. Tuy nhiên, mình sẽ giới thiệu đến bạn cách thay đổi giá trị biến a gián tiêp qua con trỏ như sau:

int *p = &a;   // Tham chiếu con trỏ p đến vị trí bộ nhớ của biến a
*p = 621;     // Thay đổi giá trị của con trỏ p thành 621, tương đương với biến a đổi thành 621

Phân bổ bộ nhớ động

Một trong các ứng dụng mạnh mẽ khác của con trỏ trong C là phân bổ lại bộ nhớ động trực tiếp trong lúc đang chạy, thay vì trong lúc biên dịch code.

Ví dụ, mình sử dụng malloc để cấp phát bộ nhớ và trả về một con trỏ tới bộ nhớ đã được cấp:

int *p = (int*)malloc(sizeof(int));

Trong chương trình trên, p là một con trỏ chỉ tới một kiểu dữ liệu số nguyên, đã được phân bổ bộ nhớ thông qua hàm malloc. Mình sử dụng sizeof để xác định kích thước của số nguyên.

Sau khi cấp phát bộ nhớ, chúng ta có thể sử dụng biến con trỏ này tương tự như với bất kỳ biến con trỏ trong C nào khác.

Khi dùng xong bộ nhớ này, bạn nên giải phóng chúng bằng hàm free, để giải phóng các bộ nhớ đã được cấp cho con trỏ p:

free(p);

Một số lỗi thường gặp khi sử dụng con trỏ

Khi sử dụng con trỏ trong C, có thể bạn sẽ gặp một số lỗi phổ biến sau, bạn có thể tham khảo:

  • Con trỏ chưa được gán giá trị địa chỉ và khởi tạo: Dù bạn đã khai báo con trỏ trong C, nhưng không gán giá trị địa chỉ hợp lệ cho chúng.
  • Hủy tham chiêu con trỏ Null: Lỗi này có thể gây gián đoạn chương trình
  • Sử dụng sai kiểu dữ liệu cho con trỏ: Ví dụ, bạn khai báo con trỏ với kiểu dữ liệu số nguyên, nhưng lại tham chiếu chúng đến một biến có kiểu dữ liệu ký tự => Chương trình lỗi

Lời kết

Nhìn chung, con trỏ trong C là một công cụ mạnh mẽ và thường được dùng nhiều trong viết chương trình. Tuy nhiên, với các bạn mới bắt đầu, có thể chúng sẽ hơi khó và phức tạp. Tuy nhiên, bạn có thể luyện tập thường xuyên để thành thạo vận dụng nó, làm quen với các cấu trúc lập trình nâng cao hơn nhé!

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *