Cấp phát bộ nhớ động trong C: malloc(), calloc()
⚡ Tóm tắt thông minh
Việc cấp phát bộ nhớ động trong C dự trữ và giải phóng bộ nhớ heap trong thời gian chạy bằng cách sử dụng các hàm malloc(), calloc(), realloc() và free() từ thư viện stdlib.h, cho phép các chương trình định kích thước mảng và cấu trúc một cách linh hoạt khi các yêu cầu chưa được biết trước khi thực thi.
Quản lý bộ nhớ trong C hoạt động như thế nào?
Khi bạn khai báo một biến sử dụng kiểu dữ liệu cơ bản, trình biên dịch C sẽ tự động cấp phát không gian bộ nhớ cho biến đó trong một vùng bộ nhớ được gọi là ngăn xếp.
Ví dụ, một biến kiểu float thường chiếm 4 byte (tùy thuộc vào nền tảng) khi được khai báo. Chúng ta có thể kiểm chứng thông tin này bằng cách sử dụng toán tử sizeof như trong ví dụ bên dưới.
#include <stdio.h> int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}
Đầu ra sẽ là:
The size of float is 4 bytes
Ngoài ra, một mảng có kích thước xác định được phân bổ thành các khối bộ nhớ liền kề, mỗi khối có kích thước cho một phần tử:
#include <stdio.h> int main() { float arr[10]; printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}
Kết quả là:
The size of the float array with 10 element is 40
Như đã học, khi khai báo một kiểu dữ liệu cơ bản hoặc một mảng, bộ nhớ được quản lý tự động. Tuy nhiên, có một quy trình cấp phát bộ nhớ trong C cho phép bạn triển khai một chương trình mà kích thước mảng chưa được quyết định cho đến khi bạn chạy chương trình (thời gian chạy). Quy trình này được gọi là "cấp phát bộ nhớ động".
Phân bổ bộ nhớ động trong C
Cấp phát bộ nhớ động là việc cấp phát và giải phóng bộ nhớ thủ công theo nhu cầu lập trình của bạn. Bộ nhớ động được quản lý và cấp phát bằng các con trỏ trỏ đến không gian bộ nhớ mới được cấp phát trong một vùng mà chúng ta gọi là heap.
Giờ đây, bạn có thể tạo và hủy một mảng phần tử một cách linh hoạt trong thời gian chạy mà không gặp vấn đề gì. Tóm lại, việc quản lý bộ nhớ tự động sử dụng ngăn xếp và Phân bổ bộ nhớ động C sử dụng vùng heap.
Các thư viện có các chức năng chịu trách nhiệm Quản lý bộ nhớ động.
| Chức năng | Mục đích |
|---|---|
| malloc () | Phân bổ bộ nhớ có kích thước được yêu cầu và trả về con trỏ tới byte đầu tiên của không gian được phân bổ. |
| calloc () | Phân bổ không gian cho các phần tử của mảng. Khởi tạo các phần tử về 0 và trả về một con trỏ vào bộ nhớ. |
| realloc () | Nó được sử dụng để sửa đổi kích thước của không gian bộ nhớ được phân bổ trước đó. |
| Miễn phí() | Giải phóng hoặc làm trống không gian bộ nhớ được phân bổ trước đó. |
Hãy thảo luận về các chức năng trên với ứng dụng của chúng
hàm malloc() trong C
Hàm C malloc() là viết tắt của cấp phát bộ nhớ. Đây là một chức năng được sử dụng để phân bổ động một khối bộ nhớ. Nó dự trữ không gian bộ nhớ có kích thước được chỉ định và trả về con trỏ null trỏ đến vị trí bộ nhớ. Con trỏ trả về thường có kiểu void. Điều đó có nghĩa là chúng ta có thể gán hàm C malloc() cho bất kỳ con trỏ nào.
Cú pháp của hàm malloc():
ptr = (cast_type *) malloc (byte_size);
Ở đây,
- ptr là con trỏ của cast_type.
- Hàm C malloc() trả về một con trỏ tới bộ nhớ được cấp phát của byte_size.
Ví dụ về malloc():
Example: ptr = (int *) malloc (50)
Khi câu lệnh này được thực thi thành công, không gian bộ nhớ 50 byte sẽ được dành riêng. Địa chỉ của byte đầu tiên của vùng dành riêng được gán cho con trỏ ptr kiểu int.
Hãy xem xét một ví dụ khác:
#include <stdlib.h> int main(){ int *ptr; ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */ if (ptr != NULL) { *(ptr + 5) = 480; /* assign 480 to sixth integer */ printf("Value of the 6th integer is %d",*(ptr + 5)); } }
Đầu ra:
Value of the 6th integer is 480
- Lưu ý rằng sizeof(*ptr) được sử dụng thay vì sizeof(int) để làm cho mã mạnh mẽ hơn khi khai báo *ptr được ép kiểu sang một kiểu dữ liệu khác sau này.
- Việc phân bổ có thể thất bại nếu bộ nhớ không đủ. Trong trường hợp này, nó trả về một con trỏ NULL. Vì vậy, bạn nên bao gồm mã để kiểm tra con trỏ NULL.
- Hãy nhớ rằng bộ nhớ được phân bổ là liên tục và nó có thể được coi như một mảng. Chúng ta có thể sử dụng phép tính số học con trỏ để truy cập các phần tử mảng thay vì sử dụng dấu ngoặc vuông [ ]. Chúng tôi khuyên bạn nên sử dụng + để tham chiếu đến các phần tử mảng vì sử dụng phép tăng dần ++ hoặc += sẽ thay đổi địa chỉ được lưu trữ bởi con trỏ.
Hàm Malloc() cũng có thể được sử dụng với kiểu dữ liệu ký tự cũng như các kiểu dữ liệu phức tạp như cấu trúc.
hàm free() trong C
Bộ nhớ dành cho biến được tự động giải phóng tại thời điểm biên dịch. Trong cấp phát bộ nhớ động, bạn phải phân bổ bộ nhớ một cách rõ ràng. Nếu không thực hiện, bạn có thể gặp phải lỗi hết bộ nhớ.
Hàm free() được gọi để giải phóng/cấp phát bộ nhớ trong ngôn ngữ C. Bằng cách giải phóng bộ nhớ trong chương trình, bạn sẽ có thêm bộ nhớ để sử dụng sau này.
Ví dụ:
#include <stdio.h> int main() { int* ptr = malloc(10 * sizeof(*ptr)); if (ptr != NULL){ *(ptr + 2) = 50; printf("Value of the 2nd integer is %d",*(ptr + 2)); } free(ptr); }
Đầu ra:
Value of the 2nd integer is 50
hàm calloc() trong C
Hàm calloc() của C là viết tắt của phân bổ liên tiếp. Hàm này được sử dụng để phân bổ nhiều khối bộ nhớ. Đây là hàm phân bổ bộ nhớ động được sử dụng để phân bổ bộ nhớ cho các cấu trúc dữ liệu phức tạp như mảng và cấu trúc.
Hàm Malloc() được sử dụng để phân bổ một khối không gian bộ nhớ trong khi hàm calloc() trong C được sử dụng để phân bổ nhiều khối không gian bộ nhớ. Mỗi khối được phân bổ bởi hàm calloc() có cùng kích thước.
Cú pháp của hàm calloc():
ptr = (cast_type *) calloc (n, size);
Câu lệnh trên được sử dụng để cấp phát n khối bộ nhớ có cùng kích thước.
Sau khi không gian bộ nhớ được phân bổ, tất cả các byte được khởi tạo về 0.
Con trỏ hiện ở byte đầu tiên của vùng nhớ được phân bổ sẽ được trả về.
Bất cứ khi nào có lỗi phân bổ không gian bộ nhớ chẳng hạn như thiếu bộ nhớ thì con trỏ null sẽ được trả về.
Ví dụ về calloc():
Chương trình dưới đây tính tổng của một dãy số học.
#include <stdio.h> int main() { int i, * ptr, sum = 0; ptr = calloc(10, sizeof(int)); if (ptr == NULL) { printf("Error! memory not allocated."); exit(0); } printf("Building and calculating the sequence sum of the first 10 terms \ n "); for (i = 0; i < 10; ++i) { * (ptr + i) = i; sum += * (ptr + i); } printf("Sum = %d", sum); free(ptr); return 0; }
Kết quả:
Building and calculating the sequence sum of the first 10 terms
Sum = 45
calloc() so với malloc(): Sự khác biệt chính
Sau đây là sự khác biệt chính giữa malloc() Vs calloc() trong C:
Hàm calloc() thường phù hợp và hiệu quả hơn hàm malloc(). Trong khi cả hai hàm đều được sử dụng để phân bổ không gian bộ nhớ, calloc() có thể phân bổ nhiều khối cùng một lúc. Bạn không phải yêu cầu một khối bộ nhớ mỗi lần. Hàm calloc() được sử dụng trong các cấu trúc dữ liệu phức tạp đòi hỏi không gian bộ nhớ lớn hơn.
Khối bộ nhớ được cấp phát bởi calloc() trong C luôn được khởi tạo bằng 0 trong khi ở hàm malloc() trong C, nó luôn chứa giá trị rác.
hàm realloc() trong C
Sử dụng hàm `realloc()` trong C, bạn có thể thêm kích thước bộ nhớ vào vùng nhớ đã được cấp phát. Nó mở rộng khối bộ nhớ hiện tại trong khi vẫn giữ nguyên nội dung ban đầu. `realloc()` trong C có nghĩa là cấp phát lại bộ nhớ.
realloc() cũng có thể được sử dụng để giảm kích thước bộ nhớ được phân bổ trước đó.
Cú pháp của hàm realloc():
ptr = realloc (ptr,newsize);
Câu lệnh trên phân bổ một không gian bộ nhớ mới với kích thước được chỉ định trong biến newsize. Sau khi thực hiện hàm, con trỏ sẽ được trả về byte đầu tiên của khối bộ nhớ. Kích thước mới có thể lớn hơn hoặc nhỏ hơn bộ nhớ trước đó. Chúng ta không thể chắc chắn rằng khối mới được cấp phát có trỏ đến cùng vị trí với khối bộ nhớ trước đó hay không. Chức năng này sẽ sao chép tất cả dữ liệu trước đó vào vùng mới. Nó đảm bảo rằng dữ liệu sẽ được an toàn.
Ví dụ về realloc():
#include <stdio.h> int main () { char *ptr; ptr = (char *) malloc(10); strcpy(ptr, "Programming"); printf(" %s, Address = %u\n", ptr, ptr); ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size strcat(ptr, " In 'C'"); printf(" %s, Address = %u\n", ptr, ptr); free(ptr); return 0; }
Bất cứ khi nào realloc() trong C dẫn đến một hoạt động không thành công, nó sẽ trả về một con trỏ null và dữ liệu trước đó cũng được giải phóng.
Mảng động trong C
Mảng động trong C cho phép số lượng phần tử tăng lên khi cần thiết. C Mảng động được sử dụng rộng rãi trong các thuật toán khoa học máy tính.
Trong chương trình sau, chúng tôi đã tạo và thay đổi kích thước một mảng động trong C
#include <stdio.h> int main() { int * arr_dynamic = NULL; int elements = 2, i; arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks for (i = 0; i < elements; i++) arr_dynamic[i] = i; for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]); elements = 4; arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements printf("After realloc\n"); for (i = 2; i < elements; i++) arr_dynamic[i] = i; for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]); free(arr_dynamic); }
Kết quả chương trình mảng động C tại màn hình:
arr_dynamic[0]=0 arr_dynamic[1]=1 After realloc arr_dynamic[0]=0 arr_dynamic[1]=1 arr_dynamic[2]=2 arr_dynamic[3]=3


