Làm thế nào để xóa [] Nhận biết kích thước của mảng toán hạng?


250
Foo* set = new Foo[100];
// ...
delete [] set;

Bạn không vượt qua ranh giới của mảng delete[]. Nhưng thông tin đó được lưu trữ ở đâu? Có chuẩn không?


sourceforge.net/projects/fastmm là nguồn mở và thay thế trình quản lý bộ nhớ. Tại đây bạn có thể tìm hiểu cách quản lý bộ nhớ hoạt động và nơi thông tin đến từ việc phân bổ và xóa bộ nhớ.

1
Lưu ý rằng FastMM chỉ dành riêng cho trình biên dịch Delphi / C ++ Builder, nó không phải là trình quản lý bộ nhớ cho mục đích chung cho C ++. Nó thậm chí không được viết bằng C ++.
Rémy Lebeau

Câu trả lời:


181

Khi bạn phân bổ bộ nhớ trên heap, bộ cấp phát của bạn sẽ theo dõi lượng bộ nhớ bạn đã phân bổ. Điều này thường được lưu trữ trong một phân đoạn "đầu" ngay trước bộ nhớ mà bạn được phân bổ. Theo cách đó, khi đến lúc giải phóng bộ nhớ, bộ phân bổ biết chính xác dung lượng bộ nhớ còn trống.


4
Lưu ý rằng điều này chỉ áp dụng cho phân bổ mảng trong C ++. Tất cả các phân bổ khác dựa trên kích thước của loại. Một số thư viện lưu trữ tất cả các kích thước phân bổ, thường chỉ trong chế độ gỡ lỗi.
Zan Lynx

97
Hoàn toàn không có lý do tại sao thông tin này không nên có sẵn cho các lập trình viên. Tôi có thể truyền một con trỏ tới một hàm và giải phóng nó, nhưng để có được kích thước cho chính nó trong cùng một hàm đó, tôi phải chuyển qua một tham số phụ. Điều đó có ý nghĩa gì?
Đánh dấu Ruzon

26
@Mark, nó có ý nghĩa rất nhỏ , vì về lý thuyết, nó không cho phép người cấp phát luôn lưu trữ kích thước của khối được phân bổ (có thể khác với kích thước của khối được yêu cầu ). Một số thiết kế bộ cấp phát có thể cần thông tin này cho mục đích riêng của chúng hoặc có thể không đủ tinh vi để sử dụng thông tin loại để theo dõi kích thước của phân bổ heap không mảng, v.v. Buộc người cấp phát phải lưu trữ kích thước được yêu cầu (để bạn không cần phải tự vượt qua kích thước mảng) có thể là một trở ngại nhỏ, nhưng nó có thể có tác động hiệu suất đến các thiết kế phân bổ có thể hiểu được.
Doug McClean

33
Xin lỗi, nhưng câu trả lời này bỏ lỡ điểm. Những gì QuantumPete mô tả về cơ bản là "Làm thế nào để freebiết bao nhiêu bộ nhớ để phân bổ". Có, kích thước khối bộ nhớ được lưu trữ "ở đâu đó" bởi malloc(thông thường trong chính khối đó), vì vậy đó là cách freebiết. Tuy nhiên, new[]/ delete[]là một câu chuyện khác nhau. Cái sau về cơ bản hoạt động trên malloc/ free. new[]cũng lưu trữ số lượng phần tử mà nó tạo ra trong khối bộ nhớ (độc lập malloc), để sau này delete[]có thể truy xuất và sử dụng số đó để gọi số lượng hàm hủy thích hợp.
AnT

26
Tức là hai bộ đếm vật lý được lưu trữ trong khối: kích thước khối (theo malloc) và số phần tử (theo new[]). Lưu ý rằng cái trước không thể được sử dụng để tính toán cái sau, vì trong trường hợp chung, kích thước của khối bộ nhớ có thể lớn hơn thực sự cần thiết cho mảng kích thước được yêu cầu. Cũng lưu ý rằng bộ đếm phần tử mảng chỉ cần thiết cho các loại có hàm hủy không tầm thường. Đối với các loại có hàm hủy tầm thường, bộ đếm không được lưu trữ bởi new[]và, tất nhiên, không được truy xuất bởi delete[].
AnT

23

Một trong những cách tiếp cận cho trình biên dịch là phân bổ thêm một chút bộ nhớ và lưu trữ một số phần tử trong phần tử head.

Ví dụ về cách nó có thể được thực hiện:

Đây

int* i = new int[4];

trình biên dịch sẽ phân bổ sizeof(int)*5byte.

int *temp = malloc(sizeof(int)*5)

Sẽ lưu trữ "4" trong các sizeof(int)byte đầu tiên

*temp = 4;

và thiết lập i

i = temp + 1;

Vì vậy, isẽ chỉ đến một mảng gồm 4 phần tử, không phải 5.

Và xóa

delete[] i;

sẽ được xử lý theo cách sau:

int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)

9

Thông tin không được chuẩn hóa. Tuy nhiên, trong các nền tảng mà tôi đã làm việc với thông tin này được lưu trữ trong bộ nhớ ngay trước phần tử đầu tiên. Do đó về mặt lý thuyết bạn có thể truy cập nó và kiểm tra nó, tuy nhiên nó không đáng.

Ngoài ra, đây là lý do tại sao bạn phải sử dụng xóa [] khi bạn cấp phát bộ nhớ cho [] mới, vì phiên bản mảng xóa biết rằng (và ở đâu) nó cần tìm để giải phóng lượng bộ nhớ phù hợp - và gọi số lượng hàm hủy thích hợp cho các đối tượng.


5

Về cơ bản, nó được sắp xếp trong bộ nhớ như:

[thông tin] [mem bạn đã yêu cầu ...]

Trong đó thông tin là cấu trúc được trình biên dịch của bạn sử dụng để lưu trữ lượng bộ nhớ được phân bổ và những gì không.

Đây là phụ thuộc thực hiện mặc dù.


4

Đây không phải là một cái gì đó trong thông số kỹ thuật - nó phụ thuộc vào việc thực hiện.


3

Nó được định nghĩa trong tiêu chuẩn C ++ là trình biên dịch cụ thể. Có nghĩa là ma thuật biên dịch. Nó có thể phá vỡ với các hạn chế liên kết không tầm thường trên ít nhất một nền tảng chính.

Bạn có thể suy nghĩ về các triển khai có thể bằng cách nhận ra rằng delete[]chỉ được xác định cho các con trỏ được trả về bởi new[], có thể không phải là con trỏ giống như được trả về bởi operator new[]. Một cách thực hiện trong tự nhiên là lưu trữ số mảng trong int đầu tiên được trả về bởi operator new[]và đã new[]trả về một con trỏ bù qua đó. (Đây là lý do tại sao sự sắp xếp không tầm thường có thể phá vỡ new[].)

Hãy ghi nhớ rằng operator new[]/operator delete[]! = new[]/delete[].

Thêm vào đó, đây là trực giao với cách C biết kích thước của bộ nhớ được phân bổ theo malloc.


2

Bởi vì mảng được 'xóa' nên đã được tạo bằng một lần sử dụng toán tử 'mới'. Hoạt động 'mới' nên đã đưa thông tin đó vào đống. Nếu không, làm thế nào để sử dụng bổ sung mới biết nơi heap kết thúc?


0

Nó không được chuẩn hóa. Trong thời gian chạy của Microsoft, toán tử mới sử dụng malloc () và toán tử xóa sử dụng free (). Vì vậy, trong cài đặt này, câu hỏi của bạn tương đương với câu hỏi sau: Làm thế nào để free () biết kích thước của khối?

Có một số sổ sách kế toán đang diễn ra đằng sau hậu trường, tức là trong thời gian chạy C.


5
Không đúng. Trước khi gọi miễn phí, xóa [] cần gọi hàm hủy trước tiên. Đối với điều này biết tổng kích thước phân bổ là không đủ. Trên thực tế, [] và xóa [] mới hoạt động khác nhau đối với các loại đơn giản và bị phá hủy trong VC ++.
Suma

0

Đây là một vấn đề thú vị hơn bạn nghĩ lúc đầu. Câu trả lời này là về một thực hiện có thể.

Đầu tiên, mặc dù ở một mức độ nào đó, hệ thống của bạn phải biết cách 'giải phóng' khối bộ nhớ, malloc / free bên dưới (mà mới / xóa / mới [] / xóa [] thường gọi) không luôn nhớ chính xác bao nhiêu bộ nhớ bạn yêu cầu, nó có thể được làm tròn lên (ví dụ: một khi bạn ở trên 4K, nó thường được làm tròn đến khối có kích thước 4K tiếp theo).

Do đó, ngay cả khi có thể có kích thước của khối bộ nhớ, điều đó không cho chúng ta biết có bao nhiêu giá trị trong bộ nhớ [] ed mới, vì nó có thể nhỏ hơn. Do đó, chúng ta phải lưu trữ một số nguyên phụ cho chúng ta biết có bao nhiêu giá trị.

NGOẠI TRỪ, nếu loại đang được xây dựng không có hàm hủy, thì xóa [] không phải làm gì ngoại trừ giải phóng khối bộ nhớ và do đó không phải lưu trữ bất cứ thứ gì!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.