Para lhe dar uma compreensão melhor de por que isso acontece, gostaria de expandir um pouco a resposta de @ r-samuel-klatchko.
Quando você liga malloc
, o que realmente está acontecendo é um pouco mais complicado do que apenas dar a você um pedaço de memória para brincar. Sob o capô, malloc
também mantém algumas informações de manutenção sobre a memória que forneceu (mais importante, seu tamanho), para que, quando você ligar free
, ele saiba coisas como quanta memória deve ser liberada. Essas informações são normalmente mantidas imediatamente antes do local da memória retornado a você por malloc
. Informações mais completas podem ser encontradas na internet ™ , mas a ideia (muito) básica é mais ou menos assim:
+------+-------------------------------------------------+
+ size | malloc'd memory +
+------+-------------------------------------------------+
^-- location in pointer returned by malloc
Com base nisso (e simplificando muito as coisas), quando você chama malloc
, ele precisa obter um ponteiro para a próxima parte da memória que está disponível. Uma maneira muito simples de fazer isso é examinar o bit de memória anterior que ele cedeu e mover os size
bytes para baixo (ou para cima) na memória. Com esta implementação, você acaba com sua memória parecida com isto após a alocação p1
, p2
e p3
:
+------+----------------+------+--------------------+------+----------+
+ size | | size | | size | +
+------+----------------+------+--------------------+------+----------+
^- p1 ^- p2 ^- p3
Então, o que está causando o seu erro?
Bem, imagine que seu código grava erroneamente além da quantidade de memória que você alocou (ou porque você alocou menos do que o necessário como era o seu problema ou porque está usando as condições de limite erradas em algum lugar do seu código). Diga seu código escreve tantos dados para p2
que ele começa a substituir o que está em p3
's size
campo. Na próxima chamada malloc
, ele irá olhar para a última localização de memória que retornou, olhar para seu campo de tamanho, mover para p3 + size
e então começar a alocar memória de lá. Como seu código foi sobrescrito size
, no entanto, esse local de memória não é mais posterior à memória alocada anteriormente.
Nem é preciso dizer que isso pode causar estragos! Os implementadores de malloc
, portanto, colocaram uma série de "asserções", ou verificações, que tentam fazer uma série de verificações de sanidade para detectar este (e outros problemas) se eles estiverem prestes a acontecer. No seu caso específico, essas asserções são violadas e, portanto malloc
, são canceladas, informando que seu código estava prestes a fazer algo que realmente não deveria estar fazendo.
Como afirmado anteriormente, esta é uma simplificação grosseira, mas é suficiente para ilustrar o ponto. A implementação glibc de malloc
tem mais de 5k linhas e tem havido uma quantidade substancial de pesquisas sobre como construir bons mecanismos de alocação de memória dinâmica, portanto, cobrir tudo em uma resposta SO não é possível. Espero que isso tenha lhe dado uma visão do que realmente está causando o problema!