Странное поведение malloc () - PullRequest
       14

Странное поведение malloc ()

Ответы [ 7 ]

8 голосов
/ 25 февраля 2010

Вы копируете больше байтов в *p, чем выделено, перезаписывая все, что могло быть в ячейках памяти после выделенного пространства.

Когда вы снова вызываете malloc, он забирает часть памяти, которая, как он знает, не используется в данный момент (которая на этот раз составляет несколько байтов после *p), записывает туда некоторую бухгалтерскую информацию и возвращает новый указатель на это местоположение.

Учетная информация malloc записи начинается с '!' в этом цикле следует нулевой байт, поэтому ваша первая строка усекается. Новый указатель указывает на конец памяти, которую вы перезаписали ранее.

Все это зависит от конкретной реализации и может привести к различным результатам при каждом запуске или в зависимости от фазы луны. Второй вызов malloc() также может привести к аварийному завершению программы ужасным образом (особенно если учесть, что вы перезаписываете память, которую malloc использует для внутреннего использования).

7 голосов
/ 25 февраля 2010

На этот раз вам просто везет: это неопределенное поведение и не рассчитывайте на него.

Обычно, но в зависимости от ОС память выделяется в «страницах» (то есть в нескольких байтах). Malloc(), с другой стороны, выделяет память из этих «страниц» более «детально»: есть «издержки», связанные с каждым распределением, управляемым через malloc.

Сигнал, который вы получаете от free, скорее всего, связан с тем фактом, что вы запутали управление памятью, записав после того, что вам выделено p, т.е. записав служебную информацию, используемую менеджером памяти для дорожка блоков памяти и т. д.

4 голосов
/ 25 февраля 2010

Это классическое переполнение кучи. У p есть только 1 байт, но менеджер кучи дополняет выделение (32 байта в вашем случае). q назначается сразу после p, поэтому он, естественно, получает следующее доступное место. Например, если адрес p равен 0x1000, адрес, который назначается для q, равен 0x1020. Это объясняет, почему q указывает на часть строки.

Более интересный вопрос, почему p - это только "01234556789abcdefghijklm", а не "01234556789abcdefghijklmnopqrstuvwxyz" Причина в том, что диспетчер памяти использует промежутки между выделением для своей внутренней бухгалтерии. С точки зрения диспетчера памяти схема памяти выглядит следующим образом: p D q где D - внутренняя структура данных диспетчера памяти (в нашем примере от 0x1010 до 0x1020). При выделении памяти для q менеджер кучи записывает свои данные в область учета (от 0x1010 до 0x1020). Байт изменен на 0, обрезает строку, поскольку он обрабатывается как терминатор NULL.

1 голос
/ 25 февраля 2010

ЗНАЧЕНИЕ "p":

Вы выделили достаточно места, чтобы вместить это: ""

[[строки заканчиваются нулем, помните?Вы не видите это, но оно там - так что это один использованный байт.]]

но вы пытаетесь сохранить это: "01234556789abcdefghijklmnopqrstuvwxyz"

в результате получается, что "материал", начинающийся с "123 ..", сохраняется вне памяти, которую вывыделено - возможно, писать поверх других "вещей" в другом месте.как таковые, ваши результаты будут грязными, и, как сказал «jidupont», вам повезло, что это не просто сбой.

ВЫВОД ПЕЧАТИ [СДЕЛАНО] «p»

как сказано,вы написали далеко за концом «р»;но Маллок этого не знает.поэтому, когда вы попросили другой блок памяти для «q», возможно, он дал вам память, следующую за тем, что он дал вам для «p»;и, возможно, он выровнял память (типично), чтобы его указатель был округлен до некоторого приятного числа;а затем, возможно, он использует часть этой памяти для хранения бухгалтерской информации, которая вас не касается.но ты не знаешь, не так ли?вы тоже не должны знать - вы просто не должны писать в память, которую вы сами не выделили!

и результат?Вы видите кое-что из того, что ожидали - но оно обрезано!потому что ... возможно, был выделен другой блок в используемой памяти (и я мог бы добавить без разрешения), или что-то еще принадлежало этому блоку и изменило его, и в любом случае некоторые значения были изменены - в результате: "01234556789abcdefghijklm!».опять же, повезло, что вещи не просто взорвались.

СВОБОДА "q"

если вы освободите "q", то попробуйте получить к нему доступ - как вы делаете, пытаясь распечатать его- вы (обычно) получите неприятную ошибку.это вполне заслуженно.Вы не должны раскомментировать это "free (q)".но вы также не должны пытаться напечатать «д», потому что вы еще ничего не положили!насколько вы знаете, он может содержать бред, и поэтому печать будет продолжаться до тех пор, пока он не встретит NULL - что может произойти не до конца света - или, что более вероятно, до тех пор, пока ваша программа не получит доступ к еще большему количеству памяти, чем должна.т, и вылетает, потому что ОС вас не устраивает.:)

0 голосов
/ 25 февраля 2010

Предлагаю прочитать это введение.

Указатели и память

Это помогло мне понять разницу между распределением стека и кучи, очень хорошее введение.

0 голосов
/ 25 февраля 2010

Malloc - это такая же функция, как ваша:)

Существует много реализаций malloc, поэтому я не буду вдаваться в бесполезные детали.

При первом вызове malloc запрашивает память у системы. Для примера скажем 4096, который является стандартным размером страницы памяти, что хорошо. Итак, вы вызываете malloc с просьбой о 1 байте. Функция malloc запросит 4096 байт для системы. Далее он будет использовать небольшую часть этой памяти для хранения внутренних данных, таких как позиции доступных блоков. Затем он отрежет одну часть этого блока и отправит вам обратно.

Внутренний алгоритм попытается повторно использовать блоки после вызова free, чтобы избежать повторного запроса памяти в системе.

Итак, с этим небольшим объяснением вы теперь можете понять, почему ваш код работает.

Вы пишете в памяти, спросил мой malloc в системе. Этот комплект не беспокоит систему, потому что вы остаетесь в памяти, выделенной для ваших процессов. Проблема в том, что вы не можете точно знать, что не пишете в критических частях памяти вашего программного обеспечения. Ошибки такого рода называются переполнением буфера и вызывают большинство "мистических ошибок".

Лучший способ избежать их - использовать valgrind в linux. Это программное обеспечение сообщит вам, пишете ли вы или читаете там, где не должны.

Это достаточно ясно?

0 голосов
/ 25 февраля 2010

Не должно быть удивительно, что преднамеренное неправильное использование этих функций даст бессмысленные результаты.

Два последовательных malloc не гарантируют вам две последовательные области памяти. malloc может выделить больше памяти, чем вы запрашивали, но не меньше, если выделение будет успешным. Поведение вашей программы при выборе перезаписи нераспределенной памяти не гарантируется как предсказуемое.

Это именно так, как есть С. Вы можете легко использовать возвращенные области памяти из malloc, а язык не волнует. Это просто предполагает, что в правильной программе вы никогда этого не сделаете, а все остальное можно получить.

...