ОК, некоторые ответы о malloc уже были опубликованы.
Более интересная часть - , как free работает (и в этом направлении malloc тоже можно понять лучше).
Во многих реализациях malloc / free free обычно не возвращает память операционной системе (или, по крайней мере, только в редких случаях). Причина в том, что вы получите пробелы в своей куче, и, таким образом, может случиться, что вы просто заделаете свои 2 или 4 ГБ виртуальной памяти пробелами. Этого следует избегать, поскольку, как только виртуальная память закончится, у вас будут действительно большие проблемы. Другая причина в том, что ОС может обрабатывать только фрагменты памяти, которые имеют определенный размер и выравнивание. Конкретно: обычно ОС может обрабатывать только блоки, которые может обрабатывать менеджер виртуальной памяти (чаще всего кратно 512 байтам, например 4 КБ).
Таким образом, возврат 40 байт в ОС просто не будет работать. Так что же делать бесплатно?
Свободный помещает блок памяти в свой собственный список свободных блоков. Обычно он также пытается объединить смежные блоки в адресном пространстве. Список свободных блоков - это просто циклический список фрагментов памяти, которые вначале содержат некоторые административные данные. Это также причина, по которой управление очень маленькими элементами памяти со стандартным malloc / free неэффективно. Каждый блок памяти требует дополнительных данных, а при меньших размерах происходит большая фрагментация.
Список свободных мест - это также первое место, на которое malloc смотрит, когда требуется новый кусок памяти. Он сканируется, прежде чем вызывает новую память из ОС. Когда найден фрагмент, который больше необходимой памяти, он разделяется на две части. Один возвращается вызывающей стороне, другой возвращается в свободный список.
Существует много различных оптимизаций для этого стандартного поведения (например, для небольших кусков памяти). Но поскольку malloc и free должны быть настолько универсальными, стандартное поведение всегда является запасным вариантом, когда альтернативы не используются. Есть также оптимизация в обработке свободного списка - например, сохранение кусков в списках, отсортированных по размерам. Но все оптимизации также имеют свои ограничения.
Почему происходит сбой кода:
Причина в том, что, записав 9 символов (не забывая завершающий нулевой байт) в область размером 4 символа, вы, вероятно, перезапишите административные данные, хранящиеся для другого фрагмента памяти, который находится "позади" вашего фрагмента. данных (поскольку эти данные чаще всего хранятся «перед» кусками памяти). Когда free затем пытается поместить ваш чанк в список free, он может коснуться этих административных данных и поэтому наткнуться на перезаписанный указатель. Это приведет к краху системы.
Это довольно изящное поведение. Я также видел ситуации, когда беглый указатель где-то перезаписывал данные в списке свободной памяти, и система не сразу падала, но некоторые подпрограммы позже. Даже в системе средней сложности такие проблемы могут быть действительно очень сложными для отладки! В одном случае, в котором я участвовал, нам (большей группе разработчиков) потребовалось несколько дней, чтобы найти причину сбоя - поскольку она находилась в совершенно другом месте, чем указано в дампе памяти. Это как бомба замедленного действия. Вы знаете, ваш следующий "free" или "malloc" потерпит крах, но вы не знаете почему!
Это одни из худших проблем C / C ++, и одна из причин, почему указатели могут быть такими проблемными.