В большинстве реализаций функций выделения памяти на С будет храниться учетная информация для каждого блока, как в строке, так и отдельно.
Один типичный способ (in-line) состоит в том, чтобы фактически выделить как заголовок, так и запрошенную вами память с минимальным размером. Так, например, если вы запросили 20 байтов, система может выделить 48-байтовый блок:
- 16-байтовый заголовок, содержащий размер, специальный маркер, контрольную сумму, указатели на следующий / предыдущий блок и так далее.
- Область данных 32 байта (ваши 20 байтов дополняются до кратного 16).
Адрес, который вам дается, является адресом области данных. Затем, когда вы освободите блок, free
просто возьмете адрес, который вы ему дали, и, предположив, что вы не заполнили этот адрес или память вокруг него, проверьте бухгалтерскую информацию непосредственно перед ней. Графически это будет выглядеть так:
____ The allocated block ____
/ \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
^
|
+-- The address you are given
Имейте в виду, что размер заголовка и отступа полностью определяются реализацией (на самом деле все зависит от реализации (а), но встроенный вариант учета является распространенным).
Контрольные суммы и специальные маркеры, которые существуют в учетной информации, часто являются причиной ошибок, таких как «Память повреждена» или «Двойное освобождение», если вы перезаписываете их или освобождаете их дважды.
Заполнение (чтобы сделать выделение более эффективным) - вот почему вы иногда можете написать немного за пределами запрошенного пространства, не вызывая проблем (тем не менее, не делайте этого, это неопределенное поведение и, просто потому, что иногда оно работает, не ' Я имею в виду, что это нормально).
(а) Я написал реализации malloc
во встроенных системах, в которых вы получили 128 байтов независимо от того, что вы запрашивали (это был размер самой большой структуры в системе), предполагая, что вы запрашивали 128 байтов или меньше (запросы на большее встретиться с нулевым возвращаемым значением). Очень простая битовая маска (т. Е. Не встроенная) использовалась, чтобы решить, был ли выделен фрагмент размером 128 байт или нет.
Другие, которые я разработал, имели разные пулы для 16-байтовых блоков, 64-байтовых блоков, 256-байтовых блоков и блоков размером 1 КБ, опять же с использованием битовой маски, чтобы определить, какие блоки были использованы или доступны.
Оба этих варианта позволили сократить накладные расходы на учетную информацию и увеличить скорость malloc
и free
(не нужно объединять смежные блоки при освобождении), что особенно важно в среде, в которой мы работали.