Thursday, November 3, 2016

Stack và Heap

1. Stack
- Cấp phát(allocate) vùng nhớ cho biến nhanh hơn trên heap.
- Các biến được khởi tạo trên stack sẽ tự hủy(deallocate) khi ra khỏi scope.
- Hoạt động theo nguyên tắc FILO(First In Last Out).
- Lưu trữ các biến local, địa chỉ trả về của hàm(return address) lúc hàm được thực thi, các tham số truyền vào hàm(parameter).
- Sử dụng quá nhiều sẽ bị tràn stack(stack overflow), thường do đệ quy, vòng lặp vô tận, hoặc dữ liệu có kích thước lớn.
- Dữ liệu trên stack có thể sử dụng trực tiếp không thông qua con trỏ(pointer).
- Thường được cấp phát kích thước tối đa khi chương trình bắt đầu chạy.
- Sử dụng stack phải biết chính xác kích thước vùng nhớ cần cấp phát lúc runtime.
- Trong các chương trình multi-thread, mỗi child-thread sinh ra sẽ có stack riêng.

2. Heap
- Cấp phát(allocate) vùng nhớ chậm hơn stack.
- Chứa dữ liệu được cấp phát bằng cách gọi hàm new, allocate. Không tự hủy(deallocate) khi ra khỏi scope, phải hủy bằng hàm delete hoặc free.
- Có thể không cấp phát được nếu chương trình yêu cầu cấp phát vùng nhớ quá lớn và heap không đủ vùng nhớ để cấp phát.
- Vùng nhớ heap bị phân mảnh(fragmentation) khi cấp phát và hủy nhiều lần.
- Có thể truy cập dữ liệu thông qua con trỏ(pointer).
- Có thể bị leak memory.
- Sử dụng heap có thể cấp phát động kích thước lúc run-time mà không cần biết trước.

Sunday, April 24, 2016

New, Malloc và Calloc để tạo mảng động

* new, malloc có thể được dùng với những công dụng khác, trong bài này chỉ nói đến việc sử dụng để tạo mảng động.

1. Malloc

void* malloc(size_t size);
malloc là phương thức để xin cấp phát một vùng nhớ trên heap. Hàm malloc nhận vào kích thước của vùng nhớ cần cấp phát(tính theo byte) và trả về con trỏ void.
Vì thế để tạo mảng động với malloc ta phải tính ra kích thước vùng nhớ của mảng cần tạo. Sử dụng sizeof để lấy kích thước kiểu dữ liệu của phần tử trong mảng rồi nhân với độ dài mảng.

int* arr = (int*)malloc(sizeof(int) * 10);
Trong đoạn code trên, ta tạo mảng có độ dài 10 phần tử. Hàm malloc trả về con trỏ void nên ta phải ép kiểu(cast) về kiểu dữ liệu của phần tử trong mảng. Trường hợp không còn bộ nhớ hợp lệ để cập phát, hàm malloc sẽ trả về 0. Trường hợp tạo thành công hàm trả về con trỏ trỏ đến vùng nhớ mới được cấp phát, và trong vung nhớ này chứa toàn giá trị rác(junk memory).
2. Calloc

void* calloc(size_t num, size_t size);
Hàm calloc để xin cấp phát một vùng nhớ và reset vùng nhớ bằng giá trị 0. Tham số num là số phần tử của mảng, size là kích thước kiểu dữ liệu của mỗi phần tử.
Cũng như hàm malloc, hàm calloc cũng xin cấp phát một vùng nhớ trên heap. Nhưng trong hầu hết các trường hợp, sau khi tao xong vùng nhớ ta đều gán một giá trị khởi tạo cho vùng nhớ đó thường là giá trị 0, để tránh vùng nhớ chứa giá trị rác. Hàm calloc tương đương với khởi tạo vùng nhớ malloc rồi reset vùng nhớ bằng memset
3. New
Operator new cũng được dùng để khởi tạo mảng động. Khác với malloc, toán tử new tự động tính kích thước kiểu dữ liệu của phần tử, và trả về con trỏ đã được ép kiểu. Hàm malloc sau khi cấp phát vùng nhớ xong, vùng nhớ vẫn chứa giá trị rác, nhưng với toán tử new, sau khi cấp phát mỗi phần tử sẽ được gọi constructor mặc định của nó.
4. Xóa mảng
Mảng động được tạo ra nhưng không được tự động hủy(Hệ điều hành thu hồi lại) nên ta phải gọi hàm hủy.
Nếu dùng malloc hay calloc để cấp phát thì hủy bằng hàm free, nếu dùng new để cấp phát thì dùng delete[].
+ Hàm free nhận vào con trỏ void, và k0 cần ép kiểu hay truyền vào kich thước. Hàm free chỉ trả lại vùng nhớ nhưng con trỏ vẫn còn chì đến vị trí vùng nhớ đó. Con trỏ này được gọi stray pointer, tức là con trỏ trỏ đế vùng nhớ rác, vì thế sau khi goi free thì nên gán con trỏ về 0.
+ Toán tử delete[] để xóa mảng và gọi destructor của mổi phần tử trước khi thực thi. Lưu ý: phải có dấu [] nếu không chỉ có phần tử đầu tiên được xóa và bị leak memory. Sử dụng delete[] để xóa cũng nên gán con trỏ về lai 0.

int* arr1 = (int*)malloc(sizeof(int) * 10);
int* arr2 = new int[10];
---
free(arr1);
arr1 = 0;
delete[] arr2;
arr2 = 0;
* Việc sử dụng malloc hay new để tạo mảng tùy thuộc vào người dùng. Hàm malloc cấp phát bộ nhớ nhanh, toán tử new cấp phát chậm hơn nhưng an toàn(đảm bảo các phần tử đều được khởi tạo bằng default constructor).