Xin chào, và chào mừng trở lại. Trong bài giảng này, tôi muốn nói về cấp phát bộ nhớ động.
Điều này liên quan trực tiếp đến con trỏ, bạn chỉ có thể sử dụng điều
này trên con trỏ. Vậy hãy bắt đầu.
Vì vậy, bất cứ khi nào bạn xác định
một biến trong c, trình biên dịch sẽ tự động phân bổ dung lượng lưu trữ chính xác cho bạn dựa trên kiểu dữ liệu.
Chúng tôi đã thấy điều này trong suốt. Khi chúng ta tạo các số nguyên, float, double, chúng ta không phải cấp phát bất kỳ bộ nhớ nào.
Trình biên dịch làm điều đó cho bạn. Nó thực sự phân bổ một số bộ nhớ.
Tương tự như vậy đối với con trỏ bằng cách này. Khi bạn tạo một con trỏ, bộ nhớ sẽ
được cấp phát cho bạn để nó lưu trữ một loại con trỏ, một địa chỉ.
Bây giờ điều đó không có nghĩa là bộ nhớ được cấp phát cho địa chỉ đó.
Cho đến thời điểm này, bất cứ khi nào chúng tôi gán dữ liệu cho một
- cho một con trỏ, chúng tôi luôn gán một biến hiện có.
Vì vậy, nếu chúng ta muốn gán dữ
liệu cho một con trỏ, chúng ta đã sử dụng ký hiệu và địa chỉ của toán
tử trên một biến mà biến đó đã được cấp phát bộ nhớ cho nó.
Nhưng điều gì sẽ xảy ra nếu bạn muốn tạo một con trỏ và bạn không muốn gán cho nó địa
chỉ của toán tử, bạn muốn cấp phát động bộ nhớ. Đó là nội dung của toàn bộ bài giảng này.
Vì vậy, bạn thường mong muốn có thể tự động phân bổ dung lượng lưu trữ
trong khi chương trình đang chạy. Phân bổ bộ nhớ khi bạn cần.
Nếu bạn có một chương trình được thiết kế để đọc trong một tập hợp dữ liệu từ
một tệp vào một mảng trong bộ nhớ, bạn có ba lựa chọn.
Bạn có thể xác định mảng để chứa số phần tử tối đa có thể có thời gian biên dịch,
đó là một kích thước cố định.
Nếu bạn xác định mảng có 50 phần tử, nó
sẽ chỉ chứa 50 phần tử. Trình biên dịch xác định rằng nó là 50,
nó sẽ cấp phát nhiều bộ nhớ đó khi nó chạy.
Bạn có thể sử dụng một mảng có độ dài thay đổi thành kích thước bằng kích thước của thời gian chạy mảng.
Chúng ta đã nói một chút về các mảng có độ dài thay
đổi này, chúng không được hỗ trợ cho đến C11. Họ thực sự đang loại bỏ C11, họ đang thêm C99.
Đây là nơi bạn có thể đặt các biến.
Nhưng đây vẫn là loại có kích thước cố định bởi vì tất cả điều
đó có nghĩa là các mảng được liên kết biến, tất cả điều đó có nghĩa là bạn có thể chuyển vào các biến cho kích thước.
Nhưng chúng vẫn tĩnh.
Hoặc lựa chọn tốt nhất là bạn có thể phân bổ mảng động khi bạn cần.
Và có các hàm c khác nhau mà bạn có thể gọi để thực hiện việc này.
Vì vậy, nó sẽ rất hữu ích để có thể phân bổ bộ nhớ động.
Vì vậy, với cách tiếp cận đầu tiên mà chúng tôi đã làm từ trước đến nay cho dù
đó là mảng hay kiểu dữ liệu, thì đó là một kích thước cố định.
Bạn phải xác định mảng của mình để chứa số phần tử tối đa
sẽ nằm ngay trong một mảng.
Vì vậy, bạn sẽ làm một cái gì đó như thế này, int dataArray [1000].
Mặc dù vậy, điều này có nghĩa là tệp dữ liệu không thể chứa nhiều hơn một nghìn phần tử.
Nếu đúng như vậy, chương trình của bạn sẽ bị hỏng.
Vì vậy, bạn chỉ cần hy vọng rằng bất kỳ tệp nào bạn đang đọc không có nhiều dữ liệu.
Chương trình của bạn sẽ không hoạt động với các bộ tệp lớn hoặc tệp lớn.
Và nếu bạn muốn làm việc với các tệp lớn, bạn phải quay lại và bạn phải làm cho mảng lớn hơn.
Bạn phải thay đổi kích thước, nhưng sau
đó bạn phải biên dịch lại nó trên tất cả những thứ đó, đó là thời gian biên dịch.
Bất kể bạn chọn giá trị nào, bạn luôn có cơ hội gặp
lại vấn đề tương tự trong tương lai vì đó là kích thước cố định.
Kích thước này được xác định khi bạn biên dịch
mã, nó không điều chỉnh khi bạn chạy chương trình.
Nó không tự động thay đổi.
Sử dụng các chức năng phân bổ bộ nhớ động, bạn có thể nhận được bộ nhớ khi cần.
Cách tiếp cận này cho phép bạn cấp phát bộ nhớ khi chương trình đang thực thi.
Vì vậy, hãy nghĩ về những lợi thế của điều này. Trước hết, bạn không tạo
ra nhiều bộ nhớ mà bạn có thể không
sử dụng, bạn sẽ chỉ tạo ra nhiều khi bạn cần.
Và nếu bạn cần nhiều hơn, bạn có thể phân bổ nhiều hơn.
Bạn không cần phải quay lại và biên dịch lại và thay đổi kích thước.
Vì vậy, phân bổ bộ nhớ động là cách để đi.
Nó phụ thuộc vào khái niệm về con trỏ và nó
cung cấp một động lực mạnh mẽ để sử dụng con trỏ trong mã của bạn.
Vì vậy, chúng ta đã nói về những ưu điểm của con
trỏ và bạn có thể tự nghĩ rằng, được rồi, chúng là một khái niệm tuyệt vời nhưng tại sao
chúng ta cần sử dụng chúng, đây
là một trong những lý do lớn, để bạn có thể phân bổ bộ nhớ một cách linh hoạt.
Và một lần nữa, đây là gì - C là ngôn ngữ hiệu quả và
không sử dụng nhiều bộ nhớ.
Vì vậy, c chạy trên hệ thống nhúng, lập trình thời gian thực và đây
là một trong những lý do tại sao vì bạn có thể giới hạn dung lượng bộ
nhớ sử dụng dựa trên nhu cầu.
Vì vậy cấp phát bộ nhớ động cho phép bộ nhớ lưu trữ dữ liệu được cấp phát động
khi chương trình thực thi.
Phân bổ bộ nhớ động chỉ có thể thực hiện được
vì bạn có sẵn các con trỏ.
Bởi vì bạn có một địa chỉ và bạn
có thể thay đổi mức độ con trỏ trỏ đến, bạn có thể điều chỉnh lại bộ nhớ.
Phần lớn các chương trình sản xuất sẽ sử dụng cấp phát bộ nhớ động.
Nếu bạn đang viết mã bằng c, bạn sẽ sử dụng cái này.
Ngay cả khi bạn là người mới bắt đầu, bạn sẽ
sử dụng phân bổ bộ nhớ động. Hầu hết các chương trình hữu ích sẽ sử dụng khái niệm này.
Phân bổ dữ liệu động cho phép bạn tạo các con trỏ trong thời gian chạy
vừa đủ lớn để chứa lượng dữ liệu bạn yêu cầu cho tác vụ, do
đó bạn không lãng phí bộ nhớ. Thiết yếu.
Chúng ta cũng phải nói về đống so với ngăn xếp.
Có hai cách để bạn có thể lưu trữ bộ nhớ trong một chương trình, hai cấu
trúc dữ liệu khác nhau, bạn có thể lưu trữ dữ liệu trên ngăn xếp hoặc bạn có thể lưu trữ
dữ liệu trên heap.
Cấp phát bộ nhớ động dự trữ mọi thứ trên heap.
Chúng tôi sẽ không đi vào chi tiết về cách các tổ chức này được tổ chức nhưng về cơ bản.
Khi bạn lưu trữ dữ liệu trên heap, nó cho phép thay
đổi nhiều hơn.
Bạn có thể tùy ý thay đổi kích thước của các đối tượng dữ liệu và nó tồn
tại lâu hơn rất nhiều khi bạn phân bổ bộ nhớ trong một đống,
nó sẽ bám quanh toàn bộ chương trình của bạn.
Khi bạn sử dụng ngăn xếp, nó sẽ hạn chế hơn.
Ngăn xếp được sử dụng cho mọi thứ từ đối số hàm và biến cục bộ.
Đó là nơi lưu trữ mọi thứ.
Và đó là lý do tại sao chúng ta đã nói về ngăn xếp cuộc gọi.
Nó đã xem xét các phương
thức và chức năng. Thỉnh thoảng tôi sử dụng các phương thức, nhưng các phương thức thực sự là một thuật ngữ
hướng đối tượng, vì vậy các hàm.
Mọi thứ liên quan đến chức năng đều được tạo trên ngăn xếp.
Vì vậy, điều đó có nghĩa là khi bạn thực thi
một hàm, nó sẽ được đưa vào ngăn xếp. Các biến cục bộ được đưa vào ngăn xếp.
Khi bạn thoát khỏi chức năng này, mọi thứ sẽ bị xóa.
Vì vậy, bộ nhớ nằm trên ngăn xếp không tồn tại lâu.
Đó là một trong những lý do khi bạn phân bổ động bộ nhớ,
nó nằm trên heap
vì bạn kiểm soát khi bạn sử dụng nó và khi bạn không sử dụng nó, bạn phân bổ nó và bạn xóa nó.
Thứ trên chồng, bạn không kiểm soát. Nó tự động bị
xóa, tự động được cấp phát.
Vì vậy, các biến cục bộ, các biến bên trong các hàm như hàm
chính, chúng được tạo trên ngăn xếp, v.v.
Bộ nhớ trên heap thì khác, nó do bạn, người lập trình kiểm soát.
Khi bạn cấp phát bộ nhớ trên heap, bạn có thể theo dõi khi nào bộ nhớ, bạn đã
cấp phát không còn cần thiết, vì vậy bạn phải xóa bộ nhớ.
Bạn phải giải phóng dung lượng
khi nó không còn được bạn sử dụng. Và điều này rất quan trọng
bởi vì trí nhớ là một nguồn tài nguyên rất khan hiếm.
Ngày nay chúng ta có rất nhiều bộ nhớ để bạn có thể chạy một máy tính, 12 biểu diễn,
16 biểu diễn bất kể đó là gì. Nhưng đối với các chương
trình được nhúng chạy trên thiết bị, bạn không nhất thiết
phải có nhiều bộ nhớ như vậy. Và vì vậy bạn muốn kiểm soát lượng bộ
nhớ sử dụng và bạn cũng muốn giải phóng bộ nhớ khi sử dụng xong để các
ứng dụng khác có thể sử dụng.
Một lần nữa, quan trọng hơn trong các hệ thống kiểu nhúng cấp thấp.
Nhưng đây là một quyền tự do mà nhiều ngôn ngữ không có được. Khả năng này để lập trình
viên kiểm soát bộ nhớ người quản lý của bạn và nó có
thể làm cho chương trình hiệu quả hơn và nhanh hơn nhiều.
Vì vậy, trong bài giảng tiếp theo, chúng ta sẽ nói về cách bạn thực sự có
thể làm điều này, những chức năng nào bạn có thể gọi. Rõ ràng, có những chức năng khác nhau mà bạn có
thể gọi để cấp phát bộ nhớ vào bộ nhớ trống. Cảm ơn bạn.