Chào các bạn.
Mình có thắc mắc là trong khi viết mã nguồn thực tế, rò rỉ bộ nhớ sẽ xảy ra khi nào và cách phòng tránh trường hợp đó vậy ạ? Làm ơn minh họa câu nói của bạn bằng đoạn mã nguồn thực tế nhé.
Xin cảm ơn các bạn.
Chào các bạn.
Mình có thắc mắc là trong khi viết mã nguồn thực tế, rò rỉ bộ nhớ sẽ xảy ra khi nào và cách phòng tránh trường hợp đó vậy ạ? Làm ơn minh họa câu nói của bạn bằng đoạn mã nguồn thực tế nhé.
Xin cảm ơn các bạn.
Về cơ bản, khi bạn cấp phát bộ nhớ cho con trỏ trong C/C++, thì bộ nhớ Heap sẽ đảm nhiệm việc này, mà một đặc điểm quan trọng của bộ nhớ Heap đó là đối tượng được cấp phát bởi bộ nhớ Heap không thể tự giải phóng sau khi kết thúc chương trình, do đó, một trong những lỗi phổ biến nhất tạo ra rò rỉ bộ nhớ trong C/C++ là bạn quên giải phóng vùng nhớ đã cấp phát đi, tức là nếu thể hiện bằng ngôn ngữ lập trình bạn quên thêm câu lệnh free
(trong C) hoặc delete
(trong C++) để giải phóng vùng nhớ.
Vậy một số trường hợp có thể xảy ra rò rỉ bộ nhớ khi viết mã nguồn là,
Quên giải phóng vùng nhớ sau khi cấp phát
Đây là một trong những lỗi rất rất phổ biến, bạn nên luôn nhớ rằng nếu có malloc/calloc/realloc
thì phải có free
, nếu có new
thì phải có delete
.
int *A = new int[10]; // Tạo 40 bytes vùng nhớ cho 10 giá trị kiểu số nguyên (1 int = 4 bytes)
delete [] A; // Giải phóng 40 bytes đã cấp phát, giả sử bạn quên câu lệnh này => rò rỉ bộ nhớ.
Có giải phóng vùng nhớ nhưng sai cú pháp
Bạn không nên nhầm lần giữa giải phóng chỉ 1 đối tượng và giải phóng một mảng các phần tử, sự khác biệt giữa chúng là dấu ngoặc vuông []
,
delete A; // Giải phóng chỉ một đối tượng A
delete []A; //Giải phóng cả một mảng A
Ví dụ trường hợp tạo nên rò rỉ bộ nhớ khi bạn quên không phân biệt giữa đối tượng và mảng,
// Trường hợp mảng
int *A = new int[10];
delete A; // Sai
delete []A; // Đúng, vì A là mảng chứa 10 phần tử
// Trường hợp 1 đối tượng
int *A = new int; // Chú ý dòng lệnh này, chỉ có 1 đối tượng A duy nhất
delete A; // Đúng
delete []A; // Có thể chấp nhận được, nhưng không rõ ràng, gây phân vân cho người đọc mã nguồn
Cấp phát lại vùng nhớ sau khi đã cấp phát
Hãy nhớ rằng bạn nên giải phóng vùng nhớ của một con trỏ trước khi bạn cấp phát lại vùng nhớ mới cho nó, ví dụ,
int *A = new int[10]; // Cấp phát vùng nhớ cho A chứa 10 số nguyên
delete []A; // Phải giải phóng A trước khi cấp phát lại vùng nhớ mới
A = new int[20]; // Cấp phát vùng nhớ mới cho A có khả năng chứa 20 số nguyên
delete []A; // Giải phóng sau khi sử dụng
Cấp phát vùng nhớ cho con trỏ cục bộ
Bạn không nên nhầm lẫn giữa biến cục bộ và con trỏ cục bộ, vùng nhớ cấp phát cho biến cục bộ nằm trong bộ nhớ Stack và tự động hủy sau khi hàm kết thúc, trong khi vùng nhớ của con trỏ cục bộ thì không, nó vẫn nằm đó trong bộ nhớ Heap mặc dù hàm của bạn đã thực hiện xong công việc.
int someFunction() {
int *A = new int[10]; // Khởi tạo mảng A nằm trong hàm someFunction
// .....
delete []A; // Nhớ phải giải phóng vùng nhớ sau khi sử dụng
}
Đó là tất cả những gì mình biết, hy vọng sẽ có ích.