Thứ Năm, 5 tháng 7, 2018

Bộ nhớ động trong C/C++

Đối với ngôn ngữ lập trình C++, mảng giúp ta quản lí bộ nhớ phục vụ cho việc thao tác khi lập trình thuận tiện, linh động và tiết kiệm thời gian. Nhưng kích cỡ của chúng là cố định và không thể thay đổi trong thời gian chương trình chạy. Nếu bây giờ chúng ta cần một lượng bộ nhớ có thể xác định ngay trong lúc chạy chương trình, có cách nào mà không cần phải tạo ra một mảng với số lượng bất kì không?
Giải pháp ở đây chính là bộ nhớ động.
Edit - Tin hoc Đà Lạt, lập trình di động Android iOS, lập trình Web Đà Lạt

Bạn sẽ nắm được gì sau bài này?

  • Bộ nhớ động là gì?
  • Các hàm malloc, calloc, realloc và free trong C
  • Toán tử new, delete trong C++

Kiến thức cần có để tiếp tục


Bộ nhớ động là gì?

Ở bài trước, chúng ta đã đề cập đến Bộ nhớ, Stack và Heap trong C++.
Như các bạn đã biết, phân vùng Stack được đặt tại vùng có địa chỉ cao nhất trong dãy bộ nhớ ảo. Dung lượng của phân vùng này khá hạn chế. Với sự hạn chế về dung lượng bộ nhớ của phân vùng Stack, chương trình của chúng ta sẽ phát sinh lỗi stack overflow nếu các bạn yêu cầu cấp phát vùng nhớ vượt quá dung lượng của Stack. Để khắc phục hạn chế này, tôi sẽ giới thiệu đến các bạn một phương thức cấp phát bộ nhớ mới được ngôn ngữ C++ hỗ trợ.
Thông thường khi code, vùng nhớ các biến sẽ được cấp phát ngay khi chạy chương trình, kích thước vùng nhớ đó là cố định và để có được nó ta bắt buộc phải khai báo mảng. Nhưng nếu kích thước mảng ta khai báo quá lớn, mà vùng nhớ ta cần sử dụng lại quá nhỏ, thành ra ta lại lãng phí bộ nhớ. Ngược lại, vì tiết kiệm bộ nhớ nên khi khai báo ta chỉ khai báo một mảng nhỏ, đến lúc sử dụng thì vùng nhớ khai báo trước đó không đủ. Đây chính là lí do tại sao ta lại cần đến bộ nhớ động.
Từ “động” ở đây có nghĩa là “tự động“. Là bộ nhớ lúc ta cần thì có thể xin quyền sử dụng, lúc không cần nữa có thể trả lại quyền sử dụng. Vì việc cấp phát và thu hồi bộ nhớ không do chương trình quyết định. Và kích thước vùng cần cấp phát không bị giới hạn.
Trong các chương trình C++, tất cả bộ nhớ đều được xử lí thông qua 2 toán tử: new để cấp phát và deleteđể thu hồi.

Kỹ thuật cấp phát động trong C++

Toán tử new và new[ ]

Để có được bộ nhớ động chúng ta có thể dùng toán tử new. Cú pháp của nó như sau:
Theo sau tên toán tử này là tên kiểu dữ liệu và có thể là số phần tử cần thiết được đặt trong cặp ngoặc vuông.
Để hiểu rõ hơn hãy xem ví dụ minh hoạ sau:
Đầu tiên, chúng ta có hai phân vùng Stack và Heap của bộ nhớ ảo,và một biến (pA).
Câu hỏi đặt ra ở đây chính là, biến pA sẽ lưu ở đâu?
bộ nhớ động trong C++
Kỹ thuật cấp phát động dùng để cấp phát bộ nhớ tại thời điểm chạy chương trình. Tại thời điểm này, chúng ta không thể tạo ra tên biến mới, mà chỉ có thể tạo ra vùng nhớ mới. Do đó, cách duy nhất để kiểm soát được những vùng nhớ được cấp phát là sử dụng con trỏ lưu trữ địa chỉ đầu tiên của vùng nhớ được cấp phát, thông qua con trỏ để quản lý vùng nhớ trên Heap.
Bây giờ, ta sử dụng new để xin cấp phát vùng nhớ cho biến pA. Nếu cấp phát thành công, toán tử newsau khi xin cấp phát vùng nhớ trên Heap sẽ trả về một con trỏ chứa địa chỉ của vùng nhớ được cấp phát.
bộ nhớ động trong C++
Tương tự, ta có một biến mới pB. Tiếp tục ta lại xin cấp phát vùng nhớ cho pB. Khi chương trình chạy, nếu quá trình cấp phát bộ nhớ trên thành công, chúng ta sẽ có địa chỉ của 2 vùng nhớ được trả về. Ta được hình vẽ bên dưới:
bộ nhớ động trong C++
Bây giờ, vùng nhớ được cấp phát sẽ được quản lý bởi 2 con trỏ pA và pB, 2 vùng nhớ này được hệ điều hành trao quyền sử dụng tạm thời cho chương trình của chúng ta.
Vậy, việc thực hiện cấp phát bộ nhớ cần thực hiện qua 2 bước:
  • Yêu cầu cấp phát vùng nhớ trên Heap.
  • Lưu trữ địa chỉ của vùng nhớ vừa được cấp phát bằng con trỏ.
Vì bộ nhớ động chỉ cần thiết trong một khoảng thời gian nhất định, khi không muốn sử dụng tiếp vùng nhớ đã được cấp phát cho chương trình thì ta nên giải phóng nó để có thể cấp phát cho các nhu cầu khác trong tương lai.

Kỹ thuật thu hồi bộ nhớ động trong C++

Toán tử delete và delete[ ]

Để thực hiện được việc đó ta dùng toán tử delete. Cú pháp của nó như sau:
Trong hầu hết các trình dịch thì cả hai biểu thức trên là tương đương nhau, mặc dù rõ ràng chúng là hai toán tử khác nhau.
Để hiểu rõ hơn hãy xem ví dụ minh hoạ sau:
Ở trên ta đã xin cấp phát vùng nhớ, dưới đây là vùng nhớ mà ta xin cấp phát được, bây giờ ta sẽ thực hiện thu hồi các vùng nhớ đó.
bộ nhớ động trong C++
Để xóa một vùng nhớ, chúng ta cần có một địa chỉ cụ thể, địa chỉ đó được giữ bởi con trỏ sau khi gán địa chỉ cấp phát cho nó.
bộ nhớ động trong C++
Bây giờ, Heap đã giải phóng pA(200). Ta có thể thấy địa chỉ của con trỏ pA trên Heap đã biến mất.
bộ nhớ động trong C++
Sau khi sử dụng delete, bộ nhớ Heap đã được giải phóng. Lúc này, Giá trị trên vùng nhớ đó có thể vẫn còn giữ nguyên do chưa có chương trình nào can thiệp vào.
bộ nhớ động trong C++
Sau đây là code minh hoạ cách phát động và thu hồi bộ nhớ động:

Kỹ thuật cấp phát động và thu hồi bộ nhớ động trong C

Tương tự trong các chương trình C++, C cũng cung cấp cho chúng ta 4 hàm: malloccallocrealloc để cấp phát và free để thu hồi, 4 hàm này nằm trong thư viện stdlib.h hoặc alloc.h. Để biết cách sử dụng các hàm trên ta hãy xem nguyên mẫu hàm (prototype) của nó.

Hàm malloc:

void * malloc ( size_t size );
Hàm này dùng để cấp phát một vùng nhớ có kích thước là size.

Hàm calloc:

void * calloc ( size_t num, size_t size );
Hàm này dùng để cấp phát vùng nhớ đủ chứa num phần tử, mỗi phần tử có kích thước là size.

Hàm realloc:

void * realloc ( void* ptr, size_t size );
Hàm này được dùng để thay đổi kích thước vùng nhớ đã cấp phát với ptr là địa chỉ của bộ nhớ đã cấp phát và size là kích thước muốn cấp phát lại (cập nhập) cho vùng nhớ đó.

Hàm free:

void * free ( void * ptr, size );
Hàm này được dùng để giải phóng vùng nhớ đã cấp phát.

Thắc mắc thường gặp:

1. Sự khác biệt giữa 2 hàm malloc và calloc ?
bộ nhớ động trong C++
Lưu ý: Vì giá trị khởi tạo cho vùng nhớ sau khi cấp phát thành công của 2 cơ chế malloc và calloc là khác nhau. Do đó, tùy vào mục tiêu sử dụng mà dùng cơ chế phù hợp. Nếu bạn không quan tâm đến giá mặc định của vùng nhớ được cấp thì dùng malloc còn nếu muốn tất cả giá trị của toàn bộ ô nhớ sau khi được cấp là 0 thì dùng calloc.
2. Tại sao có malloc, calloc rồi lại còn có realloc ?
Hàm realloc thay đổi kích thước của vùng nhớ đã được cấp phát, memblock trỏ tới vùng nhớ đã được cấp phát trước đó.
  • Nếu không đủ vùng nhớ để cấp phát mới, vùng nhớ cũ không thay đổi. Hàm trả về NULL.
  • Nếu memblock là NULL, hàm realloc tương tự như hàm malloc, sẽ cấp phát một vùng nhớ mới và trả về con trỏ tới vùng nhớ mới.
  • size: đưa ra kích thước mới cho vùng nhớ. Nếu kích thước nhỏ hơn kích thước vùng nhớ trước đó, dữ liệu trong vùng nhớ đó có thể bị thay đổi.
  • Nếu size = 0, vùng nhớ memblock được giải phóng, giá trị trả về là NULL.
  • Vùng nhớ mới được cấp phát có thể ở địa chỉ khác với vùng nhớ đã được cấp phát. Do vậy, con trỏ trả về của hàm có thể không phải là con trỏ truyền vào memblock.

Tổng kết:



bộ nhớ động trong C++
Bạn muốn học lập trình hãy liên hệ ngay với Mỹ Vân để được nhận ưu đãi từ học viện nhé
Học lập trình tại Đà Nẵng
Học lập trình tại Đà Nẵng 0935029202
Số tài khoản : 56110000942174 BIDV Chi nhánh Đà Nẵng
HUYNH THI MY VAN

Không có nhận xét nào:

Đăng nhận xét