You are viewing an old revision of this post, from January 24, 2024 @ 17:20:29. See below for differences between this version and the current revision.
Este post faz parte de uma série sobre como implementar as funções malloc()
e free()
. Anteriormente, fizemos uma implementação são simplória que praticamente não libera memória nenhuma: um ponteiro aponta para o último bloco alocado, o que permite que abfree()
o desaloque, mas somente ele.
Uma opção melhor é fazer o último bloco apontar para o penúltimo, o penúltimo para o antepenúltimo e assim por diante, formando uma lista ligada. Para isso, criamos uma struct que servirá como cabeçalho dos blocos, contendo um ponteiro para o bloco anterior:
typedef struct Header { struct Header *previous; } Header;
Além disso, o ponteiro para o último bloco, que era do tipo void*
, agora é do tipo Header*
:
Header *last = NULL;
Para usar esses cabeçalhos, abmalloc()
reserva memória suficiente para armazenar tanto o cabeçalho quanto o tamanho solicitado:
void *abmalloc(size_t size) {
Header *header = sbrk(sizeof(Header) + size);
Deste modo, utilizamos o início do bloco para armazenar informações necessárias, como um ponteiro para o último bloco alocado antes do novo:
header->previous = last;
Em seguida, atualizamos last
para apontar para o novo bloco:
last = header;
Por fim, retornamos um ponteiro para a memória que o usuário pode utilizar. Como header
aponta para os metadados, não podemos simplesmente retorná-lo. Caso contrário, toda informação do cabeçalho seria sobrescrita quando o usuário usasse o ponteiro! Ao invés disso, retornamos um ponteiro para logo depois do cabeçalho. Este ponteiro é fácil de calcular: é o endereço de memória do cabeçalho mais o tamanho do cabeçalho:
return header + 1;
}
Note como incrementamos o ponteiro header em 1. Como o tipo do ponteiro é Header*
, o incremento será, na verdade, o número de bytes da struct Header
, não um byte apenas. Por isso, o tipo do ponteiro incrementado é muito relevante na aritmética de ponteiros.
Agora que nossos blocos de memória possuem metadados no início, é preciso levar isso em conta na hora de desalocar. free()
recebe um ponteiro não para o começo do bloco, mas para a memória disponibilizada ao usuário. Logo, precisamos encontrar o começo do bloco a partir do ponteiro que o usuário passou. Nada que uma pequena aritmética de ponteiros não resolva:
void abfree(void *ptr) { Header *header = (Header*) ptr - 1;
Se header
aponta para o último bloco alocado, o bloco anterior passará a ser o último. Neste caso, podemos retornar memória do heap para o sistema operacional através de brk()
:
if (header == last) {
last = header->previous;
brk(header);
}
Eis a nova versão de nossas funções malloc()
e free()
:
typedef struct Header { struct Header *previous; } Header; Header *last = NULL; void *abmalloc(size_t size) { Header *header = sbrk(sizeof(Header) + size); header->previous = last; last = header; return header + 1; } void abfree(void *ptr) { Header *header = (Header*) ptr - 1; if (header == last) { last = header->previous; brk(header); } }
abmalloc()
e abfree()
podem ser ligeiramente mais econômicos em memória agora, mas não muito. Raramente a memória alocada dinamicamente se comporta com uma pilha, onde o bloco mais velho é sempre desalocado primeiro. No próximo post, veremos como utilizar a memória de blocos mais antigos que não estão mais em uso.
Post Revisions:
- March 17, 2025 @ 18:22:38 [Current Revision] by brandizzi
- March 17, 2025 @ 18:22:38 by brandizzi
- January 24, 2024 @ 17:20:29 by brandizzi
Changes:
January 24, 2024 @ 17:20:29 | Current Revision | ||
---|---|---|---|
Content | |||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Este post faz parte de uma série sobre como implementar as funções <code>malloc()</code> e <code>free()</code>. Anteriormente, <a href="https:/ /suspensao.blog.br/descrenca/ malloc-free-1/">fizemos uma implementação são simplória</a> que praticamente não libera memória nenhuma: um ponteiro aponta para o último bloco alocado, o que permite que ab<code>free()</code> o desaloque, mas somente ele.</p> | Unchanged: <p>Este post faz parte de uma série sobre como implementar as funções <code>malloc()</code> e <code>free()</code>. Anteriormente, <a href="https:/ /suspensao.blog.br/descrenca/ malloc-free-1/">fizemos uma implementação são simplória</a> que praticamente não libera memória nenhuma: um ponteiro aponta para o último bloco alocado, o que permite que ab<code>free()</code> o desaloque, mas somente ele.</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Uma opção melhor é fazer o último bloco apontar para o penúltimo, o penúltimo para o antepenúltimo e assim por diante, formando uma <a href="https:/ /pt.wikipedia.org/wiki/Lista_ ligada">lista ligada</a>. Para isso, criamos uma struct que servirá como cabeçalho dos blocos, contendo um ponteiro para o bloco anterior:</p> | Unchanged: <p>Uma opção melhor é fazer o último bloco apontar para o penúltimo, o penúltimo para o antepenúltimo e assim por diante, formando uma <a href="https:/ /pt.wikipedia.org/wiki/Lista_ ligada">lista ligada</a>. Para isso, criamos uma struct que servirá como cabeçalho dos blocos, contendo um ponteiro para o bloco anterior:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted">typedef struct Header { | Unchanged: <pre class="wp-block- preformatted">typedef struct Header { | ||
Unchanged: struct Header *previous; | Unchanged: struct Header *previous; | ||
Unchanged: } Header;</pre> | Unchanged: } Header;</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Além disso, o ponteiro para o último bloco, que era do tipo <code>void*</code>, agora é do tipo <code>Header*</code>:</p> | Unchanged: <p>Além disso, o ponteiro para o último bloco, que era do tipo <code>void*</code>, agora é do tipo <code>Header*</code>:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted">Header *last = NULL;</pre> | Unchanged: <pre class="wp-block- preformatted">Header *last = NULL;</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Para usar esses cabeçalhos, <code>abmalloc()</code> reserva memória suficiente para armazenar tanto o cabeçalho quanto o tamanho solicitado:</p> | Unchanged: <p>Para usar esses cabeçalhos, <code>abmalloc()</code> reserva memória suficiente para armazenar tanto o cabeçalho quanto o tamanho solicitado:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted">void *abmalloc(size_t size) {<br> Header *header = sbrk(sizeof(Header) + size);</pre> | Unchanged: <pre class="wp-block- preformatted">void *abmalloc(size_t size) {<br> Header *header = sbrk(sizeof(Header) + size);</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Deste modo, utilizamos o início do bloco para armazenar informações necessárias, como um ponteiro para o último bloco alocado antes do novo:</p> | Unchanged: <p>Deste modo, utilizamos o início do bloco para armazenar informações necessárias, como um ponteiro para o último bloco alocado antes do novo:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted"> header->previous = last;</pre> | Unchanged: <pre class="wp-block- preformatted"> header->previous = last;</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Em seguida, atualizamos <code>last</code> para apontar para o novo bloco:</p> | Unchanged: <p>Em seguida, atualizamos <code>last</code> para apontar para o novo bloco:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted"> last = header;</pre> | Unchanged: <pre class="wp-block- preformatted"> last = header;</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Por fim, retornamos um ponteiro para a memória que o usuário pode utilizar. Como <code>header</code> aponta para os metadados, não podemos simplesmente retorná-lo. Caso contrário, toda informação do cabeçalho seria sobrescrita quando o usuário usasse o ponteiro! Ao invés disso, retornamos um ponteiro para logo depois do cabeçalho. Este ponteiro é fácil de calcular: é o endereço de memória do cabeçalho <em>mais</em> o tamanho do cabeçalho:</p> | Unchanged: <p>Por fim, retornamos um ponteiro para a memória que o usuário pode utilizar. Como <code>header</code> aponta para os metadados, não podemos simplesmente retorná-lo. Caso contrário, toda informação do cabeçalho seria sobrescrita quando o usuário usasse o ponteiro! Ao invés disso, retornamos um ponteiro para logo depois do cabeçalho. Este ponteiro é fácil de calcular: é o endereço de memória do cabeçalho <em>mais</em> o tamanho do cabeçalho:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted"> return header + 1;<br>}</pre> | Unchanged: <pre class="wp-block- preformatted"> return header + 1;<br>}</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Note como incrementamos o ponteiro header em 1. Como o tipo do ponteiro é <code>Header*</code>, o incremento será, na verdade, o número de bytes da struct <code>Header</code>, não um byte apenas. Por isso, o tipo do ponteiro incrementado é muito relevante na <a href="https:/ /www.ime.usp.br/ ~pf/algoritmos/ aulas/pont.html#array" >aritmética de ponteiros</a>.</p> | Unchanged: <p>Note como incrementamos o ponteiro header em 1. Como o tipo do ponteiro é <code>Header*</code>, o incremento será, na verdade, o número de bytes da struct <code>Header</code>, não um byte apenas. Por isso, o tipo do ponteiro incrementado é muito relevante na <a href="https:/ /www.ime.usp.br/ ~pf/algoritmos/ aulas/pont.html#array" >aritmética de ponteiros</a>.</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Agora que nossos blocos de memória possuem metadados no início, é preciso levar isso em conta na hora de desalocar. <code>free()</code> recebe um ponteiro não para o começo do bloco, mas para a memória disponibilizada ao usuário. Logo, precisamos encontrar o começo do bloco a partir do ponteiro que o usuário passou. Nada que uma pequena aritmética de ponteiros não resolva:</p> | Unchanged: <p>Agora que nossos blocos de memória possuem metadados no início, é preciso levar isso em conta na hora de desalocar. <code>free()</code> recebe um ponteiro não para o começo do bloco, mas para a memória disponibilizada ao usuário. Logo, precisamos encontrar o começo do bloco a partir do ponteiro que o usuário passou. Nada que uma pequena aritmética de ponteiros não resolva:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted">void abfree(void *ptr) { | Unchanged: <pre class="wp-block- preformatted">void abfree(void *ptr) { | ||
Unchanged: Header *header = (Header*) ptr - 1;</pre> | Unchanged: Header *header = (Header*) ptr - 1;</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Se <code>header</code> aponta para o último bloco alocado, o bloco anterior passará a ser o último. Neste caso, podemos retornar memória do heap para o sistema operacional através de <code>brk()</code>:</p> | Unchanged: <p>Se <code>header</code> aponta para o último bloco alocado, o bloco anterior passará a ser o último. Neste caso, podemos retornar memória do heap para o sistema operacional através de <code>brk()</code>:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted"> if (header == last) {<br> last = header->previous;<br> brk(header);<br> }</pre> | Unchanged: <pre class="wp-block- preformatted"> if (header == last) {<br> last = header->previous;<br> brk(header);<br> }</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Unchanged: <p>Eis a nova versão de nossas funções <code>malloc()</code> e <code>free()</code>:</p> | Unchanged: <p>Eis a nova versão de nossas funções <code>malloc()</code> e <code>free()</code>:</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> | ||
Unchanged: <!-- wp:preformatted --> | Unchanged: <!-- wp:preformatted --> | ||
Unchanged: <pre class="wp-block- preformatted">typedef struct Header { | Unchanged: <pre class="wp-block- preformatted">typedef struct Header { | ||
Unchanged: struct Header *previous; | Unchanged: struct Header *previous; | ||
Unchanged: } Header; | Unchanged: } Header; | ||
Unchanged: Header *last = NULL; | Unchanged: Header *last = NULL; | ||
Unchanged: void *abmalloc(size_t size) { | Unchanged: void *abmalloc(size_t size) { | ||
Unchanged: Header *header = sbrk(sizeof(Header) + size); | Unchanged: Header *header = sbrk(sizeof(Header) + size); | ||
Unchanged: header->previous = last; | Unchanged: header->previous = last; | ||
Unchanged: last = header; | Unchanged: last = header; | ||
Unchanged: return header + 1; | Unchanged: return header + 1; | ||
Unchanged: } | Unchanged: } | ||
Unchanged: void abfree(void *ptr) { | Unchanged: void abfree(void *ptr) { | ||
Unchanged: Header *header = (Header*) ptr - 1; | Unchanged: Header *header = (Header*) ptr - 1; | ||
Unchanged: if (header == last) { | Unchanged: if (header == last) { | ||
Unchanged: last = header->previous; | Unchanged: last = header->previous; | ||
Unchanged: brk(header); | Unchanged: brk(header); | ||
Unchanged: } | Unchanged: } | ||
Unchanged: }</pre> | Unchanged: }</pre> | ||
Unchanged: <!-- /wp:preformatted --> | Unchanged: <!-- /wp:preformatted --> | ||
Unchanged: <!-- wp:paragraph --> | Unchanged: <!-- wp:paragraph --> | ||
Deleted: <p><code>abmalloc()</code> e a<code>bfree()</code> podem ser ligeiramente mais econômicos em memória agora, mas não muito. Raramente a memória alocada dinamicamente se comporta com uma pilha, onde o bloco mais velho é sempre desalocado primeiro. No próximo post, veremos como utilizar a memória de blocos mais antigos que não estão mais em uso.</p> | Added: <p><code>abmalloc()</code> e a<code>bfree()</code> podem ser ligeiramente mais econômicos em memória agora, mas não muito. Raramente a memória alocada dinamicamente se comporta com uma pilha, onde o bloco mais velho é sempre desalocado primeiro. No <a href="https:/ /suspensao.blog.br/descrenca/ implementando- malloc-e-free- reutilizando- blocos-de-memoria/">próximo post</a>, veremos como utilizar a memória de blocos mais antigos que não estão mais em uso.</p> | ||
Unchanged: <!-- /wp:paragraph --> | Unchanged: <!-- /wp:paragraph --> |
Note: Spaces may be added to comparison text to allow better line wrapping.