sexta-feira, 6 de março de 2015

Alocação Dinâmica em C

Hoje vou escrever um pouco sobre alocação dinâmica em C, a maioria das vezes não é possivel determinar o tamanho de memoria que precisa ser alocado para armarzenar um determinado valor, é ai que entra a alocação dinâmica.

Malloc


void *malloc(size_t size);
Você informa a quantidade de bytes, malloc aloca esses bytes, e retorna um ponteiro genérico (void *), esse ponteiro genérico pode ser convertido para qualquer outro tipo de dado, char, int, pid_t, etc, caso ocorra erro na alocação dessa memoria, a função retorna NULL.

Sempre é necessário verificar se o valor retornado pelo malloc, ou outras funções de alocação dinâmica que serão mostradas aqui, é NULL, para evitar que o programa acesse um endereço na memoria que não existe, resultando em segmentation fault !!!
Alocação estática:
	char ptr[10];
Alocação dinâmica usando malloc:
	char *ptr = malloc( 10 * sizeof(char) );
Quando for alocar ponteiros do tipo char *, se quiser armazenar, por exemplo:
live long and prosper
Que contem 21 bytes, aloque um byte a mais, pra no final da ultima posição você colocar o byte 0, isso porque funções como strlen, strcat, strncat, printf, fprintf, etc, so deixam de percorrer o ponteiro quando o byte 0 é encontrado, se não tiver esse byte 0 ele vai percorrer espaço na memoria não alocado, ou valores não inicializados.
	char *llap = malloc( 22 * sizeof(char) );
	strcpy(llap, "live long and prosper");
strcpy coloca 0 no final do ponteiro, depois de copiar a string.

Free


void free(void *ptr);

Depois de alocar um ponteiro dinamicamente, antes de encerrar o programa você tem que liberar esse espaço alocado, para evitar memory leaks:
	char *llap = malloc( 22 * sizeof(char) );
	/* xxxxx */
	free(llap);

Calloc


void *calloc (size_t nmemb, size_t size);

Faz a mesma coisa que malloc, a diferença é que ele inicializa todos as posições alocadas do ponteiro com zero:
	char *llap = calloc(10, sizeof(char));

Realloc


void *realloc(void *ptr, size_t size);

Aloca mais espaço para um ponteiro. exemplo:
	int i=0;

	char *x = malloc( 10 * sizeof(char) );

	for(i=0; i<9; i++)
		x[i] = 'A';
	x[i] = 0x0;

	printf("x = '%s'\n",x);

	x = realloc(x, 20);

	for(;i<19; i++)
		x[i] = 'z';
	x[i] = 0x0;
	printf("x = '%s'\n",x);

	free(x);
[mmxm@hc0d3r ~]$ ./realloc_exemplo
x = 'AAAAAAAAA'
x= 'AAAAAAAAAzzzzzzzzzz'

Strdup


char *strdup(const char *s);
Copia uma string, e retorna um ponteiro char, que foi alocado usando malloc.
	char *lol = strdup("kamehameha");
	printf("%s\n",lol);
	free(lol);

Exemplos


Alocando um ponteiro de duas dimensões:
	int i,j;

	//primeiro devemos alocar o numero de elementos:
	// equivalente: char x[13][37]

	char **x = malloc( 13 * sizeof(char *) );

	//Agora alocamos memoria para os elementos: 

	for(i=0; i<13; i++)
		x[i] = malloc( 37 * sizeof(char) );

	// escrevendo nesses elementos:
	for(i=0; i<13; i++){
		for(j=0; j<36; j++){
			x[i][j] = 'A';
		}
		x[i][j] = 0x0;
	}

	for(i=0; i<13; i++)
		printf("x[%d] = '%s'\n",i, x[i]);

	// Para liberar a memoria primeiro devemos liberar os elementos do ponteiro:

	for(i=0; i<13; i++)
		free(x[i]);

	// e por ultimo o ponteiro:

	free(x);

Alocando uma estrutura:
	struct users {
		char *nome;
		int id;
	};


	//alocando espaço - equivalente: struct users userlist[10];
	struct users *userlist = malloc( 10 * sizeof( struct users) );


	// escrevendo
	for(i=0; i<10; i++){
		userlist[i].nome = strdup("gohan");
		userlist[i].id = i;
	}

	// print

	for(i=0; i<10; i++){
		printf("nome: %s\n", userlist[i].nome);
		printf("id: %d\n\n", userlist[i].id);
	}

	// liberando espaço

	for(i=0; i<10; i++)
		free(userlist[i].nome);

	free(userlist);

Funções


Ficar sempre tendo que verificar se o retorno de malloc, realloc, calloc, strdup, etc, é NULL, dependendo do tamanho do código fica cansativo, para facilitar isso você pode usar essas funções que verificam se o retorno é NULL, e caso seja abortam o programa.
void die(const char *err){
	perror(die);
	abort();
}

void *xmalloc(size_t len){
	void *ptr = malloc(len);

	if(ptr == NULL)
		die("malloc()");
 

	return ptr;
}

void *xrealloc(void *ptr, size_t len){
	void *tmp = realloc(ptr, len);

	if(tmp == NULL)
		die("realloc()");

	return tmp;
}

void *calloc(size_t n, size_t len){
	void *ptr = calloc(n, len);

	if(ptr == NULL)
		die("calloc()");

	return ptr;
}

char *xstrdup(const char *str){
	return strcpy( xmalloc( (strlen(str)+1) * sizeof(char) ), str);
}

Wild pointers


para evitar wild pointers (aka dangling pointer), depois de dar free eu um ponteiro, você precisa setar o valor do ponteiro para NULL:
char *x = malloc( sizeof(char) );
free(x);

/* x agora é um wild pointer */

x = NULL;

/* agora não é mais =D */
função
void xfree(void **ptr){
	if(ptr != NULL){ /* checa se ptr não é NULL, para evitar erro */
		free(*ptr);
		*ptr = NULL;
	}
}
modo de uso:
	xfree((void **)&ptr);

Valgrind


Por ultimo, mas não menos importante, para achar bugs no seu programa, como memory leaks, wild pointers, e outras coisas, use o valgrind, tool fodastica: http://valgrind.org/

2 comentários:

  1. legal seu post, parabéns... só adicionando algo
    Tem o alloca() , que é baseado em stack...http://man7.org/linux/man-pages/man3/alloca.3.html
    se tiver tempo saca ae https://coolerlab.wordpress.com/?s=ponteiros
    abr

    ResponderExcluir
    Respostas
    1. Verdade, esqueci de colocar, aprendi essas parada tudo contigo, vlw mesmo ahuahua

      Excluir