Добре, деякі відповіді про malloc вже були опубліковані.
Більш цікава частина полягає в тому, як вільно працює (і в цьому напрямку малок теж можна зрозуміти краще).
У багатьох реалізаціях malloc / free, free зазвичай не повертає пам'ять операційній системі (або принаймні лише у рідкісних випадках). Причина полягає в тому, що ви отримаєте прогалини у вашій купі, і, таким чином, це може статися, що ви просто закінчите свої 2 або 4 ГБ віртуальної пам’яті з пробілами. Цього слід уникати, адже як тільки віртуальна пам’ять закінчиться, у вас виникнуть справді великі проблеми. Інша причина полягає в тому, що ОС може обробляти лише фрагменти пам'яті, які мають певний розмір та вирівнювання. Для конкретності: Зазвичай ОС може обробляти лише блоки, якими може керувати віртуальний менеджер пам'яті (найчастіше це кратні 512 байт, наприклад, 4 КБ).
Тому повернути 40 байт в ОС просто не вийде. То що ж робить безкоштовно?
Безкоштовно поставить блок пам'яті у свій власний список блоків. Зазвичай він також намагається з'єднати сусідні блоки в адресному просторі. Список безкоштовних блоків - це лише круговий список фрагментів пам’яті, які мають деякі адміністративні дані на початку. Це також причина, чому керування дуже маленькими елементами пам'яті зі стандартним malloc / free не є ефективним. Кожен фрагмент пам'яті потребує додаткових даних, і при менших розмірах відбувається більше фрагментації.
Безкоштовний список - це також перше місце, на яке дивиться малок, коли потрібен новий шматок пам'яті. Він сканується перед тим, як викликати нову пам'ять з ОС. Коли знайдеться шматок, який більший, ніж потрібна пам'ять, його ділять на дві частини. Один повертається абоненту, інший знову повертається у вільний список.
Існує багато різних оптимізацій щодо цієї стандартної поведінки (наприклад, для невеликих шматочків пам'яті). Але оскільки malloc і free повинні бути настільки універсальними, стандартна поведінка завжди є резервною, коли альтернативи не використовуються. Існують також оптимізації в обробці безкоштовного списку - наприклад, зберігання фрагментів у списках, відсортованих за розмірами. Але всі оптимізації також мають свої обмеження.
Чому ваш код виходить з ладу:
Причина полягає в тому, що, записуючи 9 символів (не забудьте остаточний нульовий байт) в область розміром на 4 символи, ви, ймовірно, перезаписуєте адміністративні дані, що зберігаються для іншого фрагмента пам'яті, який знаходиться "позаду" вашої частини даних ( оскільки ці дані найчастіше зберігаються "перед" фрагментами пам'яті). Коли безкоштовно, то намагається помістити ваш фрагмент у вільний список, він може торкнутися цих адміністративних даних і тому наткнутися на перезаписаний покажчик. Це призведе до збою системи.
Це досить витончена поведінка. Я також бачив ситуації, коли втікаючий вказівник десь перезаписав дані у вільний від пам'яті список, а система не одразу вийшла з ладу, а деякі підпрограми пізніше. Навіть у системі середньої складності такі проблеми можуть бути справді, дуже важко відладкувати! В одному випадку, в якому я брав участь, нам (більшій групі розробників) було потрібно кілька днів, щоб знайти причину аварії - оскільки він знаходився в зовсім іншому місці, ніж той, який вказано на дампах пам'яті. Це як бомба з часом. Ви знаєте, ваш наступний "вільний" або "малок" вийде з ладу, але ви не знаєте чому!
Це одні з найгірших проблем C / C ++, і одна з причин, чому вказівники можуть бути настільки проблематичними.