Hàm malloc() trong thư viện C với EXAMPLE

⚡ Tóm tắt thông minh

Hàm malloc() trong C dành riêng một khối bộ nhớ trên heap khi chạy chương trình và trả về một con trỏ void trỏ đến byte đầu tiên của khối bộ nhớ đó, cho phép các chương trình yêu cầu vùng nhớ có kích thước chưa được biết cho đến khi mã thực sự được thực thi.

  • 🧠 Phân bổ thời gian chạy: Hàm malloc() dành riêng bộ nhớ trong quá trình thực thi khi kích thước cần thiết chưa được biết tại thời điểm biên dịch.
  • 📦 Cú pháp: Con trỏ cast_type nhận malloc(byte_size), thường được định kích thước bằng sizeof để đảm bảo tính tương thích đa nền tảng.
  • ⚠️ Kiểm tra NULL: Yêu cầu thất bại sẽ trả về con trỏ NULL, vì vậy hãy xác thực kết quả trước khi truy cập vào nó.
  • Phép toán con trỏ: Khối này liền kề, vì vậy hãy sử dụng + thay vì ++ để truy cập các phần tử mảng.
  • 🧹 Giải phóng bộ nhớ: Giải phóng mọi khối bằng hàm free() để ngăn ngừa rò rỉ bộ nhớ và con trỏ lơ lửng.
  • 🤖 Hỗ trợ AI: Các trợ lý AI và GitHub Copilot soạn thảo các lệnh gọi malloc() và báo lỗi thiếu câu lệnh free().

Hàm malloc() trong C

malloc trong C là gì?

Hàm malloc() là viết tắt của việc 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 malloc cho bất kỳ con trỏ nào.

malloc trong C là gì

cú pháp

ptr = (cast_type *) malloc (byte_size);

Ở đây,

  • ptr là con trỏ của cast_type.
  • Hàm malloc trả về một con trỏ tới bộ nhớ được phân bổ của byte_size.
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 về triển khai malloc:

#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 vùng nhớ được cấp phát là liền kề và có thể được coi như một mảng. Chúng ta có thể sử dụng con trỏ Sử dụng phép toán số học để truy cập các phần tử mảng thay vì dùng dấu ngoặc vuông [ ]. Chúng tôi khuyên bạn nên dùng dấu + để tham chiếu đến các phần tử mảng vì việc sử dụng phép tăng ++ 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.

Tại sao nên sử dụng malloc() trong C?

Mảng tĩnh và các biến thông thường cố định kích thước của chúng khi chương trình được biên dịch, điều này gây lãng phí không gian khi yêu cầu thực tế nhỏ hơn và dẫn đến lỗi hoàn toàn khi yêu cầu lớn hơn. Hàm malloc() giải quyết vấn đề này bằng cách yêu cầu bộ nhớ trong thời gian chạy, do đó chương trình có thể định kích thước bộ đệm chính xác bằng lượng dữ liệu mà nó nhận được. Tính linh hoạt trong thời gian chạy này là cốt lõi của malloc(). cấp phát bộ nhớ động trong C.

Cấp phát bộ nhớ động bằng hàm malloc() là lựa chọn phù hợp trong một số trường hợp phổ biến:

  • Lượng dữ liệu đầu vào chưa được biết cho đến khi chương trình chạy, ví dụ như đọc một tập tin hoặc một danh sách do người dùng cung cấp.
  • Cần những khối dữ liệu lớn đến mức vượt quá giới hạn của ngăn xếp, vì hàm malloc() lấy dữ liệu từ vùng nhớ heap lớn hơn nhiều.
  • Dữ liệu phải tồn tại lâu hơn hàm đã tạo ra nó, bởi vì bộ nhớ heap vẫn tồn tại cho đến khi được giải phóng một cách rõ ràng.
  • Cấu trúc và mảng cần phải mở rộng hoặc thu hẹp trong quá trình thực thi chương trình.

Vì khối dữ liệu dành riêng được trả về dưới dạng con trỏ, nên nó có thể được truyền giữa các hàm một cách hiệu quả mà không cần sao chép dữ liệu cơ bản.

Cách giải phóng bộ nhớ được cấp phát bởi malloc()

Vùng nhớ được cấp phát bởi hàm malloc() nằm trên heap và không bao giờ được giải phóng tự động. Khi một chương trình liên tục cấp phát mà không giải phóng, nó sẽ dần dần tiêu thụ hết bộ nhớ khả dụng, một lỗi được gọi là rò rỉ bộ nhớ. Hàm free() trả về một khối bộ nhớ đã được giải phóng cho hệ thống để không gian đó có thể được sử dụng lại.

Hàm free() nhận một đối số duy nhất, là con trỏ mà hàm malloc() trả về, và giải phóng toàn bộ khối bộ nhớ cùng một lúc:

int *ptr;
ptr = malloc(10 * sizeof(int));
if (ptr != NULL) {
    /* use the allocated memory here */
    free(ptr);   /* release the block */
    ptr = NULL;  /* avoid a dangling pointer */
}

Hãy tuân thủ các quy tắc này để quản lý bộ nhớ động một cách an toàn:

  • Hãy gọi hàm free() chính xác một lần cho mỗi lần cấp phát bộ nhớ thành công bằng hàm malloc(); việc giải phóng cùng một khối bộ nhớ hai lần sẽ gây ra hành vi không xác định.
  • Hãy đặt con trỏ về NULL sau khi giải phóng để nó không thể được sử dụng lại như một con trỏ lơ lửng.
  • Không bao giờ được giải phóng (free()) một con trỏ mà hàm malloc() không trả về giá trị, kể cả con trỏ được dịch chuyển bằng phép toán con trỏ.
  • Hãy khớp mỗi lần cấp phát bộ nhớ với một lần giải phóng tương ứng trước khi con trỏ ra khỏi phạm vi.

Keeping Phân bổ và giải phóng bộ nhớ một cách cân bằng là nguyên tắc cốt lõi của quản lý bộ nhớ thủ công trong C, và nó ngăn ngừa cả rò rỉ bộ nhớ lẫn sự cố do việc tái sử dụng bộ nhớ đã được giải phóng gây ra.

malloc() và calloc(): Những điểm khác biệt chính

malloc() và calloc () Cả hai hàm đều dành riêng bộ nhớ heap, nhưng chúng khác nhau về các tham số và cách xử lý khối bộ nhớ mới. Hàm malloc() nhận một tham số là tổng số byte và để nội dung khối bộ nhớ chưa được khởi tạo, do đó khối bộ nhớ chứa các giá trị rác không thể dự đoán được cho đến khi chương trình ghi vào đó. Hàm calloc() nhận hai tham số là số lượng phần tử và kích thước của mỗi phần tử, và xóa toàn bộ byte về 0 trước khi trả về con trỏ.

Những khác biệt thực tế nằm ở ba điểm sau:

  • Khởi tạo: calloc() xóa toàn bộ vùng nhớ về 0, trong khi malloc() thì không, điều này khiến malloc() nhanh hơn một chút khi các giá trị sẽ bị ghi đè sau đó.
  • Lập luận: bạn viết malloc(n * size) nhưng calloc(n, size) để dành riêng cùng một mảng.
  • Trường hợp sử dụng: chọn calloc() khi cần một khối bộ nhớ sạch, được ghi toàn số 0, và chọn malloc() khi tốc độ được ưu tiên và việc khởi tạo được thực hiện thủ công.

Cả hai hàm đều được khai báo trong stdlib.h, cả hai đều trả về con trỏ void hoặc NULL khi thất bại, và cả hai đều trả lại bộ nhớ bằng cùng một hàm free().

Câu Hỏi Thường Gặp

Trong C, phép ép kiểu là tùy chọn và thường không được khuyến khích, bởi vì void* tự động chuyển đổi thành bất kỳ con trỏ đối tượng nào và việc ép kiểu tường minh có thể che giấu việc thiếu include stdlib.h. C++Tuy nhiên, cần phải có dàn diễn viên.

Giá trị trả về NULL có nghĩa là yêu cầu đã thất bại, thường là do không đủ bộ nhớ. Luôn kiểm tra con trỏ trước khi sử dụng nó, vì việc truy cập con trỏ NULL dẫn đến hành vi không xác định và thường gây lỗi chương trình.

Hàm realloc() thay đổi kích thước một khối bộ nhớ đã được dành riêng trước đó bởi hàm malloc(), giữ nguyên kích thước đó.ping dữ liệu hiện có có kích thước nhỏ hơn. Nếu thất bại, nó sẽ trả về NULL và giữ nguyên khối dữ liệu ban đầu, vì vậy hãy gán kết quả cho một con trỏ tạm thời trước.

Lỗi rò rỉ bộ nhớ xảy ra khi vùng nhớ heap được cấp phát từ hàm malloc() không bao giờ được giải phóng bằng hàm free(). Chương trình tiếp tục giữ lại không gian bộ nhớ mà nó không còn sử dụng, điều này có thể dần dần làm cạn kiệt bộ nhớ khả dụng.

Hàm malloc() cấp phát bộ nhớ trên heap, chứ không phải trên stack. Các khối bộ nhớ trên heap vẫn hợp lệ cho đến khi bạn gọi hàm free(), vì vậy chúng có thể tồn tại lâu hơn hàm đã tạo ra chúng, không giống như các biến tự động trên stack sẽ biến mất khi hàm trả về.

malloc() là một hàm thư viện trả về void* và cần sizeof để tính toán số byte. C++ `new` là một toán tử trả về con trỏ có kiểu dữ liệu xác định và gọi các hàm tạo. Hãy ghép `malloc()` với `free()`, và `new` với `delete`.

Đúng vậy. Các trợ lý lập trình AI soạn thảo các lệnh gọi malloc(), chèn các kiểm tra NULL và báo lỗi thiếu lệnh gọi free() hoặc rò rỉ bộ nhớ, và chúng có thể giải thích các phép toán con trỏ. Luôn luôn biên dịch và kiểm tra mã được tạo ra trước khi tin tưởng vào nó.

Trợ lý GitHub Gợi ý sử dụng các dòng mã malloc(), sizeof() và free() tương ứng từ một bình luận mô tả ý định của bạn. RevHãy xem xét kỹ từng gợi ý về việc thiếu kiểm tra giá trị NULL và lỗi rò rỉ bộ nhớ trước khi biên dịch.

Tóm tắt bài viết này với: