Nếu bạn đang học hoặc chỉ đang tìm hiểu về ngôn ngữ lập trình C, chắc hẳn bạn đã từng nghe đến thuật ngữ “con trỏ”. Con trỏ trong C là một trong những tính năng quan trọng và mạnh mẽ nhất của ngôn ngữ lập trình này. Con trỏ được sử dụng ở mọi nơi trong C, cho phép bạn thao tác trực tiếp vào bộ nhớ, điều này rất hữu ích khi lập trình. Trong bài viết dưới đây, hãy cùng ICANTECH tìm hiểu về con trỏ trong C nhé!
Con trỏ là 1 tính năng hữu ích và thật sự hữu ích. Tuy nhiên, việc sử dụng con trỏ sai cách có thể gây ra lỗi và các sự cố liên quan đến bộ nhớ. Chính vì thế mà điều quan trọng là bạn phải hiểu cách sử dụng con trỏ C một cách chính xác.
Trong lập trình C, con trỏ là một biến dùng để lưu trữ địa chỉ bộ nhớ của một biến khác. Con trỏ cho phép các chương trình có quyền thao tác trực tiếp vào bộ nhớ, điều này đặc biệt hữu ích khi bạn thực hiện các tác vụ như cấp phát bộ nhớ động hay làm việc với mảng.
Một con trỏ được khai báo bằng ký hiệu (*) đặt trước tên biến. Ví dụ: để khai báo một con trỏ tới một số nguyên, bạn sẽ sử dụng cú pháp: int *ptr;
Trong ngôn ngữ lập trình C, con trỏ được sử dụng trong các trường hợp sau:
Trong một số trường hợp, lập trình viên cũng sử dụng con trỏ khi lập trình C để tăng hiệu quả code.
Vì con trỏ chứa các địa chỉ bộ nhớ nên bạn có thể thực hiện các phép tính số học trên đó, để di chuyển chúng đến các vị trí khác nhau của bộ nhớ.
Điều này thường được sử dụng trong các hoạt động của mảng, khi mà bạn sử dụng con trỏ để truy cập các phần tử của mảng.
Ví dụ để in phần tử đầu tiên của mảng số nguyên bằng con trỏ, bạn có đoạn code như sau:
int arr[] = {1, 2, 3};
int *p = arr; // p points to the first element of arr
printf("%d\n", *p); // prints 1
Trong đó:
Tiếp theo, ICANTECH sẽ hướng dẫn bạn một vài cách sử dụng con trỏ trong C cơ bản.
Để khai báo một biến con trỏ trong C, chúng ta sử dụng ký hiệu dấu * đứng trước tên biến. Có hai cách khai báo biến con trỏ trong ngôn ngữ C là:
int *p;
int* p;
Cả hai cách khai báo này đều mang ý nghĩa tương đương nhau. Chúng khai báo một biến con trỏ có tên "p", có chứa địa chỉ bộ nhớ của một số nguyên.
Tuy nhiên, nếu bạn khai báo nhiều biến trong một câu lệnh, bạn cần thêm dấu * trước mỗi tên biến để hệ thống ghi nhận tất cả chúng đều là con trỏ. Ví dụ:
int *p, *q, *r;
Ở ví dụ trên, chúng ta đang khai báo ba biến con trỏ có tên "p", "q" và "r" và có chứa địa chỉ bộ nhớ của một số nguyên.
Khi bạn khai báo một biến con trỏ, nó sẽ không tự động trỏ đến bất kỳ vị trí cụ thể nào trên bộ nhớ. Để khởi tạo một con trỏ có thể trỏ đến 1 biến hoặc 1 vị trí cụ thể, bạn cần sử dụng ký hiệu toán tử & để lấy địa chỉ của biến đó.
Ví dụ, để khởi tạo con trỏ p để trỏ tới một biến số nguyên có tên là x. Bạn sẽ viết cú pháp như mẫu để khai báo giá trị của p là địa chỉ bộ nhớ của x.
int x = 42;
int *p = &x;
Khi có một con trỏ, bạn có thể truy cập, sửa đổi giá trị được lưu trữ tại vị trí đó bằng cách hủy đăng ký con trỏ.
Để hủy đăng ký một con trỏ, bạn sử dụng ký hiệu dấu * đứng ở phía trước chính biến con trỏ. Ví dụ: để in giá trị của số nguyên mà p trỏ tới, bạn có mẫu sau:
printf("%d\n", *p);
Một con trỏ cũng có thể trỏ tới một biến của con trỏ khác, việc này được gọi là "con trỏ tới con trỏ". Bạn thực hiện khai báo một con trỏ tới một con trỏ bằng cách sử dụng hai dấu **. Ví dụ:
int x = 42;
int *p = &x;
int **q = &p;
Trong ví dụ trên, q là một con trỏ tới một con trỏ. Nó trỏ đến địa chỉ của biến p, từ đó trỏ đến địa chỉ của biến x.
Để truyền một con trỏ tới một hàm, bạn chỉ cần khai báo tham số của hàm dưới dạng một con trỏ. Ví dụ:
void increment(int *p) {
(*p)++;
}
int main() {
int x = 42;
int *p = &x;
increment(p);
printf("%d\n", x); // prints 43
return 0;
}
Ở ví dụ trên, hàm tăng lấy một con trỏ tới một số nguyên (int *p) và tăng giá trị của số nguyên đó lên 1 đơn vị ((*p)++).
Trong main(), chúng ta khai báo số nguyên x và con trỏ p trỏ tới x. Sau đó chúng ta gọi hàm tăng, truyền vào con trỏ p. Sau khi gọi hàm, x đã được tăng lên 43.
Một trong những ứng dụng hữu ích nhất của con trỏ C là cấp phát bộ nhớ động. Điều này cho phép bạn phân bổ bộ nhớ ngay trong thời gian chạy chương trình, thay vì phải chờ đến lúc biên dịch.
Ví dụ dưới đây là cách sử dụng hàm malloc để cấp phát động bộ nhớ và trả về một con trỏ tới bộ nhớ vừa được cấp phát.
int *p = (int*)malloc(sizeof(int));
Ở đây, p là một con trỏ tới một số nguyên đã được cấp phát bằng malloc. Toán tử sizeof được sử dụng để xác định kích thước của một số nguyên tính theo byte.
Sau khi cấp phát bộ nhớ, bạn có thể sử dụng biến con trỏ như với bất kỳ con trỏ nào khác. Khi sử dụng xong, bạn nên giải phóng bộ nhớ được phân bổ cho p bằng hàm free. Ví dụ:
free(p);
Khi cần chuyển một con trỏ từ kiểu này sang kiểu khác, bạn áp dụng cú pháp (type *). Ví dụ:
double *p = (double *)malloc(sizeof(double));
Ở ví dụ trên, p được chuyển thành con trỏ kiểu double.
Con trỏ trong C là một công cụ mạnh mẽ với lập trình viên ngôn ngữ C. Tính năng con trỏ không những phổ biến mà còn rất hữu ích trong việc lập trình. Thành thạo con trỏ C, sẽ giúp bạn thao tác với bộ nhớ cũng như làm việc với các cấu trúc dữ liệu phức tạp dễ dàng hơn.
Cảm ơn bạn đã đọc bài viết, nếu bạn đang quan tâm đến học lập trình online thì hãy tham khảo ngay các khóa học lập trình dưới đây tại ICANTECH nhé!
Nguồn ảnh: ICANTECH.