Buscar

Resumo_AlocaçãoDinâmica

Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original

Resumo Alocação Dinâmica:
***********************************************************************************
OBS: Essa matéria > só < é cobrada na P1, P2 e P3! :D
 
***********************************************************************************
- Definição:
Em termos bem simples: Alocação dinâmica é uma forma de "pedir emprestado" memória para o Pc através de uma função chamada >> malloc << que recebe como parâmetro a >> quantidade exata de memória << que você precisa e retorna um ponteiro genérico que "aponta" para o local onde está a memória emprestada.
Exemplo: Vamor criar um vetor que armazena 10 notas:
- Sem usar alocação dinâmica (Prog 1)
float notas[10];
for(i=0;i<10;i++){
 puts("Digite a nota: ");
 scanf("%f",&notas[i]);
} 
- Usando alocação dinâmica:
 
float * notas; 
notas = (float *) malloc (10 * sizeof(float));
if(!notas){
 puts("Erro! Sem memoria!");
 exit(1);
}
Primeiro declaramos um ponteiro para float. Depois chamamos a função malloc, passando a quantidade de memória que precisamos. (Em bytes)
OBS: A quantidade de bytes de um float pode variar de sistema pra sistema, então existe uma função chamada >> sizeof << que nos diz exatamente a quantidade de bytes de qualquer tipo!
E como precisamos armazenar 10 notas, são 10 valores do tipo float, certo? 
É só colocar 10 * sizeof( float), ou seja, 10 vezes o tamanho de um float!
OBS: Na historinha do começo, eu disse que a função malloc retorna um >> Ponteiro Genérico << né? É justamente pelo fato de ser genérico que precisamos converter ele pro formato que a gente precisa! (float *) malloc
OBS2: Lembra quando enviamos um vetor como parâmetro? 
EX: float calcula_total (float notas[], int n);
Uma outra forma de escrever esse parâmetro é: float * notas
Isso é possível porque quando enviamos qualquer vetor como parâmetro, na verdade não estamos enviando uma cópia do vetor e sim um ponteiro que "aponta" pro começo do vetor! (Primeira posição) 
OBS3: Se caso a função malloc não conseguir pegar a memória emprestada, ela vai retornar um ponteiro Nulo, então >> SEMPRE << precisamos colocar uma prevenção de erro! Pois se o vetor notas for NULL não faz o menor sentido continuar! Nesse caso temos que encerrar o programa inteiro ou no mínimo a função auxiliar que chamou a malloc! 
if(!notas){
 puts("Erro! Sem memoria!");
 exit(1);
}
OBS: Lembrando que if(!notas) é uma forma bem mais simples de escrever if(notas == NULL)
Para manipular o vetor é >>exatamente<< como se tivesse sido criado da forma tradicional!
for(i=0;i<10;i++){
 puts("Digite a nota: ");
 scanf("%f",&notas[i]);
} 
OBS: >>SEMPRE<< que terminar de usar algo alocado dinamicamente, >>NUNCA<< se esqueça de devolver a memória para o sistema!
free(notas);
 
Lembra que a memória era emprestada? :D
Pois é, eu sei que é triste, mas temos que devolver! :( 
A multa da correção é de aproximadamente -0.1 por esquecimento! Cuidado! 
-------------------------------------//------------------------------------------
- Mas por que não criar um vetor sempre da maneira tradicional? 
O método tradicional continua valendo, mas além do enunciado exigir o uso de alocação dinâmica, vamos pensar na seguinte situação:
Em todos os enunciados das provas em Prog 1 era dado a quantidade máxima de algo, notas por exemplo. Lembrando que ao declarar um vetor, é obrigatório especificar um tamanho máximo:
EX: float notas[10];
Mas e se for um usuário que digita essa quantidade durante a execução do programa? 
É exatamente nesses casos que é essencial usar alocação dinâmica!
---------------------------------------//-----------------------------------------
Exemplo 2: Enunciado pede pra criar uma função chamada Duplica que recebe como parâmetro uma string e que você aloque dinamicamente uma string, copie o conteúdo da string recebida e retorne o ponteiro para a nova string alocada:
char * Duplica (char *frase){
 int tam;
 char *nova;
 tam = strlen (frase);
 nova = (char *) malloc (tam + 1);
 if(!nova){
	 puts("Sem memoria!");
	 return NULL;
 }
 strcpy(nova, frase);
 return nova;
}
int main (void) {
 char frase[] = "Fritas eh um alimento saudavel";
 char *copia;
 
 copia = Duplica (frase);
 if (!copia) { 
 puts("Nao conseguiu duplicar a string!");
 exit(1);
 }
 printf("Original: %s\nCopia: %s\n", frase, copia);
 free(copia);
 return 0;
}
 
Primeiro vamos analisar o protótipo dessa função: 
char * Duplica (char *frase);
Ela recebe como parâmetro uma string, lembrando que um vetor é representado pelo endereço do primeiro elemento! Então escrever char *frase é >> o mesmo << que escrever char frase[], assim como em qualquer vetor!
E o retorno dela é char * , ou seja, ela retorna um ponteiro para char!
Apesar de chamar "Ponteiro para char", na verdade se trata de um String, pois como já relembramos, toda String é sempre representada por um ponteiro que "aponta" pro primeiro elemento, ou seja, um caracter!
OBS: >>SEMPRE<< que uma função tiver esse ou um tipo de retorno parecido (int *, float *, char *, etc), mesmo que o enunciado não especifique, será sempre alocação dinâmica! (Tirando os casos em que o enunciado diz para não usar alocação dinâmica)
Antes de usar a função malloc, precisamos declarar um ponteiro para char:
char *nova;
Lembra que a função malloc recebe a quantidade de bytes como parâmetro?
Mas como cada caracter (cada letra da string) possui exatamente >> um << byte, então o primeiro passo é armazenar a quantidade de caracteres (tamanho) através da função strlen! 
tam = strlen(frase);
nova = (char *) malloc (tam + 1);
OBS: Lembra que pra ser uma string, precisa >>obrigatoriamente<< terminar com '\0'? 
Por isso usamos tam + 1 que é justamente o espaço necessário para o '\0' !
Outra forma mais prática: nova = (char *) malloc (strlen(frase) + 1);
Logo em seguida vem a nossa prevenção de erro: 
if(!nova){
 puts("Sem memoria!");
 return NULL;
}
OBS: Percebe que dessa vez não usei a função exit pra encerrar o programa ? 
Se a função retorna um ponteiro para algo e a malloc não conseguiu a memória emprestada, o correto é retornar o valor NULL! Em outras palavras, a decisão de encerrar o programa deve ser sempre de quem chamou a função (a função main no caso) e nunca da função auxiliar!
Se não retornou NULL é porque deu tudo certo no empréstimo de memória. Então é só copiar o contéudo através da função strcpy e retornar esse novo ponteiro para string! 
Duas observações importantes sobre a main:
- Se chamamos uma função que retorna um ponteiro para algo, precisamos de um local para armazenar o retorno, certo? 
char *copia; 
copia = Duplica (frase);
Percebe que na main tratamos se o retorno da função foi NULL e > somente < nela que podemos encerrar o programa? 
if (!copia) { 
 puts("Nao conseguiu duplicar a string!");
 exit(1);
}
OBS: Não é que seja errado encerrar o programa na função auxiliar...Mas isso depende muito da correção de cada professor. Levando em conta que a mais boazinha de todas recomenda fazer assim, então melhor nem arriscar, né não? :)
E pra fechar com chave de ouro, nunca esqueça de devolver a memória emprestada!
free(copia);
OBS: Apesar da main não ter alocado dinamicamente a string, ela recebeu o endereço (ponteiro) da memória alocada e armazenou na variável "copia" né? Agora a variável "copia" também "aponta" para a memória que foi alocada! Por isso podemos devolver ela usando a função free!
-------------------------------------//-------------------------------------------

Teste o Premium para desbloquear

Aproveite todos os benefícios por 3 dias sem pagar! 😉
Já tem cadastro?

Outros materiais