Alocação dinâmica de memória em C

Esta artigo objetiva mostrar com se faz alocação dinâmica de memória para programas em C (utilizando o malloc). A alocação dinâmica é um recurso muito utilizado, em especial para as linguagens de alto nível.

[ Hits: 53.178 ]

Por: Carlos Roberto S. Junior em 18/04/2008


Introdução

A alocação dinâmica de memória trata-se de pegar um espaço na memória maior do que era esperado, ou simplesmente pegar um espaço quando não é possível prever. Há diversas formas de se fazer isso. Em C usamos uma função malloc(); já em C++ utilizamos uma palavra reservada "new".

A desvantagem da alocação dinâmica está no rompimento da pilha de memória, o que causaria o fechamento do programa e também, com a alocação dinâmica, o programa torna-se mais lento, pois ela requer muito do processamento.

Alocação dinâmica em C

Os compiladores mais antigos (anterior a 1998) não aceitavam este trecho de código:

#include <stdio.h>
/*include*/

int main(void)
{
       unsigned short int valor;

       printf("\nDigite o tamanho da string: ");
       scanf("%d",&valor);

       char string[valor];

       printf("\nDigite a string: ");
       scanf("%s",string);
       printf("%s",string");

       return 0;
}

Este código nos novos compiladores simplesmente gerará uma aviso. Mas o programa será executado normalmente. É um tipo simples do alocação dinâmica, porém há funções que fazem isso um pouco melhor.

Utilizando o malloc(), a função está contida na biblioteca malloc.h e possui a seguinte sintaxe:

tipoDoPonteiro= malloc(valorParaAlocação * tamanhoDoTipo);

Por exemplo:

#include <stdio.h>
#include <malloc.h>
/*includes*/

int main(void)
{
       unsigned short int tamanho;
       char *string;     /*ponteiro para char, é necessário que seja uma ponteiro para ser alocado*/

       printf("\nDigite o tamanho da string: ");
       scanf("%d",&tamanho);

       string= malloc( tamanho * sizeof(char) ); /*o sizeof ajuda a aumentar a portabilidade*/
      
       printf("\nDigite a string: ");
       scanf("%s",string);
       printf("\n%s",string);

       free(string);    /*libera a memória alocada*/

       return 0;
}

A utilização do free() é altamente recomendada, pois ele devolve a memória alocada para o sistema, alguns sistemas operacionais fazem isso automaticamente quando o programa fecha, porém se ocorrer um erro você pode ficar com uma parte da memória sem utilização, tanto pelo sistema quanto pelos programas que você utiliza.

    Próxima página

Páginas do artigo
   1. Introdução
   2. Alocação dinâmica em C - alocando uma matriz bidimensional
Outros artigos deste autor

Estudando recursividade direta e indireta

Leitura recomendada

Compilando Templates C++

Cuidado com números em Ponto Flutuante

SDL e C - Uma dupla sensacional

Algum humor e C++ Design Patterns (parte 1)

SDL - Ótimo para criação de jogos

Comentários
[1] Comentário enviado por elgio em 18/04/2008 - 10:00h

Oi. legal esta tua iniciativa de se escrever um artigo sobre malloc, mas veja que algumas de suas afirmações, mesmo que corretas, podem induzir ao erro se o inexperiente programador não souber contextualizá-las (e o experiente não precisaria ler este artigo ;-)

Primeiro, quando se programa em C ANSI deve-se cuidar do respeito aos padrões ANSI. A forma de alocação que sugeriste de alocar um espaço em relação à uma variável não funciona em todos os casos, logo para que se arriscar a usar:


printf("\nDigite o tamanho da string: ");
scanf("%d",&valor);

char string[valor];

E se o cara digitar -1 para valor? (-1 será interpretado como 2.147.483.647, ou seja, alocaria 2G, aproximadamente!!!). Induz ao erro. Eu prefiro JAMAIS, em HIPÓTESE ALGUMA ensinar estas coisas para quem está iniciando.

Outra indução ao erro é que SEMPRE que se faz malloc deve-se testar o seu retorno. Pode ser que tu não consigas alocar a memória que solicitou:

printf("\nDigite o tamanho da string: ");
scanf("%d",&tamanho);

string= malloc( tamanho * sizeof(char) ); /*o sizeof ajuda a aumentar a portabilidade*/

printf("\nDigite a string: ");
scanf("%s",string);
(e poderiamos ainda discutir o problema de buffer overflow que o scanf permite. Eu ensinaria direto o fgets)

E se DENOVO, o usuário digitar -1? Ou mesmo 1000000 e não se tem memória? Veja que o programa continua e vais escrever em uma área NÃO ALOCADA o que é GRAVÍSSIMO! Sempre, sempre, sempre, sempre se deve testar se o malloc funcionou:

printf("\nDigite o tamanho da string: ");
scanf("%d",&tamanho);

string= malloc( tamanho * sizeof(char) );

if (string == NULL){
fprintf(stderr, "ERRO NO MALLOC\n");
return(1);
}

printf("\nDigite a string: ");
fgets(string, tamanho, stdin);

[2] Comentário enviado por removido em 18/04/2008 - 10:04h

Para gerar matrizes dinamicamente, talvez seja mais indicado o uso de calloc:

void *calloc(size_t nmemb, size_t size);

Que está definido em stdlib.h. Nele você especifica quantos membros terá na sua matriz e qual o tamanho deles.

[3] Comentário enviado por elgio em 18/04/2008 - 10:06h

calloc é praticamente um define:

calloc (3,4) == malloc(3*4)

[4] Comentário enviado por elgio em 18/04/2008 - 10:09h

"A desvantagem da alocação dinâmica está no rompimento da pilha de memória, o que causaria o fechamento do programa e também, com a alocação dinâmica, o programa torna-se mais lento, pois ela requer muito do processamento. "


Como assim?
rompimento de pilha? malloc não usa a pilha para alocação e este rompimento e fechamento de um programa é somente se não conseguiu alocar e o programador não testou. Usado de forma correta não há nenhum rompimento e nenhum fechamento de programa!

E mais lento?
Nada disso.
Depois de alocado o acesso é na mesma velocidade do que vetores estáticos. A lentidão existe no momento de alocar, pois a alocação é uma tarefa onerosa (que pode envolver até - PASMEM - defragmentação de memória).

Abraços.

[5] Comentário enviado por ulissescosta em 18/04/2008 - 19:47h

scanf("%d",&valor);
char string[valor];

Isto não é alocação dinâmica de memória!
No máximo podes alocar 2GB que é o tamanho máximo da stack relativa a variáveis.

[6] Comentário enviado por maran em 20/04/2008 - 14:54h

caramba, olha os cara meu, aula de programação....
Isso ae show de bola o artigo e os comentarios

[7] Comentário enviado por FelipeAbella em 24/04/2008 - 19:29h

calloc(a,b) é igual a malloc (a*b)
com exceção de que calloc zera a memória alocada.

É possível evitar um buffer overflow no scanf:

char str[16];
scanf("%15s", str);

Boa iniciativa.

[8] Comentário enviado por elgio em 24/04/2008 - 19:51h

calloc: Correto!
Eu prefiro não perder este tempo extra com esta inicialização

scanf: Correto também, mas o fgets é muito mais intuitivo.

Com fgets o tamanho pode ser um #define ou mesmo uma variável:

#define MAX 200
...
fgets(str, MAX, stdin);

Porque isto NÃO FUNCIONA:

scanf("%MAXs", str);

Não funciona até porque teria que ser MAX-1 (para o fgets tu passa o tamanho do buffer e ele já sabe que precisa de um para o 0. Já no scanf é quantos cars e ele não reserva 1 para o zero.)

O scanf permite inclusive livrar-se do inconveniente de não ler espaços em branco:

scanf("%[^\n]s", str);

Mas ai já é coisa par aum artigo avançado (poderes ocultos do scanf...)

[9] Comentário enviado por odavai em 28/04/2008 - 08:50h

gostei vou utilizalo depois enviarei se deu tudo certo nos meus estudos de c e c++

[10] Comentário enviado por gdm.arc em 31/10/2008 - 12:47h

Senhores(as)
Saudações
Gostaria de dizer que o código está com alguns problemas, segue abaixo o código modificado visto não ter funcionado o inicial apresentado no presente artigo, aliás muito interessante.

Os seguintes pontos de erro foram encontrados:
1) tipo da variável de entrada via scanf não era compativel com unsigned short int, visto estar retornando sempre zero ao invés do valor digitado.
2) a sintaxe do malloc() omitia o casting. A função malloc() retorna um ponteiro 'void', que precisa ser 'moldado' ao ser atribuido.
3) atribuição do valor ao invés do endereço para a variável "matriz" na chamada ao malloc() na primeira dimensão.
4) comentário incorreto na chamada da função free() para a segunda dimensão.
5) o sizeof() deve ser usado para o ponteiro e não para o valor apontado pelo ponteiro

Agradecido.
Parabéns pela iniciativa

gdm.arc

#include <stdio.h>
#include <stdlib.h>
/*includes*/

int main(void)
{
int linhas, colunas, i; /* (1) */
int **matriz;

printf("\nDigite o número de linhas e colunas: ");
scanf("%d,%d",&linhas, &colunas);
matriz= (int **) malloc( linhas * sizeof( int *) ); /* (2),(3),(5) */
for(i=0;i<linhas;i++)
matriz[i]= (int *) malloc( colunas * sizeof( int ) ); /* (2) */

/*a partir deste ponto pode ser usado matriz[a][b] sendo a o número da linha e b o número da coluna para qualquer parte do programa*/

for(i=0; i<linhas; i++)
free(matriz[i]); /* (4) */
free(matriz); /*faz a liberação da memória alocada*/

return 0;
}

[11] Comentário enviado por inacioalves em 29/10/2009 - 23:16h

Reativando o tópico.

1. Me corrijam se eu estiver errado, mas pelo menos em C ANSI basta incluir a biblioteca stdlib.h para podermos usar malloc e sizeof.

2. Não se faz necessário trabalhar com ponteiros de ponteiros para usarmos "matrizes".
Considerem o código abaixo:

#include<stdio.h>
#include<stdlib.h>
int main(){
int i,j, l,c;
float *m;
scanf("%d%d",&l,&c);
m = malloc( l*c*sizeof(float) );
for( i=0; i<l; i++ )
for(j=0;j<c;j++ )
scanf("%f",m+(i*c+j) );

for( i=0; i<l; i++ ){
for(j=0;j<c;j++ )
printf("%8.2f",*(m+i*c+j) );
printf("\n");
}

return 0;
}

Ele trata muito bem um vetor como se este fosse uma matriz ;)

[12] Comentário enviado por ElisMacedo em 28/10/2011 - 22:21h

// Isso é um trecho de um programa,coloquei somente a parte que usamos para alocar dinamicamicana a quantidade de filas e a quantidade de cadeiras da primeira fileira de um teatro.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//DEFINIÇÕES DAS VARIAVEIS
#define MAX 26 //Numero máximo de filas
#define CASE_1 1 // Vender cadeira isolada
#define CASE_2 2 // Vender lote de cadeiras
#define CASE_3 3 //Consultar cadeiras com folga
#define CASE_4 4 // consultar cadeiras com vista livre
#define CASE_5 5 //Fechar bilheteria
#define CASE_6 6 //Mostrar ocupação total do teatro
#define CASE_7 7 //Sair do programa

//Protótipo das funções

int ** cria_fileiras_int(int , int); //Função para criar a matriz de alocação para criar as filas e cadeiras do teatro
void vender_cadeira(int **, float , int , int, char[]); // função para vender cadeiras isoladas
void vender_lote(int **, float , int , int, char[]); //Função pra vender cadeiras por lote
void teatro_total(int ** ,int ,int , char[]); //Função para exibir a matriz das cadeiras com folga
void vista_livre(int ** ,int ,int , char[]); //Funçao para exibir as caderias com vista livre
void fechar_bilheteria(int **, int , int , float, char[]); //Função para calcular o faturamento total da bilheteria, taxa de ocupação total e por fila
void ocupacao(int ** ,int ,int , char[]); //Função que mostra o teatro

//Início do prgrama
int main(){

int escolha; // guarda a opção do menu
int n; // guarda quantidade de fila
int c; // guarda a quantidade de cadeiras
int ** teatro; //vetor de ponteiro que aponta para a matriz teatro
float preco = 0;// guarda o preço

char * escolhas[] = {"Vender uma cadeira isolada",
"Vender um lote de cadeiras",
"Consultar cadeiras com folga",
"Consultar cadeiras com vista livre",
"Fechar bilheteria",
"Mostrar a ocupacao total do Teatro",
"Finalizar programa"};

char fileira[] = {'A','B','C','D','E',
'F','G','H','I','J',
'K','L','M','N','O',
'P','Q','R','S','T',
'U','W ','X','Y','Z'};


printf("Insira a quantidade de fileiras totais: "); //Solicita ao usuário que digite a quantidade de filas e cadeiras da primeira fila
scanf("%d", &n);
printf("Insira a quantidade de cadeiras da 1a fileira: ");
scanf("%d", &c);


while(n<2 || n>MAX){ // Verifica os parâmetros digitados
printf("Minimo de 2 e maximo de 26 fileiras no teatro!!\n");
printf("Insira a qtde de fileiras totais e cadeiras da 1a fileira): ");
scanf("%d %d",&n,&c);
}

//Função para criar a matriz de alocaçao para criar as filas e cadeiras do teatro

int ** cria_fileiras_int(int n, int c) {
int ** mat, i;
if((mat = (int **) calloc(n,sizeof(int *))) == NULL) {
return NULL;
}

for(i = 0; i < n ; i++) {
if((mat[i] = (int *) calloc(c + 2 * i, sizeof(int))) == NULL) return NULL; // c+2*iacrescentar 2 cadeiras a cada nova fileira
}
return mat;
}


Contribuir com comentário