Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Khi bạn khai báo biến, dữ liệu phải nằm ở đâu đó trong RAM. Thật ra chương trình dùng hai vùng bộ nhớ chính với tính cách rất khác nhau: stack và heap. Hiểu chúng giúp bạn giải thích được nhiều hiện tượng như đệ quy tràn, rò rỉ bộ nhớ hay vì sao sao chép đối tượng đôi khi khác sao chép số.
Stack là vùng nhớ lưu thông tin của các lần gọi hàm: tham số, biến cục bộ, địa chỉ quay về. Mỗi lần gọi hàm, một "khung" mới được đặt lên đỉnh; hàm kết thúc thì khung đó bị gỡ bỏ. Đặc điểm:
#include <stdio.h>
int binh_phuong(int x) {
int ket_qua = x * x; // biến cục bộ nằm trên stack
return ket_qua;
}
int main(void) {
int a = 5; // cũng nằm trên stack
printf("%d\n", binh_phuong(a));
return 0;
}Heap là vùng nhớ lớn hơn dùng để cấp phát linh hoạt trong lúc chạy, cho những dữ liệu có kích thước không biết trước hoặc cần tồn tại lâu hơn một lần gọi hàm. Đặc điểm:
#include <stdio.h>
#include <stdlib.h>
int *tao_mang(int n) {
int *p = malloc(n * sizeof(int)); // cấp phát trên heap
for (int i = 0; i < n; i++) {
p[i] = i * i;
}
return p; // vẫn tồn tại sau khi hàm kết thúc
}
int main(void) {
int *ds = tao_mang(5);
printf("%d\n", ds[4]); // 16
free(ds); // phải tự giải phóng
return 0;
}Con trỏ p nằm trên stack, còn mảng mà nó trỏ tới nằm trên heap. Nếu quên free, vùng nhớ đó bị rò rỉ cho đến khi chương trình kết thúc.
| Tiêu chí | Stack | Heap |
|---|---|---|
| Tốc độ | Rất nhanh | Chậm hơn |
| Quản lý | Tự động | Thủ công hoặc do bộ thu gom rác |
| Kích thước | Nhỏ, cố định | Lớn, linh hoạt |
| Thời gian sống | Hết hàm là mất | Đến khi được giải phóng |
| Lỗi điển hình | Stack overflow | Rò rỉ, truy cập vùng đã giải phóng |
Python, Java, JavaScript, C# đều có bộ thu gom rác (garbage collector) tự động giải phóng đối tượng không còn ai tham chiếu, nên bạn hiếm khi tự gọi free. Tuy nhiên mô hình vẫn tương tự: biến trỏ tới đối tượng nằm trên stack, còn bản thân đối tượng (danh sách, đối tượng của lớp) nằm trên heap. Đó là lý do:
a = [1, 2, 3]
b = a # b trỏ tới CÙNG danh sách trên heap
b.append(4)
print(a) # [1, 2, 3, 4]
c = a.copy() # tạo danh sách mới
c.append(5)
print(a) # [1, 2, 3, 4] không đổiGán b = a chỉ sao chép tham chiếu, không sao chép dữ liệu. Với số nguyên nhỏ thì cảm giác như sao chép giá trị, nhưng với đối tượng thì khác. Đây là nguồn gốc của vô số lỗi cho người mới.
Viết hàm đệ quy đếm ngược không có điều kiện dừng, chạy và quan sát RecursionError để hiểu giới hạn stack. Sau đó, trong C, thử tạo mảng lớn trên stack và trên heap bằng malloc để so sánh.
Stack gọn, nhanh, tự dọn; heap rộng, linh hoạt, cần quản lý. Biết biến của mình nằm ở đâu giúp bạn hiểu lỗi bộ nhớ và hành vi tham chiếu. Hãy thử vẽ sơ đồ stack và heap cho một đoạn code nhỏ, bạn sẽ thấy khái niệm trở nên rõ ràng.
Muốn học bài bản và có người hướng dẫn? Hãy xem các khóa học trên codebem.com để đi từ nền tảng đến dự án thực tế.
Nguyễn Đình Cường
0 comment