C Cấp phát bộ nhớ
Quá trình dự trữ bộ nhớ được gọi là phân bổ. Cách cấp phát bộ nhớ phụ thuộc vào loại bộ nhớ.
C có hai loại bộ nhớ: Bộ nhớ tĩnh và bộ nhớ động.
Bộ nhớ tĩnh
Bộ nhớ tĩnh là bộ nhớ dành riêng cho các biến trước khi chương trình chạy. Phân bổ bộ nhớ tĩnh còn được gọi là phân bổ bộ nhớ thời gian biên dịch .
C tự động cấp phát bộ nhớ cho mọi biến khi chương trình được biên dịch.
Ví dụ: nếu bạn tạo một mảng số nguyên gồm 20 sinh viên (ví dụ cho một học kỳ mùa hè), C sẽ dành không gian cho 20 phần tử thường có dung lượng 80 byte bộ nhớ (20 * 4):
Nhưng khi học kỳ bắt đầu, hóa ra chỉ có 12 sinh viên theo học. Khi đó bạn đã lãng phí không gian của 8 phần tử không được sử dụng.
Vì bạn không thể thay đổi kích thước của mảng nên bạn sẽ có bộ nhớ dành riêng không cần thiết.
Lưu ý rằng chương trình sẽ vẫn chạy và không bị hỏng hóc gì. Nhưng nếu chương trình của bạn chứa nhiều loại mã này, nó có thể chạy chậm hơn mức tối ưu.
Nếu bạn muốn kiểm soát tốt hơn bộ nhớ được phân bổ, hãy xem Bộ nhớ động bên dưới.
Bộ nhớ động
Bộ nhớ động là bộ nhớ được cấp phát sau khi chương trình bắt đầu chạy. Phân bổ bộ nhớ động cũng có thể được gọi là phân bổ bộ nhớ thời gian chạy .
Không giống như bộ nhớ tĩnh, bạn có toàn quyền kiểm soát lượng bộ nhớ đang được sử dụng bất cứ lúc nào. Bạn có thể viết mã để xác định dung lượng bộ nhớ bạn cần và phân bổ nó.
Bộ nhớ động không thuộc về một biến, nó chỉ có thể được truy cập bằng con trỏ.
Để cấp phát bộ nhớ động, bạn có thể sử dụng hàm malloc()
hoặc calloc()
. Cần phải bao gồm tiêu đề <stdlib.h>
để sử dụng chúng. Các hàm malloc()
và calloc()
phân bổ một số bộ nhớ và trả về một con trỏ tới địa chỉ của nó.
int *ptr1 = malloc( size );
int *ptr2 = calloc( amount , size );
Hàm malloc()
có một tham số, size , xác định lượng bộ nhớ cần phân bổ, được đo bằng byte.
Hàm calloc()
có hai tham số:
- money - Chỉ định số lượng mục cần phân bổ
- size - Chỉ định kích thước của từng mục được đo bằng byte
Lưu ý: Dữ liệu trong bộ nhớ được cấp phát bởi malloc()
là không thể đoán trước được. Để tránh các giá trị không mong muốn, hãy đảm bảo ghi nội dung nào đó vào bộ nhớ trước khi đọc.
Không giống như malloc()
, hàm calloc()
ghi các số 0 vào tất cả bộ nhớ được phân bổ. Tuy nhiên, điều này làm cho calloc()
kém hiệu quả hơn một chút.
Cách tốt nhất để phân bổ lượng bộ nhớ phù hợp cho loại dữ liệu là sử dụng toán tử sizeof
:
int *ptr1, *ptr2;
ptr1 = malloc(sizeof(*ptr1));
ptr2 = calloc(1, sizeof(*ptr2));
Hãy cẩn thận sizeof(*ptr1)
yêu cầu C đo kích thước của dữ liệu tại địa chỉ. Nếu bạn quên *
và viết sizeof(ptr1)
thay vào đó, nó sẽ đo kích thước của chính con trỏ, tức là (thường) 8 byte cần thiết để lưu trữ địa chỉ bộ nhớ.
Lưu ý: Toán tử sizeof
không thể đo được lượng bộ nhớ động được phân bổ. Khi đo bộ nhớ động, nó chỉ cho bạn biết kích thước kiểu dữ liệu của bộ nhớ. Ví dụ: nếu bạn dành không gian cho 5 giá trị float
, toán tử sizeof
sẽ trả về 4, là số byte cần thiết cho một giá trị float
.
Hãy sử dụng bộ nhớ động để cải thiện ví dụ của học sinh ở trên.
Như đã lưu ý trước đây, chúng ta không thể sử dụng sizeof
để đo lượng bộ nhớ được phân bổ, chúng ta phải tính toán điều đó bằng cách nhân số lượng mục với kích thước của kiểu dữ liệu:
Ví dụ
int *students;
int numStudents = 12;
students = calloc(numStudents,
sizeof(*students));
printf("%d", numStudents * sizeof(*students)); // 48
bytes
Hãy tự mình thử »Ghi chú
Khi làm việc với cấp phát bộ nhớ động, bạn cũng nên kiểm tra lỗi và bộ nhớ trống ở cuối chương trình. Bạn sẽ tìm hiểu thêm về điều này trong các chương tiếp theo.
Bộ nhớ ngăn xếp
Để hoàn thiện, điều đáng nói đến là bộ nhớ ngăn xếp. Bộ nhớ ngăn xếp là một loại bộ nhớ động được dành riêng cho các biến được khai báo bên trong hàm. Các biến được khai báo bên trong hàm sử dụng bộ nhớ ngăn xếp thay vì bộ nhớ tĩnh.
Khi một hàm được gọi, bộ nhớ ngăn xếp được phân bổ cho các biến trong hàm. Khi hàm trả về bộ nhớ ngăn xếp được giải phóng.
Bạn nên biết về bộ nhớ ngăn xếp để có thể xử lý việc sử dụng bộ nhớ của các lệnh gọi hàm lồng nhau và đệ quy. Đệ quy lặp lại quá nhiều lần có thể chiếm quá nhiều bộ nhớ ngăn xếp. Khi điều đó xảy ra, nó được gọi là tràn ngăn xếp .