300 likes | 426 Views
Estruturas de Dados - T.332 Capítulo 3 Parte 2: Alocação Dinâmica de Memória. 3.1 As Funções de Alocação Dinâmica de Memória em "C". Já visto até agora: Constantes são " codificadas " dentro do código objeto de um programa em tempo de compilação.
E N D
Estruturas de Dados - T.332Capítulo 3Parte 2:Alocação Dinâmica de Memória
3.1 As Funções de Alocação Dinâmica de Memória em "C" • Já visto até agora: • Constantes são "codificadas" dentro do código objeto de um programa em tempo de compilação. • Variáveis globais (estáticas) têm a sua alocação codificada em tempo de compilação e são alocadas logo que um programa inicia a execução. • Variáveis locais em funções (ou métodos) são alocadas através da requisição de espaço na pilha (stack). Alocação Dinâmica é um meio pelo qual o programa pode obter memória enquanto está em execução.
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } Topo da Memória StackPointer Inicio da Pilha HeapPointer Início da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } Topo da Memória StackPointer Topo da Pilha HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB &func_B-#2 local1 local2 HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB &func_B-#2 local1 local2 HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB &func_B-#3 local1 local2 HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB &func_B-#2 local1 local2 HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } StackPointer Topo da Pilha Topo da Memória &main-#3 localA localB HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a = "Essa aula é legal”; • char *b = "Será mesmo?"; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • func_B(); • } Topo da Memória StackPointer Topo da Pilha HeapPointer Topo da Área Alocável a b Variáveis estáticas 10010101... Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Alocação Dinâmica em "C" • A memória alocada pelas funções de alocação dinâmica é obtida do heap. • O heap é a região de memória livre que se encontra entre o programa (com a área de armazenamento permanente) e a pilha (stack). • O tamanho do heap é, a princípio, desconhecido do programa. • "C" possui duas funções básicas para gerência de memória: • malloc(nº de bytes) - aloca memória. • free(endereço) - libera memória
Função malloc: • Protótipo: void *malloc(size_t número_de_bytes); • Detalhes : • Devolve um ponteiro do tipo void (sem tipo) para o início da área de memória alocada. • Isto significa que o valor deste ponteiro pode ser atribuído a qualquer variável do tipo ponteiro. • número_de_bytes é a quantidade de bytes a ser alocada. • Se a memória for alocada no topo do heap, o heapPointer é atualizado (incrementado de número_de_bytes). • O tipo size_t é definido em stdlib.h que deve ser sempre incluído no início de um programa usando alocação dinâmica.
Exemplo: • #include <stdio.h> • #include <stdlib.h> • char *p; • int *q; • main () • { • p = malloc(1000); • // Aloca 1000 • // bytes de RAM • q = malloc(50*sizeof(int)); • // Aloca espaço • // para 50 inteiros. • } HeapPointer Topo da Área Alocável Topo da Memória StackPointer Topo da Pilha 50*int = 200 bytes 1000 bytes p q Variáveis estáticas 10010101... Código objeto Constantes Sist.Operacional Base da Memória
malloc devolve: • um ponteiro para a área alocada • o ponteiro nulo (NULL) caso não seja possível alocar a memória requisitada. • Convém verificar se foi pos-sível alocar a memória: • #include <stdio.h> • #include <stdlib.h> • char *p; • main () • { ................ • p = malloc(1000) • // Tenta alocar 1000 • // bytes de RAM • if (p == NULL) • // Testa se p • // diferente de 0 • printf("Sem memória."); • } Topo da Memória Espaço de variáveis locais alocado StackPointer Topo da Pilha HeapPointer Topo da Área Alocável Já alocado antes p Variáveis estáticas 10010101... Código objeto "Sem memória" Constantes Sist.Operacional Base da Memória
Função free: • Protótipo: void free( void *p ); • Detalhes : • Devolve memória previamente alocada ao sistema. • A memória devolvida é aquela que foi alocada com um ponteiro com o valor de p. • O valor de pdeve obrigatoriamente ser um valor que foi alguma vez retornado por malloc(). • Não é possível alocar-se um vetor enorme e depois dealocar-se a parte dele que "sobrou". • A utilização de free() com um valor de ponteiro qualquer poder ter resultados catastróficos. • A gerência de buracos no heap é responsabilidade do sistema operacional.
Exercício (agora): Lista Ordenada com um vetor de ponteiros para Strings. • Uma lista ordenada pode conter Strings de qualquer comprimento < 10000. • Esta lista tem número de elementos máximo fixo = 100 e é implementada como um vetor de ponteiros para Strings. • Um novo String é lido primeiramente para dentro de uma variável auxiliar qualquer. • Então é alocada memória para exatamente o seu tamanho e ele é copiado para esta área. Para copiar um String utilize strcopy. • Por fim um lugar na lista é encontrado para ele, através da atualização dos valores dos ponteiros da lista. • Como critério de ordenação basta somente a primeira letra do String.
Topo da Memória StackPointer Topo da Pilha • Organização de memória do Exercício: Espaço de variáveis locais alocado HeapPointer Topo da Área Alocável str4 str3 str2 str1 Vetor de char* Var.Estáticas 10010101... Código objeto Constantes Sist.Operacional Base da Memória
Para verificar o comprimento de um String: • Utilize a função strlen(). • Esta função devolve o comprimento (em caracteres imprimíveis) de um String. • Protótipo: int strlen(char *p); • #include <stdio.h> • #include <stdlib.h> • #include <sting.h> • char p[90] = "Carro"; • main () • { • printf("%i", strlen(p) ); • } • Imprime: 5.
Para copiar um String: • Utilize a função strcpy(). • Esta função copia o conteúdo de um string (dado por um apontador) para a posição de memória dada por outro apontador. • Protótipo: char *strcpy(char *destino, *fonte); • #include <stdio.h> • #include <stdlib.h> • #include <sting.h> • char p[90] = "Carro"; • char lata[20]; • main () • { • strcpy(lata, p) ); • printf("s%", lata); • } • Imprime: Carro.
Exemplo: Para ler um String: • Utilize a função scanf(formato, char1*, …, charn*). • Esta função copia o conteúdo digitado ate um separador para o primeiro ponteiro, depois para outro e assim por diante. • Utilize a função gets(char*). • Esta função le uma linha inteira, inclusive virgulas, espaços e outros para a posição de memória apontada por char*. • #include <stdio.h> • #include <sting.h> • char linha[1000]; • char estringue[20]; • main () • { • scanf(“%s”, estringue); /* le até um espaço ou • similar */ • getchar(); /* limpa o enter */ • gets(linha);/* lê tudo até o enter */ • }
Detalhes: Lista Ordenada com um vetor de ponteiros para Strings. • Como você não sabe o comprimento do String que o usuário vai digitar, use primeiro uma variável auxiliar grande (10000 posições) para guardar o que foi digitado. • A lista deve ser passada como parâmetro para todas as funções que a utilizam. • Da mesma maneira as variáveis de controle da lista. • Todas as funções de lista ordenada implementadas anteriormente devem ser reimplementadas para utilizar estes Strings.
Headerfile: Como Garantir Inclusão Única /* Arquivo: pilha.h */ #ifndef EstruturaDaPilha #define EstruturaDaPilha /* Definir uma estrutura para a pilha */ struct estruturaDaPilha { int topo; int dados[MaxPilha]; }; /* Def. um tipo que tem a estrutura da pilha. */ typedef struct estruturaDaPilha pilha; #endif
Headerfiles: Importante • A diretiva de compilação #ifndef (if not defined) diz que aquela área de código fonte entre o #ifndef e o #endif somente será levada em conta pelo compilador se o argumento de #ifndef ainda não houver sido definido na mesma sessão de compilação no escopo de um módulo. • Isso garante que código que a gente "por via das duvidas" inclui mais de uma vez em um modulo não seja considerado duas vezes. • Um exemplo de como isto e útil esta na diretiva #include <stdio.h> que esta presente tanto em pilha.h como em pilha.c como em aplic.c. • Como aplic.c carrega pilha.h "para dentro" de si mesmo, carregara também stdio.h. Como está explicitamente também carregando stdio.h, se não houver uma diretiva #ifndef em stdio.h, ele terá o mesmo código existente em stdio.h duas vezes.
Projetos de Implementação: Usando Make • Make: Utilitário que auxilia a compilação de projetos formados por vários arquivos de programas • Realiza checagem de dependências entre o arquivo destino e os fontes • Baseia-se nas datas de arquivos
Projetos de Implementação: Sintaxe do Makefile aplic: aplic.o pilha.o gcc -g -o aplic aplic.o pilha.o aplic.o: aplic.c pilha.h gcc -g - c aplic.c pilha.o: pilha.c pilha.h gcc -g -c pilha.c meta: dependencia1 dependênciaN <tab>um comando para atingir meta <tab>outro comando para atingir meta
Projetos de Implementação: Usando o Makefile • Chamada: make -f <nome do Makefile> • Se o nome não for dado, make procurará por arquivos chamados Makefile e makefile, nesta ordem. • Para definir qual meta será a primeira a ser considerada: • Se você não diz nada, a primeira meta será considerada. • Voce pode passar como último parâmetro o nome da meta: make -f meuMakefile compilar • Todas as dependencias de compilar, se esta meta existir, serão também levadas em consideração.
Módulo lista.h: • #ifndef Lista • #define Lista • typedef struct estruLista { • char *elemento[30]; • int ultimo; • int max; • }; • typedef struct estruLista lista; • #endif • Módulo lista.c: • #include <stdio.h> • #include <stdlib.h> • #include “lista.h” • lista *criaLista () • { • lista *nova; • nova = malloc( sizeof(lista) ); • nova->max = 30; • nova->ultimo = -1; • return (nova); • } • void destroiLista(lista *morta) • { • // Libera memória p/os strings • for (i=0; morta->ultimo; i++) • free (morta->elemento[i]); • // Libera memória da lista • free ( morta ); • }