Trong bài trước ta đã tìm hiểu cơ bản về heap Con trỏ và bộ nhớ [2] - Bộ nhớ heap hôm nay ta sẽ sử dụng nó và đưa ra một vài lưu ý khi sử dụng heap.
Trong ngôn ngữ C, thật thuận tiện để allocate một mảng trong array, bởi vì C có thể xử lý mọi pointer giống như một mảng. Kích cỡ của khối nhớ của mảng là kích cỡ của từng phần tử (được tính toán nhờ toán tử sizeof()) nhân với số phần tử. Vì vậy ta có code phân bổ một mảng gồm 100 phần tử có kiểu dữ liệu struct fraction trong heap và cho tất cả các phần tử là 22/7 và giải phóng mảng heap .
void heapArray(){
struct fraction* fracts;
// cấp phát cho mảng
fracts = malloc(sizeof(struct fraction) * 100);
// sử dụng nó như mảng - trong trường hợp này cho tất cả các giá trị là 22/7
for(int i = 0; i < 99; i++){
fracts[i].numerator = 22;
fracts[i].denominator = 7;
}
// Thu hồi bộ nhớ của mảng
free(fracts);
}
Ở đây là một ví dụ về heap hữu ích hơn mảng. Hàm StringCopy() lấy xâu ,và sao chép xâu đó vào heap, và trả cho ta pointer trỏ tới new string . Caller sẽ chiếm quyền sở hữu của new string và có trách nhiệm free nó.
/*
Lấy string C, trả về heap được cấp phát và đã copy string vào. Vấp phát khối nhớ trong heap với một kích cỡ thích hợp, sao chép string vào khối và trả về con trỏ trỏ vào khối đó. Caller chiểm quyền sở hữu của khối đó và có trách nhiệm free nó.
*/
char* StringCopy(const char* string) {
char* newString;
int len;
len = strlen(string) + 1; // +1 to account for the '\0'
newString = malloc(sizeof(char)*len); // elem-size * number-of-elements
assert(newString != NULL); // simplistic error check (a good habit)
strcpy(newString, string); // copy the passed in string to the block
return(newString); // return a ptr to the block
}
StringCopy() có lợi thế về cả hai tính năng của bộ nhớ heap :
#Size: StringCopy() xác định size , lúc chạy , kích thước xác định của khối cần được lưu trữ string trong nó , kích cỡ này được đặt trong lời gọi malloc(). Bộ nhớ local không thể làm được điều này bởi vì kích cỡ của nó cần được xác định trong thời gian biên dịch tức trước là trước lúc chạy. Lời gọi sizeof(char) thực sự không cần thiết, bởi vì kích cỡ của char là 1 đã được định nghĩa. Kích thước của mảng là size của một phần tử nhân với số phần tử.
#Vòng đời. StringCopy() cấp phát khối nhớ, nhưng sau đó nó chuyển quyền sở hữu của nó cho caller. Bởi vì không có một lời gọi đến free() nào, vì vậy khối nhớ tiếp tục tồn tại thậm chí sau khi chương trình kết thúc. Bộ nhớ local không thể làm như vậy. Caller cần quan tâm đến việc thu hồi khi nó kết thúc .
Điều gì xảy nếu như có một số vùng nhớ của heap được cấp phát, nhưng không bao giờ được thu hồi? Một chương trình quên việc thu hồi khối nhớ được nói là có "memory leak" có thể không hoặc cũng có thể có một vẫn đề rất nghiêm trọng. Kết quả sẽ là heap sẽ dần dần bị làm đầy khi tiếp tục có yêu cầu cấp phát, nhưng không có lệnh thu hồi để khối đó "re-use". Vơi một chương trình chạy, tính toán thứ gì đó , và kết thúc ngay tức thì, thì memory leaks thường không được quan tâm. Như một chương trình "một lần" nó bỏ qua mọi mọi lệnh thu hồi của nó nhưng vẫn làm việc. Memory leak có nhiều vẫn đề cho một chương trình nó chạy không xác định thời gian. Trong trường hợp đó, memory leak có thể dãn dần lấp đầy heap đến khi yêu cầu cấp phát không được đáp ứng và chương trình ngừng làm việc hoặc crashing. Có rất nhiều chương trình thương mại bị memory leak, có thể khi chạy đủ dài chũn sẽ lấp đầy heap và bị crash. Thông thường phát hiện lỗi và tránh code bị heap đầy không được kiểm nghiệm tốt, chính vì trường hợp heap đầy hiểm khi gặp với chương trình chạy ngắn nên nhiều chương trình chạy ngắn gây leak memory - đó là lý do tại sao heap đầy sẽ cho kết quả là crash thay vì một lời báo lỗi . Phần lớn trình biên dịch có "heap debugging" hữu ích để thêm debugging vào chương trính để kiểm soát mọi lời cấp phát và thu hồi. Khi một cấp phát không có thu hồi, nguy cơ leak, heap debugger có thể giúp bạn tìm nó.
StringCopy() cấp phát khối nhớ heap, nhưng nó không thu hồi nó. Đây là để caller có thể sử dụng string mới. Tuy nhiên, điều này trình bày cho ta một lỗi mà một vài người cần phải nhwor thu hồi lại khối nhớ , và nó sẽ không phải là StringCopy(). Đó là lý do tại sao comment của StringCopy() đề cập cụ thể là caller sẽ chiếm quyền sở hữu của khối . Mọi khối nhớ có chính xác một "chủ nhân" , có trách nhiệm giải phóng nó. Trên phương diện khác thì có thể có con trỏ, nhưng nó chỉ là "sharing". Nó cũng là một chủ sở hữu và comment cho StringCopy() làm cho nó rõ ràng là quyền sở hữu được truyền từ StringCopy() đến caller. Một tài liệu hay luôn nhớ bàn luận đến điều luật về quyền sở hữu. Bằng cách này hay cách khác luôn các tài liệu phải luôn bàn luận đến quyền sở hữu cho tham số hoặc giá trị trả về. Nó là một con đường mà lỗi bộ nhớ hoặc leak sẽ bị tạo ra.
# Caller sở hữu. caller tự nó sở hữu bộ nhớ của nó. Nó có thể thông qua con trỏ tới callee cho mục đich "sharing", nhưng caller giữ lại quyền sở hữu. Callee có thể truy cấp những thứ đó khi nó chạy, và có thể cấp phát và thu hồi bộ nhớ của nó, nhưng nó sẽ không làm gián đoạn gì đến bộ nhớ của caller.
# Callee cấp phát và trả về. Callee cấp phát một vài bộ nhớ và trả nó về cho caller. Điều này xảy ra bởi vì kết quả của hoạt động của callee cần đến vùng nhớ mới để lưu trữ hoặc miêu tả. Vung nhớ mới này được chuyển qua cho caller vì vậy chúng có thể nhìn thấy kết quả và caller có thê chiểm quyền sở hữu với khối nhớ đó. Đây là kiểu mẫu được miêu tả trong StringCopy().
Heap memory cung cấp một sự điều khiển tuyệt vời cho các lập trình viên - những khối nhớ có thể được yêu cầu với một vài kích cỡ, và chúng tiếp tục được cấp phát cho đến khi nó được giải phóng (deallocated) một cách rõ ràng. Heap memory có thể thông qua lại caller bởi vì nó không bị thu hồi khi kết thúc và nó sử dụng cho cấu trúc liên kết như : linked list, binary trees, ... . Khuyến điểm của heap là chương trình phải làm công việc cấp phát và thu hồi một cách rõ ràng gọi đến trình quản lý heap. Heap không điều hành tự động cà tiện lợi như local memory làm.
Cảm ơn các bạn đã xem .
. Heap Array
Trong ngôn ngữ C, thật thuận tiện để allocate một mảng trong array, bởi vì C có thể xử lý mọi pointer giống như một mảng. Kích cỡ của khối nhớ của mảng là kích cỡ của từng phần tử (được tính toán nhờ toán tử sizeof()) nhân với số phần tử. Vì vậy ta có code phân bổ một mảng gồm 100 phần tử có kiểu dữ liệu struct fraction trong heap và cho tất cả các phần tử là 22/7 và giải phóng mảng heap .
void heapArray(){
struct fraction* fracts;
// cấp phát cho mảng
fracts = malloc(sizeof(struct fraction) * 100);
// sử dụng nó như mảng - trong trường hợp này cho tất cả các giá trị là 22/7
for(int i = 0; i < 99; i++){
fracts[i].numerator = 22;
fracts[i].denominator = 7;
}
// Thu hồi bộ nhớ của mảng
free(fracts);
}
. Ví dụ về string trong heap
Ở đây là một ví dụ về heap hữu ích hơn mảng. Hàm StringCopy() lấy xâu ,và sao chép xâu đó vào heap, và trả cho ta pointer trỏ tới new string . Caller sẽ chiếm quyền sở hữu của new string và có trách nhiệm free nó.
/*
Lấy string C, trả về heap được cấp phát và đã copy string vào. Vấp phát khối nhớ trong heap với một kích cỡ thích hợp, sao chép string vào khối và trả về con trỏ trỏ vào khối đó. Caller chiểm quyền sở hữu của khối đó và có trách nhiệm free nó.
*/
char* StringCopy(const char* string) {
char* newString;
int len;
len = strlen(string) + 1; // +1 to account for the '\0'
newString = malloc(sizeof(char)*len); // elem-size * number-of-elements
assert(newString != NULL); // simplistic error check (a good habit)
strcpy(newString, string); // copy the passed in string to the block
return(newString); // return a ptr to the block
}
. Chú ý về Heap string
StringCopy() có lợi thế về cả hai tính năng của bộ nhớ heap :
#Size: StringCopy() xác định size , lúc chạy , kích thước xác định của khối cần được lưu trữ string trong nó , kích cỡ này được đặt trong lời gọi malloc(). Bộ nhớ local không thể làm được điều này bởi vì kích cỡ của nó cần được xác định trong thời gian biên dịch tức trước là trước lúc chạy. Lời gọi sizeof(char) thực sự không cần thiết, bởi vì kích cỡ của char là 1 đã được định nghĩa. Kích thước của mảng là size của một phần tử nhân với số phần tử.
#Vòng đời. StringCopy() cấp phát khối nhớ, nhưng sau đó nó chuyển quyền sở hữu của nó cho caller. Bởi vì không có một lời gọi đến free() nào, vì vậy khối nhớ tiếp tục tồn tại thậm chí sau khi chương trình kết thúc. Bộ nhớ local không thể làm như vậy. Caller cần quan tâm đến việc thu hồi khi nó kết thúc .
.Memory Leaks
Điều gì xảy nếu như có một số vùng nhớ của heap được cấp phát, nhưng không bao giờ được thu hồi? Một chương trình quên việc thu hồi khối nhớ được nói là có "memory leak" có thể không hoặc cũng có thể có một vẫn đề rất nghiêm trọng. Kết quả sẽ là heap sẽ dần dần bị làm đầy khi tiếp tục có yêu cầu cấp phát, nhưng không có lệnh thu hồi để khối đó "re-use". Vơi một chương trình chạy, tính toán thứ gì đó , và kết thúc ngay tức thì, thì memory leaks thường không được quan tâm. Như một chương trình "một lần" nó bỏ qua mọi mọi lệnh thu hồi của nó nhưng vẫn làm việc. Memory leak có nhiều vẫn đề cho một chương trình nó chạy không xác định thời gian. Trong trường hợp đó, memory leak có thể dãn dần lấp đầy heap đến khi yêu cầu cấp phát không được đáp ứng và chương trình ngừng làm việc hoặc crashing. Có rất nhiều chương trình thương mại bị memory leak, có thể khi chạy đủ dài chũn sẽ lấp đầy heap và bị crash. Thông thường phát hiện lỗi và tránh code bị heap đầy không được kiểm nghiệm tốt, chính vì trường hợp heap đầy hiểm khi gặp với chương trình chạy ngắn nên nhiều chương trình chạy ngắn gây leak memory - đó là lý do tại sao heap đầy sẽ cho kết quả là crash thay vì một lời báo lỗi . Phần lớn trình biên dịch có "heap debugging" hữu ích để thêm debugging vào chương trính để kiểm soát mọi lời cấp phát và thu hồi. Khi một cấp phát không có thu hồi, nguy cơ leak, heap debugger có thể giúp bạn tìm nó.
.Ownership
StringCopy() cấp phát khối nhớ heap, nhưng nó không thu hồi nó. Đây là để caller có thể sử dụng string mới. Tuy nhiên, điều này trình bày cho ta một lỗi mà một vài người cần phải nhwor thu hồi lại khối nhớ , và nó sẽ không phải là StringCopy(). Đó là lý do tại sao comment của StringCopy() đề cập cụ thể là caller sẽ chiếm quyền sở hữu của khối . Mọi khối nhớ có chính xác một "chủ nhân" , có trách nhiệm giải phóng nó. Trên phương diện khác thì có thể có con trỏ, nhưng nó chỉ là "sharing". Nó cũng là một chủ sở hữu và comment cho StringCopy() làm cho nó rõ ràng là quyền sở hữu được truyền từ StringCopy() đến caller. Một tài liệu hay luôn nhớ bàn luận đến điều luật về quyền sở hữu. Bằng cách này hay cách khác luôn các tài liệu phải luôn bàn luận đến quyền sở hữu cho tham số hoặc giá trị trả về. Nó là một con đường mà lỗi bộ nhớ hoặc leak sẽ bị tạo ra.
.Mô hình ownership
Có hai mô hình chung cho việc sở hữu đó là :# Caller sở hữu. caller tự nó sở hữu bộ nhớ của nó. Nó có thể thông qua con trỏ tới callee cho mục đich "sharing", nhưng caller giữ lại quyền sở hữu. Callee có thể truy cấp những thứ đó khi nó chạy, và có thể cấp phát và thu hồi bộ nhớ của nó, nhưng nó sẽ không làm gián đoạn gì đến bộ nhớ của caller.
# Callee cấp phát và trả về. Callee cấp phát một vài bộ nhớ và trả nó về cho caller. Điều này xảy ra bởi vì kết quả của hoạt động của callee cần đến vùng nhớ mới để lưu trữ hoặc miêu tả. Vung nhớ mới này được chuyển qua cho caller vì vậy chúng có thể nhìn thấy kết quả và caller có thê chiểm quyền sở hữu với khối nhớ đó. Đây là kiểu mẫu được miêu tả trong StringCopy().
.Tóm lại.
Heap memory cung cấp một sự điều khiển tuyệt vời cho các lập trình viên - những khối nhớ có thể được yêu cầu với một vài kích cỡ, và chúng tiếp tục được cấp phát cho đến khi nó được giải phóng (deallocated) một cách rõ ràng. Heap memory có thể thông qua lại caller bởi vì nó không bị thu hồi khi kết thúc và nó sử dụng cho cấu trúc liên kết như : linked list, binary trees, ... . Khuyến điểm của heap là chương trình phải làm công việc cấp phát và thu hồi một cách rõ ràng gọi đến trình quản lý heap. Heap không điều hành tự động cà tiện lợi như local memory làm.
Cảm ơn các bạn đã xem .
Không có nhận xét nào:
Đăng nhận xét