380 likes | 468 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". Alocação Dinâmica é um meio pelo qual o programa pode obter memória enquanto está em execução. Já visto até agora:
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" • Alocação Dinâmica é um meio pelo qual o programa pode obter memória enquanto está em execução. • 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).
Topo da Memória StackPointer Inicio da Pilha • Programa: #include <stdio.h> char *a, *b; int func_A () { int local1, local2; - - - } void func_B () { int localA, localB; localA = func_A(); localB = func_A(); } main () { a = "Essa aula é legal"; b = "Será mesmo?" func_B(); } HeapPointer Início da Área Alocável a b Variáveis estáticas 10010101... P r o g r a m a Código objeto "Essa aula é ... "Será mesmo.. Constantes Sist.Operacional Base da Memória
Programa: • #include <stdio.h> • char *a, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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, *b; • int func_A () • { • int local1, local2; • - - - • } • void func_B () • { • int localA, localB; • localA = func_A(); • localB = func_A(); • } • main () • { • a = "Essa aula é legal"; • b = "Será mesmo?" • 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 (1º byte) da área de memória alocada. • Isto significa que o valor deste ponteiro pode ser atribuído a qualquer variável do tipo ponteiro. • Para isto deve ser utilizado sempre um typecasting. Ex.: se x é ponteiro para inteiro então explicitar isto comx = (int *) malloc( sizeof(int) ); • número_de_bytes é a quantidade de bytes 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.
HeapPointer Topo da Área Alocável Topo da Memória StackPointer Topo da Pilha • Exemplo: • #include <stdlib.h> • #include <stdio.h> • char *p; • int *q; • main () • { • p = (char *) malloc(1000); • // Aloca 1000 • // bytes de RAM • q = (int *) malloc(50*sizeof(int)); • // Aloca espaço • // para 50 inteiros. • } 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 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: Lista com um vetor de Ponteiros para Strings. • Uma lista ordenada pode conter Strings de qualquercomprimento < 10000. • Esta lista tem número de elementos máximo fixo = 100 e é implementada como um vetor de ponteiros para Strings. • Utilize as rotinas de lista com vetor que você utilizou para a agenda. • 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 strcpy(). • Por fim um lugar na lista é encontrado para ele. A posição escolhida do vetor de ponteiros da lista é instanciada através da atualização dos valores do ponteiro da posição do string na lista com o endereço do string.
Modelagem da estrutura Strings lidos do usuário e alocados no Heap C o n s t i t u i r \0 Lista com Vetor de Ponteiros -1 0 1 2 3 4 1 S a b ã o \0
Modelagem da Lista • Pseudo-código: constantes Maxlista = 100; tipo Lista { caracter *dados[Maxlista]; “ Vetor de ponteiros para char “ inteiro ultimo; }; • Importante: Observe que criando uma variável do tipo Lista você não vai estar alocando memória para os strings a serem lidos, apenas para os ponteiros para eles.
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.
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. • Para a leitura de um String utilize scanf("%s", entrada) onde char entrada[10000].
Exercício Nº 2: Trabalho com Passagem de Parâmetros • Agora você vai fazer um programa que manipula mais de uma lista. • O programa fará isto com um único conjunto de funções e passagem das diversas listas como parâmetros. • Como aplicação imaginemos um sistema de contabilidade simples. • Você vai ter um Plano de Contas constituído por duas listas: débitos e créditos. • O mesmo conjunto de funções (que você já implementou) vai poder ser utilizado para isso: você somente precisa ampliar o conjunto de parâmetros da função para passar por referência também a lista que você quer alterar. • A passagem de parâmetro da lista deve ser por referência porque você deseja que as alterações sejam persistentes.
Modelagem de um Lancamento • Cada lista de débitos ou créditos é constituida de um lançamento. O lançamento possui: • Um valor real (positivo). • Um nome, por exemplo “pagar proteção à Mafia” • Estrutura: tipo Lancamento { caracter*nome; “ponteiro para char alocado no heap“ real valor; };
Modelagem de um tipo Lista para Débitos ou Créditos • Pseudo-código: constantes Maxlista = 100; tipo ListaContabil { Lancamento dados[Maxlista]; “ Vetor de Estrutura Lançamento “ inteiro ultimo; }; • Importante: Observe que criando um vetro de lancamentos, você não vai estar reservando memória para os nomes destes, pois o campo nome é só ponteiros. Lembre-se de alocar.
Usando (pseudo código) • Crie variáveis globais: ListaContabil debitos, creditos; • Passe estas variáveis como parâmetro por referência: adiciona(&debitos, nomeLanc, valorLanc) • Cabeçalho: Inteiro FUNÇÃO adiciona(ListaContabil *plano; caracter *nome; real valor) • Importante: nome é passado como ponteiro para caracter. Use um buffer global para ler o nome do lancamento do usuário.
Desenho Strings lidos do usuário e alocados no Heap Lista de debitos ou de créditos com Vetor de Estruturas do tipo Lançamento -1 0 1 2 3 4 1 R$ 5,00 S a b ã o \0 R$ 505,00 P a s s a g e n s \0
Usando (código C) • Referencie diferentemente se estiver usando ponteiros para a lista ou a lista diretamente: ListaContabil debitos, creditos; debitos.dados[2].valor = 5.0; strcpy(debitos.dados[2].nome, buffer); Dentro das funções: Suponha: ListaContabil *ponteiro e ponteiro = &debitos; ponteiro->dados[2].valor = 5.0; strcpy(ponteiro->dados[2].nome, buffer);
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 ); • }