Cấp phát động trong C++ (Dynamic memory allocation).

Khóa học lập trình C++ căn bản

5.0 (8 đánh giá)
Tạo bởi Kteam Cập nhật lần cuối 5 năm trước 118.634 lượt xem 6 bình luận
Tác giả/Dịch giả: Kteam
Học nhanh

Danh sách bài học

01. Giới thiệu về C++ 02. Cài đặt môi trường phát triển (IDE) Visual studio 2015 03. Xây dựng chương trình C++ đầu tiên với Visual Studio 2015 04. Cấu trúc một chương trình C++ (Structure of a program) 05. Ghi chú trong C++ (Comments in C++) 06. Biến trong C++ (Variables in C++) 07. Số tự nhiên và Số chấm động trong C++ (Integer, Floating point) 08. Kiểu ký tự trong C++ (Character) 09. Kiểu luận lý và cơ bản về Câu điều kiện If (Boolean and If statements) 10. Nhập, Xuất và Định dạng dữ liệu trong C++ (Input and Output) 11. Hằng số trong C++ (Constants) 12. Toán tử số học, toán tử tăng giảm, toán tử gán số học trong C++ (Operators) 13. Toán tử quan hệ, logic, bitwise, misc và độ ưu tiên toán tử trong C++ 14. Cơ bản về chuỗi ký tự trong C++ (An introduction to std::string) 15. Biến cục bộ trong C++ (Local variables in C++) 16. Biến toàn cục trong C++ (Global variables in C++) 17. Biến tĩnh trong C++ (Static variables in C++) 18. Ép kiểu ngầm định trong C++ (Implicit type conversion in C++) 19. Ép kiểu tường minh trong C++ (Explicit type conversion in C++) 20. Cơ bản về Hàm và Giá trị trả về (Basic of functions and return values) 21. Truyền Giá Trị cho Hàm (Passing Arguments by Value) 22. Truyền Tham Chiếu cho Hàm (Passing Arguments by Reference) 23. Tiền khai báo và Định nghĩa Hàm (Forward declarations and Definitions of Functions) 24. Giới thiệu về cấu trúc điều khiển (Control flow introduction) 25. Câu điều kiện If và Toán tử điều kiện (If statements and Conditional operator) 26. Câu điều kiện Switch trong C++ (Switch statements) 27. Câu lệnh Goto trong C++ (Goto statements) 28. Vòng lặp While trong C++ (While statements) 29. Vòng lặp Do while trong C++ (Do while statements) 30. Vòng lặp For trong C++ (For statements) 31. Từ khóa Break and continue trong C++ 32. Phát sinh số ngẫu nhiên trong C++ (Random number generation) 33. Mảng 1 chiều trong C++ (Arrays) 34. Các thao tác trên Mảng một chiều 35. Mảng 2 chiều trong C++ (Two-dimensional arrays) 36. Các thao tác trên Mảng 2 chiều 37. Mảng ký tự trong C++ (C-style strings) 38. Các thao tác trên Mảng ký tự (C-style strings) 39. Từ khóa auto trong C++11.(The auto keyword) 40. Vòng lặp for each trong C++11 (For each loops) 41. Lớp dựng sẵn Array trong C++11 42. Con trỏ cơ bản trong C++ 43. Con trỏ NULL trong C++ (NULL pointers) 44. Con trỏ và mảng trong C++ (Pointers and arrays) 45. Các phép toán trên Con trỏ và Chỉ mục mảng trong C++ (Pointers and arrays) 46. Cấp phát động trong C++ (Dynamic memory allocation). 47. Cấp phát mảng động (Dynamically allocating arrays) 48. Con trỏ & Hằng trong C++ 49. Biến tham chiếu trong C++.(Reference variables) 50. Con trỏ void (Void pointers) 51. Con trỏ trỏ đến con trỏ (Pointers to pointers) 52. Cơ bản về lớp Vector 53. Truyền địa chỉ cho hàm (Passing arguments by address) 54. Hàm trả về giá trị, tham chiếu và địa chỉ trong C++(value, reference, and address) 55. Hàm nội tuyến trong C++ (Inline functions) 56. Nạp chồng hàm trong C++ (Function overloading) 57. Hàm có đối số mặc định trong C++ (Default arguments) 58. Con trỏ hàm trong C++ (Function pointers) 59. Đệ quy trong C++ (Recursion) 60. Khuôn mẫu hàm trong C++ (Function templates)

Cấp phát động trong C++ (Dynamic memory allocation).

Dẫn nhập

Ở bài học trước, mình đã chia sẻ cho các bạn về sự liên quan giữa CÁC PHÉP TOÁN TRÊN CON TRỎ & CHỈ MỤC MẢNG TRONG C++. Kiến thức khá quan trọng về con trỏ và mảng mà bạn cần nắm.

Hôm nay, chúng ta sẽ cùng tìm hiểu về Cấp phát động trong C++ (Dynamic memory allocation).


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về:

Trong bài ta sẽ cùng tìm hiểu các vấn đề:

  • Cấp phát bộ nhớ trong C++
  • Cấp phát động trong C++

Cấp phát bộ nhớ trong C++

Ngôn ngữ C++ hỗ trợ ba loại cấp phát bộ nhớ cơ bản, hai loại trong số đó bạn đã được học ở những bài học trước:

  1. Cấp phát bộ nhớ tĩnh (Static memory allocation):
  • Xảy ra trên các biến tĩnh biến toàn cục.
  • Vùng nhớ của các loại biến này được cấp phát một lần khi chương trình bắt đầu chạy và vẫn tồn tại trong suốt thời gian tồn tại của chương trình.
  • Kích thước của biến/mảng phải được biết tại thời điểm biên dịch chương trình.
  1. Cấp phát bộ nhớ tự động (Automatic memory allocation):
  • Xảy ra trên các tham số hàmbiến cục bộ.
  • Vùng nhớ của các loại biến này được cấp phát khi chương trình đi vào khối lệnh và được giải phóng khi khối lệnh bị thoát.
  • Kích thước của biến/mảng phải được biết tại thời điểm biên dịch chương trình.
  1. Cấp phát bộ nhớ động (Dynamic memory allocation) sẽ được nói đến trong bài học này.

Trong hầu hết các trường hợp, cấp phát bộ nhớ tĩnh tự động có thể đáp ứng tốt các yêu cầu của chương trình. Tuy nhiên, ta cùng xem ví dụ bên dưới:

Ví dụ: Chúng ta cần sử dụng một chuỗi để lưu tên của người dùng, nhưng chúng ta không biết tên của họ dài bao nhiêu cho đến khi họ nhập tên. Hoặc chúng ta cần lưu trữ danh sách nhân viên trong một công ty, nhưng chúng ta không biết trước được công ty đó sẽ có bao nhiêu nhân viên.

Đối với cấp phát bộ nhớ tĩnh tự động, kích thước của biến/mảng phải được biết tại thời điểm biên dịch chương trình. Vì vậy, điều tốt nhất chúng ta có thể làm là cố gắng đoán một kích thước tối đa của các biến đó:

Khuyết điểm của cách khai báo trên:

  1. Gây lãng phí bộ nhớ nếu các biến không thực sự sử dụng hết kích thước khi khai báo. Ví dụ: nếu công ty chỉ có 100 nhân viên, chúng ta có 400 vùng nhớ nhân viên không được sử dụng tới.
  2. Thứ hai, hầu hết các biến thông thường (bao gồm mảng tĩnh) được cấp phát trong một phần bộ nhớ gọi là ngăn xếp (stack). Kích thước bộ nhớ stack cho một chương trình khá nhỏ (khoảng 1Mb với Visual Studio), nếu yêu cầu cấp phát vùng nhớ vượt quá con số này, chương trình sẽ bị đóng bởi hệ điều hành với lỗi stack overflow.
  1. Thứ ba, điều gì xảy ra nếu công ty có 600 nhân viên, trong khi mảng staff chỉ có 500 phần tử. Lúc này, chương trình sẽ bị giới hạn bởi kích thước được khai báo ban đầu.

Để giải quyết những hạn chế trên, cấp phát bộ nhớ động được ra đời.


Cấp phát động trong C++

Cấp phát bộ nhớ động (Dynamic memory allocation) là cách để yêu cầu bộ nhớ từ hệ điều hành khi cần thiết (thời điểm chương trình đang chạy). Cấp phát bộ nhớ động sử dụng vùng nhớ được quản lý bởi hệ điều hành được gọi là heap. Ngày nay, bộ nhớ heap có thể có kích thước gigabyte.

Cấp phát động cho các biến đơn (Dynamically allocating single variables)

Để cấp phát động cho một biến, ta sử dụng toán tử new:

Trong ví dụ trên, chương trình yêu cầu cấp phát vùng nhớ của một số nguyên từ hệ điều hành. Toán tử new tạo đối tượng sử dụng vùng nhớ đó và sau đó trả về một con trỏ chứa địa chỉ của vùng nhớ đã được cấp phát.

Thông thường, để truy cập vào vùng nhớ được cấp phát, chúng ta dùng con trỏ để lưu giữ địa chỉ được trả về bởi toán tử new:

Sau đó, chúng ta có thể thao tác trên vùng nhớ vừa được cấp phát thông qua con trỏ:

Khi cấp phát động cho một biến, bạn có thể cùng lúc khởi tạo giá trị cho nó:


Xóa các biến đơn (Deleting single variables)

Khi chúng ta không còn sử dụng một biến được cấp phát động, chúng ta cần trao quyền quản lý vùng nhớ đó lại cho hệ điều hành. Đối với các biến đơn (không phải mảng), điều này được thực hiện thông qua toán tử delete:

Toán tử delete không thực sự xóa bất cứ điều gì. Nó chỉ đơn giản là trao lại quyền sử dụng vùng nhớ được cấp phát cho hệ điều hành. Sau đó, hệ điều hành được tự do gán lại vùng nhớ đó cho một ứng dụng khác (hoặc ứng dụng này).

Chú ý: Mặc dù câu lệnh ”delete ptr” giống như việc xóa một biến, nhưng thực tế không phải! Biến con trỏ ptr vẫn có thể sử dụng như trước và có thể được gán một giá trị mới giống như bất kỳ biến nào khác.


Con trỏ lơ lửng (Dangling pointers)

Thông thường, khi delete một con trỏ, vùng nhớ được trả lại cho hệ điều hành sẽ chứa cùng giá trị mà nó có trước đó. Lúc này, con trỏ đang trỏ sang một vùng nhớ chưa được cấp phát (hệ điều hành quản lý).

Con trỏ trỏ đến vùng nhớ chưa được cấp phát gọi là một con trỏ lơ lửng (Dangling pointers). Truy cập vào vùng nhớ (dereferencing pointer) hoặc xóa một con trỏ lơ lửng sẽ dẫn đến lỗi undefined behavior.

Việc giải phóng một vùng nhớ cũng có thể tạo ra nhiều con trỏ lơ lửng (dangling pointers).

Ví dụ:

Chú ý:

  • Tránh sử dụng nhiều con trỏ trỏ vào cùng một vùng nhớ.

  • Khi xóa một con trỏ, nếu chương trình không ra khỏi phạm vi của con trỏ ngay sau đó, hãy gán con trỏ thành 0 (hoặc nullptr trong C++ 11).


Con trỏ null và cấp phát động

Chúng ta đã được biết về con trỏ null trong bài CON TRỎ NULL TRONG C++ (NULL pointers).

Con trỏ null đặc biệt hữu ích trong cấp phát bộ nhớ động. Trong cấp phát bộ nhớ động, một con trỏ null có ý nghĩa “không có vùng nhớ nào được cấp phát cho con trỏ này”.


Rò rỉ bộ nhớ trong C++ (Memory leaks)

Chúng ta cùng xem hàm bên dưới:

Ví dụ 1:

Trong hàm doSomething() cấp phát động một số nguyên, nhưng không sử dụng toán tử delete để giải phóng vùng nhớ đó. Vì con trỏ tuân theo tất cả các quy tắc giống như các biến thông thường, khi hàm kết thúc, ptr sẽ bị hủy. Mặt khác, ptr là biến duy nhất giữ địa chỉ của số nguyên được cấp phát động. Nghĩa là chương trình đã "mất" địa chỉ của bộ nhớ được cấp phát động trong hàm doSomething(). Kết quả là chương trình không thể giải phóng vùng nhớ được cấp phát động.

Vấn đề trên được gọi là rò rỉ bộ nhớ (memory leaks). Rò rỉ bộ nhớ xảy ra khi chương mất địa chỉ của một số vùng nhớ được cấp phát động trước khi giải phóng nó cho hệ điều hành.

Khi rò rỉ bộ nhớ, chương trình của bạn không thể xóa bộ nhớ được cấp phát động, bởi vì chương trình không còn nắm giữ địa chỉ vùng nhớ đó. Hệ điều hành cũng không thể sử dụng vùng nhớ này, vì vùng nhớ đó vẫn nằm trong quyền sử dụng của chương trình.

Các chương trình gặp vấn đề rò rỉ bộ nhớ nghiêm trọng có thể lấy hết bộ nhớ có sẵn, làm cho hệ điều hành chạy chậm hoặc thậm chí bị crash. Chỉ sau khi chương trình tắt, hệ điều hành mới có thể dọn dẹp và "đòi lại" tất cả vùng nhớ bị rò rỉ.

Một số trường hợp khác có thể gây rò rỉ bộ nhớ trong C++:

Ví dụ 2: Con trỏ giữ địa chỉ của bộ nhớ được cấp phát động được gán một giá trị khác gây rò rỉ bộ nhớ.

Ví dụ 3: Cấp phát vùng nhớ liên tục nhiều lần

Để khắc phục vấn đề rò rỉ bộ nhớ (memory leaks) trong C++, chúng ta cần giải phóng vùng nhớ khi ra khỏi phạm vi con trỏ (ví dụ 1), hoặc trước khi gán (ví dụ 2), cấp phát một con trỏ (ví dụ 3).


Kết luận

Qua bài học này, bạn đã nắm được cách Cấp phát động trong C++ (Dynamic memory allocation). Với kỹ thuật này, bạn có thể tự do sử dụng bộ nhớ hệ thống một cách không giới hạn (giới hạn phần cứng) trong chương trình.

Lưu ý rằng khi sử dụng cấp phát động, bạn cần nắm rõ những kiến thức cơ bản về cấp phát và giải phóng vùng nhớ trong bài viết này để tránh rò rỉ bộ nhớ, cũng như những vấn đề về vùng nhớ khác.

Trong bài tiếp theo, mình sẽ giới thiệu cho các bạn cách Cấp phát mảng động (Dynamically allocating arrays).

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.


Tải xuống

Tài liệu

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học Cấp phát động trong C++ (Dynamic memory allocation). dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên likeshare để ủng hộ Kteam và tác giả nhé!


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng.

Tác giả/Dịch giả

Khóa học

Khóa học lập trình C++ căn bản

Hiện nay, C++ đã là cái tên rất quen thuộc trong ngành lập trình. Mặc dù C++ là ngôn ngữ lập trình đã ra đời khá lâu, nhưng không phải ai cũng có cơ hội để tìm hiểu về nó.

Vì vậy, Kteam đã xây dựng lên khóa học LẬP TRÌNH C++ CĂN BẢN để cung cấp một lượng kiến thức về ngôn ngữ C++ nói riêng, và các khái niệm khác trong lập trình nói chung.

Nội dung khóa học sẽ được phân tách một cách chi tiết, nhằm giúp các bạn dễ hiểu và thực hành được ngay. Serial dành cho những bạn chưa có bất kỳ kiến thức gì về lập trình, hoặc những bạn mất căn bản muốn lấy lại kiến thức nền tảng lập trình, cụ thể là C++.

Đánh giá

5.0
8 đánh giá
Đánh giá
5
8
4
3
2
1
win1702 đã đánh giá 9 tháng trước

rarl123 đã đánh giá 2 năm trước

Calamanbe đã đánh giá 3 năm trước

nguyennhucong đã đánh giá 3 năm trước

longkhtnhcmus đã đánh giá 4 năm trước

Bình luận

Để bình luận, bạn cần đăng nhập bằng tài khoản Howkteam.

Đăng nhập
Minh Nguyễn đã bình luận 5 năm trước

cho em hỏi tí là mình cấp phát một biến động nhưng mà không delete thì chạy hết chương trình hệ điều hành có quyền lấy ô nhớ cấp phát đó không ạ

tdkhoa2018 đã bình luận 6 năm trước

Ở mục con trỏ lơ lửng,
 

int *ptr=new int(15);
delete ptr;
cout << *ptr; //nó xuất ra 1 giá trị rác chứ không báo lỗi gì cả

em đã thử trên cả devC++ và sublime text 3.
Mong mn giúp em với.

tdkhoa2018 đã bình luận 6 năm trước

Ở mục con trỏ lơ lửng,
 

int *ptr=new int(15);
delete ptr;
cout << *ptr; //nó xuất ra 1 giá trị rác chứ không báo lỗi gì cả

em đã thử trên cả devC++ và sublime text 3.
Mong mn giúp em với.

Không có video.