Làm thế nào miễn phí biết bao nhiêu để miễn phí?


385

Trong lập trình C, bạn có thể chuyển bất kỳ loại con trỏ nào bạn muốn làm đối số để giải phóng, làm thế nào để biết kích thước của bộ nhớ được phân bổ miễn phí? Bất cứ khi nào tôi chuyển một con trỏ đến một số hàm, tôi cũng phải truyền kích thước (tức là một mảng gồm 10 phần tử cần nhận 10 làm tham số để biết kích thước của mảng), nhưng tôi không phải truyền kích thước cho chức năng miễn phí. Tại sao không, và tôi có thể sử dụng kỹ thuật tương tự này trong các chức năng của riêng mình để cứu tôi khỏi cần phải xoay quanh biến phụ của chiều dài của mảng không?


Một câu hỏi tương tự: stackoverflow.com/questions/851958/ (mặc dù tôi nói nó không hoàn toàn trùng lặp)
John Carter

Các hệ thống bạn bè là một cách khác để làm điều đó có thể tìm ra dựa trên con trỏ, mà không cần chi phí trong mỗi khối.
EvilTeach

Bài đăng này giải thích rất rõ: stackoverflow.com/questions/1957099/
Kẻ

Câu trả lời:


349

Khi bạn gọi malloc(), bạn chỉ định dung lượng bộ nhớ sẽ phân bổ. Dung lượng bộ nhớ thực sự được sử dụng nhiều hơn một chút so với mức này và bao gồm thông tin bổ sung ghi lại (ít nhất) khối lượng lớn như thế nào. Bạn không thể (đáng tin cậy) truy cập thông tin khác đó - và bạn cũng không nên :-).

Khi bạn gọi free(), nó chỉ cần nhìn vào thông tin bổ sung để tìm hiểu khối lớn như thế nào.


44
FYI, ví dụ BSD malloc_size()phải truy cập đáng tin cậy kích thước khối từ một malloc()con trỏ ed. Nhưng không có cách nào đáng tin cậy, di động.
laalto

50
Tôi nghĩ điều quan trọng là phải nói rằng khối thông tin bổ sung này được đặt trước con trỏ được trả về.
Georg Schölly

39
@ss Vâng, đó là phụ thuộc vào việc thực hiện. Nhưng, vâng, đó là nơi thường có.
Falaina

31
Bạn có thể tưởng tượng sự kinh hoàng nếu free()yêu cầu lập trình viên báo cáo chính xác malloc()khối lớn như thế nào không? Các rò rỉ bộ nhớ là đủ xấu như nó là.
MusiGenesis

35
Tại sao thông tin đó có sẵn cho malloc()free(), nhưng bạn phải lưu trữ kích thước của một mảng? Tại sao họ không thể làm điều gì đó giống như blockSize(ptr)nếu họ đang lưu trữ thông tin?
corsiKa

144

Hầu hết các triển khai chức năng cấp phát bộ nhớ C sẽ lưu trữ thông tin kế toán cho từng khối, theo dòng hoặc riêng biệt.

Một cách điển hình (nội tuyến) là thực sự phân bổ cả tiêu đề và bộ nhớ bạn yêu cầu, được đệm ra một số kích thước tối thiểu. Vì vậy, ví dụ, nếu bạn yêu cầu 20 byte, hệ thống có thể phân bổ một khối 48 byte:

  • Tiêu đề 16 byte chứa kích thước, điểm đánh dấu đặc biệt, tổng kiểm tra, con trỏ tới khối tiếp theo / trước, v.v.
  • Vùng dữ liệu 32 byte (20 byte của bạn được đệm thành bội số của 16).

Địa chỉ được cung cấp cho bạn là địa chỉ của vùng dữ liệu. Sau đó, khi bạn giải phóng khối, freechỉ cần lấy địa chỉ bạn cung cấp và giả sử bạn chưa nhồi địa chỉ đó hoặc bộ nhớ xung quanh nó, hãy kiểm tra thông tin kế toán ngay trước nó. Về mặt đồ họa, đó sẽ là dọc theo dòng:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Hãy ghi nhớ kích thước của tiêu đề và phần đệm hoàn toàn được xác định thực hiện (thực ra, toàn bộ điều được xác định theo thực hiện (a) nhưng tùy chọn kế toán nội tuyến là một tùy chọn phổ biến).

Tổng kiểm tra và các điểm đánh dấu đặc biệt tồn tại trong thông tin kế toán thường là nguyên nhân gây ra các lỗi như "Đấu trường bộ nhớ bị hỏng" hoặc "Nhân đôi miễn phí" nếu bạn ghi đè lên chúng hoặc giải phóng chúng hai lần.

Phần đệm (để phân bổ hiệu quả hơn) là lý do tại sao đôi khi bạn có thể viết một chút ngoài phần cuối của không gian bạn yêu cầu mà không gây ra sự cố (vẫn không làm điều đó, đó là hành vi không xác định và đôi khi nó hoạt động, không ' t có nghĩa là nó ổn để làm điều đó).


(a) Tôi đã viết các triển khai malloctrong các hệ thống nhúng, nơi bạn có 128 byte cho dù bạn yêu cầu gì (đó là kích thước của cấu trúc lớn nhất trong hệ thống), giả sử bạn đã yêu cầu 128 byte hoặc ít hơn (yêu cầu nhiều hơn sẽ được đáp ứng với giá trị trả về NULL). Một mặt nạ bit rất đơn giản (nghĩa là không phải trong dòng) đã được sử dụng để quyết định xem một đoạn 128 byte có được phân bổ hay không.

Những người khác mà tôi đã phát triển có các nhóm khác nhau cho các khối 16 byte, khối 64 byte, khối 256 byte và khối 1K, một lần nữa sử dụng mặt nạ bit để quyết định khối nào được sử dụng hoặc có sẵn.

Cả hai tùy chọn này đều có thể giảm chi phí thông tin kế toán và tăng tốc độ mallocfree(không cần phải kết hợp các khối liền kề khi giải phóng), đặc biệt quan trọng trong môi trường chúng tôi đang làm việc.


@paxdiablo Điều đó có nghĩa là malloc không phân bổ các khối bộ nhớ liền kề?
dùng10678

2
@ user10678, yêu cầu thực sự duy nhất malloclà nó cung cấp cho bạn, đối với trường hợp thành công, một khối bộ nhớ ít nhất là lớn như những gì bạn yêu cầu. Các khối riêng lẻ tiếp giáp nhau về cách bạn truy cập các phần tử bên trong chúng, nhưng không có yêu cầu nào về việc các khối đến từ đó liền kề nhau.
paxdiablo

Câu hỏi liên quan: Tại sao không có biến thể của malloc / free, nơi bạn chỉ định kích thước khi giải phóng và do đó nó không phải lưu trữ kích thước?
dùng253751

@ user253751, bởi vì sau đó, một điều nữa bạn cần theo dõi, hơn và hơn chính con trỏ. Điều đó không cần thiết nguy hiểm: void *x = malloc(200); free(x, 500);sẽ không kết thúc tốt :-) Trong mọi trường hợp, về hiệu quả, kích thước thực tế của bộ đệm có thể lớn hơn (bạn không thể dựa vào điều này).
paxdiablo

@paxdiablo Nó cũng tránh lãng phí bộ nhớ để giữ kích thước.
dùng253751

47

Từ comp.lang.cdanh sách Câu hỏi thường gặp: Làm thế nào miễn phí biết có bao nhiêu byte miễn phí?

Việc thực hiện malloc / miễn phí ghi nhớ kích thước của mỗi khối khi được phân bổ, do đó không cần thiết phải nhắc nhở về kích thước khi giải phóng. (Thông thường, kích thước được lưu trữ liền kề với khối được phân bổ, đó là lý do tại sao mọi thứ thường bị hỏng nặng nếu giới hạn của khối được phân bổ thậm chí hơi bị vượt quá)


2
Đây là một câu trả lời không. Câu hỏi chính xác là: tại sao có thể tự do tra cứu kích thước của khối, nhưng vẫn không có chức năng nào dành cho lập trình viên làm điều đó?
Tunea

Đây thực sự là một chi tiết triển khai cho malloc api và không có api để lấy lại thông tin này theo cách chuẩn (theo hiểu biết của tôi). "Hệ thống" ghi lại nó và sử dụng nó trên free. Có thể câu trả lời không làm bạn hài lòng nhưng tôi không nghĩ bạn sẽ có được một thông tin có thể áp dụng rộng rãi hơn :-)
jdehaan

6

Câu trả lời này được chuyển từ Làm thế nào để free () biết có bao nhiêu bộ nhớ để giải quyết? nơi tôi bị ngăn cản không thể trả lời bằng một câu hỏi trùng lặp rõ ràng. Câu trả lời này sau đó nên có liên quan đến bản sao này:


Trong trường hợp malloc, bộ cấp phát heap lưu trữ ánh xạ của con trỏ trả về ban đầu, đến các chi tiết có liên quan cần thiết để freelấy bộ nhớ sau này. Điều này thường liên quan đến việc lưu trữ kích thước của vùng bộ nhớ dưới bất kỳ hình thức nào có liên quan đến bộ cấp phát đang sử dụng, ví dụ kích thước thô hoặc nút trong cây nhị phân được sử dụng để theo dõi phân bổ hoặc số lượng "đơn vị" bộ nhớ đang sử dụng.

freesẽ không thất bại nếu bạn "đổi tên" con trỏ hoặc sao chép nó theo bất kỳ cách nào. Tuy nhiên, đây không phải là tài liệu tham khảo, và chỉ người đầu tiên freesẽ đúng. freeS bổ sung là lỗi "miễn phí gấp đôi".

Cố gắng với freebất kỳ con trỏ nào có giá trị khác với những con trỏ được trả về bởi mallocs trước đó và khi chưa tham gia là một lỗi. Không thể để các vùng bộ nhớ trống một phần được trả về malloc.


Tôi đã thay đổi giá trị của một con trỏ được trả về bởi một cuộc gọi malloc. Và tôi đã giải phóng nó mà không có lỗi. Tại sao? Xem tại đây: stackoverflow.com/questions/42618390/ Từ
smwikipedia

4

Trên một lưu ý liên quan Thư viện GLib có các chức năng cấp phát bộ nhớ không lưu kích thước ẩn - và sau đó bạn chỉ cần chuyển tham số kích thước miễn phí. Điều này có thể loại bỏ một phần của chi phí.


3

malloc()free()phụ thuộc vào hệ thống / trình biên dịch nên thật khó để đưa ra câu trả lời cụ thể.

Thêm thông tin về câu hỏi khác này .


2
Chúng thực sự phụ thuộc vào thư viện (thường là thư viện C, thường được liên kết rất chặt chẽ với HĐH). Đối với trình biên dịch, chúng chỉ là các chức năng.
Donal Fellows

2

Trình quản lý heap lưu trữ số lượng bộ nhớ thuộc về khối được phân bổ ở đâu đó khi bạn gọi malloc.

Tôi chưa bao giờ tự thực hiện một cái, nhưng tôi đoán bộ nhớ ngay trước khối được phân bổ có thể chứa thông tin meta.


3
Đó là một cách thực hiện có thể, nhưng người ta có thể nghĩ ra một hệ thống trong đó tất cả bộ nhớ được theo dõi trong một bảng trong một trang hoàn toàn khác, không nhất thiết là bất kỳ nơi nào gần với nhóm bộ nhớ được phân bổ.
ephemient

2

Kỹ thuật ban đầu là phân bổ một khối lớn hơn một chút và lưu trữ kích thước lúc ban đầu, sau đó cung cấp cho ứng dụng phần còn lại của blog. Không gian thêm giữ một kích thước và có thể liên kết để xâu các khối miễn phí lại với nhau để tái sử dụng.

Tuy nhiên, có một số vấn đề nhất định với các thủ thuật đó, chẳng hạn như bộ nhớ cache và hành vi quản lý bộ nhớ kém. Sử dụng bộ nhớ ngay trong khối có xu hướng trang những thứ không cần thiết và nó cũng tạo ra các trang bẩn làm phức tạp việc chia sẻ và sao chép khi viết.

Vì vậy, một kỹ thuật nâng cao hơn là giữ một thư mục riêng. Phương pháp tiếp cận kỳ lạ cũng đã được phát triển trong đó các khu vực của bộ nhớ sử dụng cùng kích cỡ của hai kích cỡ.

Nói chung, câu trả lời là: một cấu trúc dữ liệu riêng biệt được phân bổ để giữ trạng thái.


1

Để trả lời nửa sau câu hỏi của bạn: có, bạn có thể, và một mẫu khá phổ biến trong C là như sau:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;

Đó là một kỹ thuật hoàn toàn khác với một malloc BSD sử dụng cho các đối tượng nhỏ (mặc dù đó là một kỹ thuật hoàn toàn tốt để tạo các mảng kiểu Pascal)
Pete Kirkham

0

Khi chúng ta gọi malloc, nó chỉ đơn giản là tiêu thụ nhiều byte hơn từ yêu cầu của nó. Mức tiêu thụ nhiều byte hơn này chứa thông tin như tổng kiểm tra, kích thước và thông tin bổ sung khác. Khi chúng tôi gọi miễn phí tại thời điểm đó, nó sẽ trực tiếp đến thông tin bổ sung nơi tìm địa chỉ và cũng tìm thấy bao nhiêu khối sẽ miễn phí.


0

để trả lời câu hỏi thứ hai, vâng, bạn có thể (loại) sử dụng kỹ thuật tương tự như malloc() bằng cách chỉ định ô đầu tiên bên trong mỗi mảng với kích thước của mảng. cho phép bạn gửi mảng mà không gửi đối số kích thước bổ sung.

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.