Phân bổ bộ nhớ động trong C bằng cách sử dụng các hàm malloc(), calloc()
Trước khi bạn tìm hiểu cách phân bổ Bộ nhớ động trong C, hãy hiểu:
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 float thường mất 4 byte (tùy theo nền tảng) khi nó được khai báo. Chúng tôi có thể xác minh thông tin này bằng cách sử dụng kích thước toán tử như trong ví dụ dưới đây
#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 cho đến nay, khi khai báo một kiểu dữ liệu cơ bản hoặc một mảng, bộ nhớ sẽ được quản lý tự động. Tuy nhiên, có một quy trình cấp phát bộ nhớ trong C sẽ cho phép bạn triển khai một chương trình trong đó kích thước mảng không được quyết định cho đến khi bạn chạy chương trình của mình (thời gian chạy). Quá trình này được gọi là “Cấp phát bộ nhớ động".
Phân bổ bộ nhớ động trong C
Phân bổ bộ nhớ động là phân bổ thủ công và giải phóng bộ nhớ theo nhu cầu lập trình của bạn. Bộ nhớ động được quản lý và cung cấp bằng các con trỏ trỏ đến không gian bộ nhớ mới được phân bổ trong vùng mà chúng ta gọi là vùng 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
- Thông báo rằng kích thước(*ptr) đã được sử dụng thay vì kích thước(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ớ.
Miễn phí() hàm được gọi để giải phóng/giải phóng bộ nhớ trong C. Bằng cách giải phóng bộ nhớ trong chương trình của bạn, bạn sẽ có nhiều bộ nhớ hơn để sử dụng sau.
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 C realloc () chức năng, bạn có thể thêm nhiều kích thước bộ nhớ hơn vào bộ nhớ đã được phân bổ. Nó mở rộng khối hiện tại trong khi vẫn giữ nguyên nội dung gốc. realloc() trong C là viết tắt của việc phân bổ 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
Tổng kết
- Chúng ta có thể quản lý bộ nhớ một cách linh hoạt bằng cách tạo các khối bộ nhớ khi cần thiết trong heap
- Trong C Phân bổ bộ nhớ động, bộ nhớ được phân bổ tại thời điểm chạy.
- Cấp phát bộ nhớ động cho phép thao tác các chuỗi và mảng có kích thước linh hoạt và có thể thay đổi bất kỳ lúc nào trong chương trình của bạn.
- Nó được yêu cầu khi bạn không biết một cấu trúc cụ thể sẽ chiếm bao nhiêu bộ nhớ.
- Malloc() trong C là hàm cấp phát bộ nhớ động, viết tắt của cấp phát bộ nhớ, chặn bộ nhớ có kích thước cụ thể được khởi tạo thành giá trị rác
- Calloc() trong C là hàm cấp phát bộ nhớ liền kề, cấp phát nhiều khối bộ nhớ tại một thời điểm được khởi tạo bằng 0
- Realloc() trong C được sử dụng để phân bổ lại bộ nhớ theo kích thước đã chỉ định.
- Hàm Free() được sử dụng để xóa bộ nhớ được cấp phát động.