Baixe o app para aproveitar ainda mais
Prévia do material em texto
1 Tipos de Alocação de Memória Tecnologia(s) C Complexidade Média Fonte Faculdade , ChatGPT Status Completo C int numeroInteiro; // O compilador reserva 4 bytes para um inteiro padrão de 32 bits. char caractere; // O compilador reserva 1 byte para um caractere. float numeroReal; // O compilador reserva 4 bytes para um número de ponto flutuante. Alocação Estática Refere-se à reserva de espaço de memória durante a compilação do programa.• O tamanho da memória alocada permanece constante durante toda a execução do programa Critério utilizado para determinar quanto espaço na memória é necessário para armazenar uma variável: Depende do tipo de dado da variável. Cada tipo de dado em uma linguagem de programação tem um tamanho associado, que é definido pelo compilador. • Os tamanhos padrão dos tipos de dados podem variar entre diferentes linguagens de programação e compiladores, mas geralmente seguem um padrão comum. • Exemplos comuns de tamanhos de tipos de dados em C�• int� Geralmente, 4 bytes em sistemas de 32 bits e 8 bytes em sistemas de 64 bits. • char� Normalmente, 1 byte.• float� Geralmente, 4 bytes.• double� Geralmente, 8 bytes.• Exemplos 2 C // A função sizeof() retorna o número de bytes necessários para armazenar o tipo de dado ou a variável especificada: #include <stdio.h> int main() { printf("Tamanho de int: %zu bytes\n", sizeof(int)); printf("Tamanho de char: %zu byte\n", sizeof(char)); printf("Tamanho de float: %zu bytes\n", sizeof(float)); return 0; } C #include <stdlib.h> int main() { int *ponteiroInteiro = (int *)malloc(sizeof(int)); // Alocou memória, mas não liberou // Se essa linha for removida, haverá um vazamento de memória return 0; } Variáveis alocadas estaticamente existem desde o início até o término da execução do programa (tempo de vida da variável = tempo de execução do programa). • O espaço de memória é liberado automaticamente no encerramento do programa.• Essas variáveis são armazenadas no segmento de dados.• Vantagens Acesso rápido, já que a localização na memória é conhecida antecipadamente.• Não há risco de vazamento de memória. Memory Leak Ocorre quando um programa aloca dinamicamente memória durante sua execução, mas falha em liberar ou desalocar essa memória antes de encerrar. • Resulta em acúmulo progressivo de memória não utilizada ao longo do tempo (consumo excessivo de recursos → falhas no programa ou no sistema). • É responsabilidade do programador liberar a memória alocada dinamicamente: O programa aloca dinamicamente um espaço de memória para armazenar um inteiro, mas não utiliza a função free para liberar essa memória antes de encerrar o programa. Isso resulta em um vazamento de memória, pois o espaço alocado não é recuperado, mesmo quando o programa não precisa mais dele. 3 Desvantagens Limitações na flexibilidade, pois o tamanho precisa ser conhecido antecipadamente.• Ineficiente para lidar com dados cujo tamanho não pode ser determinado até o tempo de execução. Essa situação é comum quando lida-se com estruturas de dados dinâmicas, como listas encadeadas, árvores, ou arrays cujo tamanho precisa ser ajustado dinamicamente com base nos dados de entrada ou em condições durante a execução do programa. Listas Encadeadas:1. Suponha que você esteja construindo uma lista encadeada, e você não sabe quantos elementos ela terá até que os dados de entrada sejam processados durante a execução do programa. Nesse caso, você pode precisar alocar dinamicamente memória para cada novo nó na lista à medida que os dados são lidos. • Leitura de Dados Externos:2. Se você estiver lendo dados de um arquivo ou de uma fonte externa e o número de elementos não é conhecido até a execução do programa, você pode precisar alocar memória dinamicamente para armazenar esses dados. • Redimensionamento Dinâmico de Arrays:3. Suponha que você esteja construindo um array que precisa ser redimensionado conforme os dados são processados. Se o tamanho não puder ser determinado até o tempo de execução, a alocação dinâmica de memória pode ser mais adequada do que a alocação estática. • Em contraste, a alocação estática de memória, que ocorre em tempo de compilação, exige que o tamanho seja conhecido antecipadamente. Se você souber exatamente quantos elementos sua estrutura de dados terá e esse número for constante durante a execução do programa, a alocação estática pode ser mais eficiente e direta. Em C, a alocação estática de variáveis acontece por padrão para variáveis globais. Para variáveis locais (dentro de funções), você precisa usar a palavra-chave static para indicar que deseja uma alocação estática. Aqui estão alguns exemplos para ilustrar isso: Variáveis Globais Exemplo 4 C #include <stdio.h> int globalVariable = 10; // Alocada estaticamente int main() { printf("Global Variable: %d\n", globalVariable); return 0; } C #include <stdio.h> void exampleFunction() { static int staticVariable = 5; // Alocada estaticamente printf("Static Variable: %d\n", staticVariable); staticVariable++; // Mantém o valor entre chamadas da função } int main() { exampleFunction(); // Chama a função exampleFunction(); // Chama novamente return 0; } No exemplo acima, globalVariable é uma variável global e será alocada estaticamente. Isso significa que ela será inicializada uma vez, antes do início da execução do programa, e manterá seu valor ao longo do tempo de vida do programa. Variáveis Locais Estáticas Exemplo Neste exemplo, staticVariable é uma variável local da função exampleFunction, mas a palavra-chave static é usada para indicar que ela deve ser alocada estaticamente. Isso significa que ela será inicializada uma vez, e seu valor será mantido entre chamadas sucessivas da função. A função exampleFunction() é chamada duas vezes consecutivas, portanto, o valor da variável staticVariable será o seguinte: Na primeira chamada: A variável staticVariable é inicializada com o valor 5.• O valor de staticVariable é impresso como 5.• A variável staticVariable é incrementada para 6 na primeira chamada da função. • Na segunda chamada: 5 C #include <stdio.h> static int a = 0; // variável global, alocação estática void incrementa(void) { int b = 0; // variável local, alocação automática static int c = 0; // variável local, alocação estática printf("a: %d, b: %d, c: %d\n", a, b, c); a++; b++; c++; } int main(void) { int i; for (i = 0; i < 5; i++) incrementa(); system("pause"); return 0; } // Saída: // a: 0, b: 0, c: 0 // a: 1, b: 0, c: 1 // a: 2, b: 0, c: 2 // a: 3, b: 0, c: 3 // a: 4, b: 0, c: 4 Como staticVariable é uma variável estática, seu valor não é reiniciado para 5. • Em vez disso, o valor de staticVariable mantém-se como 6, que foi o valor após a primeira chamada. • Portanto, o valor de staticVariable é impresso novamente como 6.• Depois, staticVariable é incrementada novamente, tornando-se 7.• Se a variável não fosse estática, o valor de staticVariable seria sempre reiniciado para 5 a cada chamada da função, resultando em uma saída de 6 em ambas as chamadas da função. Exemplo A cada chamada da função incrementa(), as variáveis a, b e c são impressas na tela e depois tem seu valor incrementado. • A variável global a é incrementada a cada chamada da função, então seu valor será de 0 a 4 após as cinco chamadas. • A variável local automática b é inicializada com 0 a cada chamada, então sempre será impressa como 0. • 6 A variável local estática c é incrementada a cada chamada, mas seu valor é mantido entre chamadas, então será de 0 a 4 após as cinco chamadas. • Primeira chamada Variáveis: a � 0, b � 0, c � 0• Saída: a: 0, b: 0, c: 0• Após incrementos: a � 1, b � 1, c � 1• Segunda chamada Variáveis: a � 1, b � 0, c � 1• Saída: a: 1, b: 0, c: 1• Após incrementos: a � 2, b � 1, c � 2• Terceira chamada Variáveis:a � 2, b � 0, c � 2• Saída: a: 2, b: 0, c: 2• Após incrementos: a � 3, b � 1, c � 3• Quarta chamada Variáveis: a � 3, b � 0, c � 3• Saída: a � 3, b � 0, c � 3• Após incrementos: a � 4, b � 1, c � 4• Quinta chamada Variáveis: a � 4, b � 0, c � 4• Saída: a � 4, b � 0, c � 4• Após incrementos: a � 5, b � 1, c � 5• Então, para resumir: Para variáveis globais, a alocação estática acontece automaticamente.• Para variáveis locais, você precisa usar a palavra-chave static para indicar alocação estática. • Alocação Automática Gerenciamento Automático de Memória� Nesse método de alocação, o sistema gerencia automaticamente a memória para as variáveis locais da função, reservando espaço quando a função é chamada e liberando-o quando a função é encerrada. • Alocação na Pilha de Execução: A memória para variáveis de alocação automática é tipicamente alocada na pilha de execução, uma área de memória gerenciada pelo sistema operacional. • 7 C void *malloc (size_t size) Tempo de Vida Atrelado à Função: O tempo de vida dessas variáveis está diretamente ligado à execução da função. Elas são automaticamente liberadas quando a função é concluída, garantindo que o espaço de memória que ocupam seja recuperado automaticamente. • Inicialização Opcional: Variáveis alocadas automaticamente não exigem inicialização explícita, embora seus valores iniciais possam ser imprevisíveis se não forem inicializados explicitamente pelo programador. • Eficiência para Dados Temporários: Esse método é eficiente para variáveis de curta duração, como aquelas usadas dentro do escopo de uma função. A alocação e desalocação de memória na pilha são operações rápidas, tornando-o adequado para dados temporários. • Alocação Dinâmica Uma área da memória é requisitada explicitamente pelo programada durante sua execução.• O controle das áreas alocadas dinamicamente pode ser manual ou semiautomático, portanto, o programa solicita e libera memória conforme necessário ou quando o programa encerrar. • A liberação das áreas alocadas é realizada pelo programador.• Esses dados são armazenados no segmento Heap.• É realizada principalmente por meio de funções como malloc(), calloc(), realloc() e free()• Alocação Dinâmica Simples Função malloc É usada para alocar um bloco de memória (bytes) de tamanho específico durante a execução do programa. • Retorna um Ponteiro para o primeiro byte alocado ou NULL se a alocação falhar.• Exemplos Exemplo 01 8 C #include <stdio.h> #include <stdlib.h> int main() { int *ptr; // Ponteiro para int (4 bytes) // Aloca espaço para um único inteiro (4 bytes) ptr = (int*)malloc(sizeof(int)); if (ptr == NULL) { printf("Erro ao alocar memória!"); return 1; } // Usa o espaço alocado *ptr = 42; // Libera o espaço alocado quando não for mais necessário free(ptr); return 0; } Exemplo 02 9 C #include <stdio.h> #include <stdlib.h> int main() { int *p; int n = 5; // quantidade de elementos alocados // A função malloc() aloca memória no heap para armazenar n inteiros. // sizeof(int) é usado para garantir que o espaço alocado seja suficiente para armazenar um inteiro. p = (int*)malloc(n * sizeof(int)); if (p == NULL) { printf("Falha ao alocar memória\n"); exit(1); // Sai do programa indicando erro } // Atribui valores aos elementos alocados // Neste caso, os elementos são preenchidos com números pares. for (int i = 0; i < n; i++) { p[i] = i * 2; } // Imprime os valores atribuídos for (int i = 0; i < n; i++) { printf("%d ", p[i]); } printf("\n"); // Libera a memória alocada free(p); return 0; } C void *realloc(void *ptr, size_t newsize); Função realloc ptr: Ponteiro para o bloco de memória previamente alocado dinamicamente, a qual deseja-se realocar. • Se ptr foi NULL , realloc() funcionará como malloc()• É utilizada para redimensionar um bloco de memória que já havia sido alocado anteriormente. • 10 C #include <stdio.h> #include <stdlib.h> int main() { int *ptr; int n = 5; // Alocando memória inicialmente ptr = (int *)malloc(n * sizeof(int)); if (ptr == NULL) { printf("Erro ao alocar memória.\n"); exit(1); } // Imprimindo os valores originais printf("Valores originais:\n"); for (int i = 0; i < n; i++) { ptr[i] = i; printf("%d ", ptr[i]); } printf("\n"); // Realocando memória para armazenar mais elementos n = 10; ptr = (int *)realloc(ptr, n * sizeof(int)); if (ptr == NULL) { printf("Erro ao realocar memória.\n"); exit(1); } // Imprimindo os valores atualizados printf("Valores atualizados:\n"); for (int i = 0; i < n; i++) { printf("%d ", ptr[i]); } printf("\n"); // Liberando a memória alocada free(ptr); return 0; } É recomendável atribuir o resultado de realloc() a um novo ponteiro, porque se a realocação falhar e realloc() retornar NULL , o acesso ao bloco de memória original não terá sido alterado. • Exemplo Output 11 C void free(void *ptr) Valores originais: 0 1 2 3 4 Valores atualizados: 0 1 2 3 4 0 0 0 0 0 Este código aloca inicialmente espaço para armazenar 5 inteiros. Depois, ele realoca espaço para armazenar 10 inteiros e imprime os valores atualizados. Por fim, libera a memória alocada antes de terminar. Função free ptr: É o o ponteiro para o bloco de memória previamente alocado que deseja-se liberar. Ao chamar a função free(prt), o bloco de memória que havia sido alocado anteriormente pelo ponteiro ptr é marcado como livre e disponível para reutilização pelo sistema. • Entretando, o ponteiro ptr continua apontando para o endereço de memória liberadao e por isso é aconselhável mudar seu valor para NULL após a liberação. • Exemplo 12 C #include <stdio.h> #include <stdlib.h> int main() { // Aloca memória dinamicamente para armazenar um inteiro int *ptr = (int *)malloc(sizeof(int)); if (ptr == NULL) { printf("Falha ao alocar memória\n"); return 1; // Sai do programa indicando erro } *ptr = 10; // Atribui um valor ao inteiro alocado dinamicamente printf("Valor antes de liberar a memória: %d\n", *ptr); // Libera a memória alocada free(ptr); // Após liberar a memória, ptr ainda aponta para o endereço de memória liberado // É uma boa prática definir o ponteiro como NULL após liberar a memória ptr = NULL; // Tentativa de acesso ao conteúdo da memória liberada, o que pode causar comportamento indefinido printf("Valor após liberar a memória: %d\n", *ptr); return 0; } Em resumo: Função Ação Sintaxe malloc( ) Aloca um determinado número de bytes na memória e retorna um ponteiro para o primeiro byte alocado. Se não for possível alocar, a função retorna NULL #include <stdlib.h> void *malloc(size_t size) free() Libera o número de bytes alocados previamente na memória, apontados por ptr #include <stdlib.h> void ree (void *ptr) 13 C void* calloc(size_t num_elements, size_t element_size); realloc () Redimensiona a área previamente alocada, apontada por ptr , para o novo tamanho newsize . #include <stdlib.h> void *realloc(void *ptr size_t newsize) Alocação Dinâmica por Vetor É usada para alocar memória para uma sequência de elementos de um determinado tipo de dados. • Em C, uma das maneiras de alocar memória para um vetor é usando a função calloc() .• Função calloc Parâmetros num_elements � Número de elementos a serem alocados.• element_size � Tamanho de cada elemento em bytes.• Retorno Retorna um ponteiro para a região da memória alocada, que pode ser convertida para o tipo de dado desejado. Funcionamento calloc() aloca espaço na memória para um vetor com num_elements , cada um tendo o tamanho especificado em element_size . • A função retorna umponteiro para a região de memória alocada.• A memória alocada é inicializada com zeros.• Se a alocação falhar, calloc() retorna NULL .• Exemplo 14 C #include <stdio.h> #include <stdlib.h> int main() { int *vetor; int num_elementos = 5; // Aloca espaço para um vetor de 5 inteiros vetor = (int*)calloc(num_elementos, sizeof(int)); // Verifica se a alocação foi bem-sucedida if (vetor == NULL) { printf("Erro: Não foi possível alocar memória."); return 1; } // Preenche o vetor com alguns valores for (int i = 0; i < num_elementos; i++) { vetor[i] = i * 10; } // Imprime os valores do vetor printf("Valores do vetor:\n"); for (int i = 0; i < num_elementos; i++) { printf("%d ", vetor[i]); } // Libera a memória alocada free(vetor); return 0; } Diferença entre malloc() e calloc() Característic a malloc() calloc() Inicialização Não inicializa a memória alocada. Inicializa toda a memória alocada com zeros. Parâmetros Recebe apenas o tamanho em bytes do bloco. Recebe o número de elementos e o tamanho de cada um. Exemplo 15 C #include <stdio.h> #include <stdlib.h> int main() { int *ptr_malloc, *ptr_calloc; // Usando malloc para alocar espaço para um único inteiro ptr_malloc = (int*)malloc(sizeof(int)); // Usando calloc para alocar espaço para um único inteiro ptr_calloc = (int*)calloc(1, sizeof(int)); if (ptr_malloc == NULL || ptr_calloc == NULL) { printf("Erro: Não foi possível alocar memória.\n"); return 1; } printf("Conteúdo de ptr_malloc: %d\n", *ptr_malloc); // Pode ser um valor aleatório printf("Conteúdo de ptr_calloc: %d\n", *ptr_calloc); // Deve ser 0 // Liberando a memória alocada free(ptr_malloc); free(ptr_calloc); return 0; }
Compartilhar