Estrutura de Dados I




           UNICAP




  Eduardo Araújo Oliveira
  http://eduoliveira.com
 Twitter: @oliveiraeduardo




                             slide 1
PONTEIROS


            slide 2
“Memória?”




             slide 3
Uma memória é uma
seqüência de células
 de armazenamento
    (ou posições)




                       slide 4
“Uma variável é um
   aglomerado de
uma ou mais células de
     memória”.




                         slide 5
Variável

Atributos de uma variável


– nome: seqüência de caracteres utilizada para identificar a
variável;


– tipo: é o tipo dos dados que serão armazenados na variável;


– conteúdo: é o valor armazenado na variável;


– endereço: é a localização (posição) da variável na memória;




                                                                slide 6
slide 7
Variável

– char sexo = ‘M’;


• Nome da variável: sexo
• Tipo da variável: char
• Conteúdo da variável: ‘M’
• Endereço da variável: 5




                              slide 8
Variável

– int idade = 31;


• Nome da variável: idade
• Tipo da variável: int
• Conteúdo da variável: 31
• Endereço da variável: 1




                             slide 9
Variável

– int idade = 31;


• Nome da variável: idade
• Tipo da variável: int
• Conteúdo da variável: 31
• Endereço da variável: 1




                             slide 10
Variáveis do tipo ponteiro

Ponteiros, como o próprio nome diz, é um tipo de variável que
aponta para outra (de um tipo qualquer).


• Um ponteiro guarda o endereço de memória de uma variável.
• Sintaxe: Variáveis que são ponteiros são representadas da
seguinte forma:


tipo_dado_apontado *nome_variavel;




– int *ptr1;
– char *ptr2;


                                                          slide 11
Variáveis do tipo ponteiro

Utilizado para:


– Substituir a manipulação de vetores/matrizes com eficiência;


– Passar valores e mudar valores dentro de funções;


– Manipular arquivos;


– Aumento de eficiência para algumas rotinas;


– Possibilitar a alocação dinâmica de memória.




                                                             slide 12
Variáveis do tipo ponteiro

Operadores de Endereço



–O    & (“endereço de”) antes de um nome de variável qualquer
retorna o endereço desta variável.



– O   * (“conteúdo de”) antes de um nome de variável do tipo
ponteiro   retorna   o   conteúdo   armazenado    na    posição    de
memória     apontada.    Operação    chamada     de    indireção   ou
desreferenciamento.




                                                                   slide 13
Variáveis do tipo ponteiro

char letra = ‘Z’;
char *ptr = &letra;


– Nome da variável: ptr
– Tipo da variável: char *
– Conteúdo da variável: 1
– Endereço da variável: 4




                             slide 14
Variáveis do tipo ponteiro

void main(){
int x = 50;
int * ptr;
printf (“%i”,x); /*exibe o conteúdo da variável x (50)*/
printf (“%p”,&x); /*exibe o endereço da variável x*/
ptr = &x; /*armazena em ptr o endereço de x*/
printf (“%p”,ptr); /*exibe o conteúdo da variável ptr*/
printf (“%p”,&ptr); /*exibe o endereço da variável ptr*/
printf (“%i”,*ptr); /*exibe o conteúdo da variável x*/
}




                                                           slide 15
Variáveis do tipo ponteiro

void main () {
int x;
int *ptr;
printf(“Digite um valor para x:”);
scanf(“%i”, &x);
ptr = &x;
*ptr = *ptr + 1;
    printf(“O valor de x é %i”, *ptr);
printf(“O endereço de x é %i”, ptr);
}




                                         slide 16
Variáveis do tipo ponteiro


*p ⇒ representa o conteúdo da variável apontada.


 p ⇒ representa o endereço de memória da variável apontada.




                                                         slide 17
Variáveis do tipo ponteiro

Observação
– Quando queremos indicar que um ponteiro está “vazio”, ou
seja, não contém um endereço de uma variável, atribuímos
a ele o valor NULL.


•   NULL:    Endereço de memória 0 (zero). Esta posição de
memória não é utilizada para armazenar dados.


– Exemplo:
ptr = NULL; /* inicializa o ponteiro ptr*/




                                                       slide 18
Variáveis do tipo ponteiro

int a = 5, b = 7; (neste exemplo, um int ocupa 2bytes na
memoria, e não 4, como em outros exemplos).
int *ptr = NULL;




ptr = &a;




                                                     slide 19
Variáveis do tipo ponteiro

Atribuição


– Podemos atribuir o conteúdo de um ponteiro a outro. Dessa
forma, teremos dois ponteiros referenciando o mesmo endereço
de memória.


• Exemplo:
int a = 5, *p1, *p2;
p1 = &a;
p2 = p1;




                                                          slide 20
Aritmética de ponteiros

Sendo os ponteiros números que representam posições de
memória, podem ser realizadas algumas operações aritméticas
sobre eles:


– Incremento;
– Decremento;
– Diferença;
– Comparação.




                                                        slide 21
Aritmética de ponteiros

Incremento e Decremento


– Um ponteiro pode ser incrementado como qualquer outra
variável


– Se ptr é um ponteiro para um determinado tipo, quando ptr é
incrementado, por exemplo, de uma unidade, o endereço que
passa a conter é igual ao endereço anterior de ptr + sizeof(tipo)
para que o ponteiro aponta


– A mesma situação ocorre para a operação de decremento,
onde o endereço que passa a conter é igual ao endereço
anterior de ptr - sizeof(tipo) para que o ponteiro aponta

                                                              slide 22
Aritmética de ponteiros

int main(int argc, char **argv) {
 int x = 5, *px = &x;
 double y = 5.0, *py = &y;

printf("%i %in", x, px);
printf("%i %in", x+1, px+1);       5 2293524
                                    6 2293528
printf("%f %in", y, py);
                                    5.000000 2293512
printf("%f %in", y+1, py+1);
                                    6.000000 2293520
                                    5 2293524
printf("%i %in", x, px);
printf("%i %in", x-1, px-1);       4 2293520
                                    5.000000 2293512
 printf("%f %in", y, py);          4.000000 2293504
 printf("%f %in", y-1, py-1);
 getchar();
return 0;
}                                                      slide 23
Aritmética de ponteiros

Além das operações de incremento e decremento, só é possível
somar ou subtrair inteiros de um ponteiro:

– ptr = ptr + 7;
        • Esta expressão faz com que ptr aponte para o sétimo
        elemento a partir do seu elemento atual.


– ptr = ptr – 5;
        • Esta expressão faz com que ptr aponte para o quinto
        elemento anterior ao elemento atual.


Somar 2 a um ponteiro de inteiros de 4 bytes irá incrementá-lo em 8. Isso faz
com que o incremento de um ponteiro o posicione para o próximo elemento em
um arranjo, o que geralmente é o resultado intencionado.
                                                                         slide 24
Aritmética de ponteiros

 • Adição: somar um inteiro a um ponteiro

 • Exemplo:
 float *p;
 p = p + 2;

 Fator de escala: é o tamanho (número de
   bytes) do objeto apontado.
 • Exemplo: fator de escala de uma variável do tipo
   float: 4
 p = p + 2 significa p = p + 2 * fator de escala

                                                slide 25
Aritmética de ponteiros

 • Subtração: subtrair um inteiro de um
   ponteiro

 • Exemplo:
 float *p;
 p = p - 3;

 p = p - 3 significa p = p - 3 * fator de escala




                                                   slide 26
Aritmética de ponteiros

 Aritmética de Ponteiros
 • Incremento: somar um a um ponteiro

 • Exemplo:
 float *p;
 p++; OU ++p;




                                        slide 27
Aritmética de ponteiros

 Aritmética de Ponteiros
 • Decremento: subtrair um de um ponteiro

 • Exemplo:
 float *p;
 p--; OU --p;




                                            slide 28
Aritmética de ponteiros

Comparação


– É possível comparar dois ponteiros em uma expressão
relacional.


-Exemplo:
É possível verificar se um ponteiro aponta para um endereço
menor que o endereço apontado por outro ponteiro:


if (px < py)
       printf(“px aponta para memória mais baixa que py”);




                                                              slide 29
Aritmética de ponteiros

Precedência de Operadores
– O operador   * e & têm maior precedência que os operadores
aritméticos, de forma que:


// obtém o valor do objeto apontador por px, adiciona 1 e
//atribui o valor a y
y = *px + 1;


// y recebe o conteúdo do endereço de px+1
y = *(px + 1);




                                                            slide 30
Expressões com ponteiros

Precedência de Operadores
– Os operadores   ++ e -- possuem precedência sobre o * e
operadores matemáticos


(*px)++; //soma 1 no conteúdo de px


*px++; //adianta uma posição de memória e obtém o seu
        //conteúdo


*(px--); //volta uma posição de memória e obtém
        //o seu conteúdo ⇒ o mesmo que *px--


              Cuidado com ponteiros perdidos!
                                                        slide 31
Aritmética de ponteiros

Diferença
– A operação de diferença entre elementos de mesmo tipo
permite saber quantos elementos existem entre um endereço e
outro.


– Exemplo:
   #include <stdio.h>
   void main(){
    int x = 5, *px = &x;
    int y = 10, *py = &y;
    printf("%i %dn", x, px);
    printf("%i %dn", y, py);
    printf("A diferenca entre eles eh: %d", py - px);
    getchar();
                                                          slide 32
   }
Ponteiro para estrutura

Como os outros tipos do C, as estruturas podem ser
referenciadas usando ponteiros.


typedef struct {
   int numero;
   char nome[10];
   float saldo;
} conta;
conta conta1;
conta *ptr;
ptr = &conta1;




                                                     slide 33
Ponteiro para estrutura

Há duas formas para recuperar os valores de uma estrutura
usando o ponteiro:


– Se st é uma estrutura e ptr é um ponteiro para st, para
referenciar a estrutura usamos:


(*ptr).elemento ou ptr->elemento




                                                            slide 34
Ponteiro para estrutura

conta conta1, *ptr;                     typedef struct {
conta1.num_conta = 2;                        int num_conta;
strcpy(conta1.nome, “Maria”);                char nome[10];
conta1.saldo = 482.25;                       float saldo;
ptr = &conta1;                          } conta;


printf(“Numero = %in”, (*ptr).num_conta);
printf(“Nome = %sn”, (*ptr).nome);
pritnf(“Saldo = %fn”, (*ptr).saldo);
/* ou */
printf(“Numero = %in”, ptr->num_conta);
printf(“Nome = %sn”, ptr->nome);
pritnf(“Saldo = %fn”, ptr->saldo);

                                                              slide 35
Ponteiro para estrutura

typdef struct{
    char titulo [40];
    float preco;
} livro ;


void main()
{
    livro liv, *ptr;
    gets (liv.titulo);
    scanf(“%f”, &liv.preco);
    ptr = &liv;
    ptr->preco = ptr->preco * 0.1;
    printf(“O preço de %s eh %f.”, pt->titulo, pt->preco);
}
                                                             slide 36
Ponteiro para estrutura

Exercício


– Fazer um programa que permita a entrada de registros com
nome, cidade e telefone de 1 pessoa usando a sintaxe de
ponteiro para estrutura.




                                                             slide 37
Ponteiro e Vetor

O nome de um vetor corresponde ao endereço do seu primeiro
elemento, isto é, se v for um vetor então v é igual ao &v[0].


Embora o nome de um vetor seja um ponteiro para o primeiro
elemento do vetor, esse ponteiro não pode ser alterado durante
a execução do programa a que pertence.




                                                                slide 38
Ponteiro e Vetor

Existem duas formas de colocar um ponteiro apontando para o
primeiro elemento de um vetor:


int v[3] = {10, 20, 30};
int * ptr;


ptr = &v[0]; // ou
ptr = v;




                                                        slide 39
Ponteiro e Vetor

Ao contrário de v, que é um vetor (ponteiro constante associado
à sua própria memória), ptr é um ponteiro puro, e portanto
pode receber endereços de diferentes elementos de um vetor.


int v[3] = {10, 20, 30};
int *ptr;
ptr = v;
printf(“%i %i”, v[0], *ptr); /* 10 10 */
ptr = &v[2]
printf(“%i %i”, v[2], *ptr); /* 30 30 */




                                                              slide 40
Ponteiro e Vetor

Outra sintaxe


– Colocamos em c o endereço do terceiro elemento de vetor:
char vetor[5] = { ‘a’, ‘e’, ‘i’, ‘o’, ‘u’ };
char *c;
c = &vetor[2];


– Portanto:
c[0] = ‘i’; c[1] = ‘o’ e c[2] = ‘u’.


– Se tivéssemos feito c = &vetor[3], então:
c[0] = ‘o’ e c[1] = ‘u’.



                                                             slide 41
Ponteiro e Vetor

Usando aritmética de ponteiros, como acessar os valores do
vetor usando a variável ponteiro c ?
   #include <stdio.h>
   void main(){
    int i;
    char vetor[5] = { 'a', 'e', 'i', 'o', 'u' };
    char *c;
    c = vetor;
    for (i = 0; i < 5; i++) {
       printf("n%c ", c[i]); /* ou */
       printf("%c ", *(c + i));
    }
    getchar();
   }
                                                       slide 42
Ponteiro e Vetor

Usando a sintaxe de ponteiro e de vetor de que forma
poderemos      acessar   o   caractere   ‘a’   presente   na   string
“OlaOleOli”?

   #include <stdio.h>
   #include <stdio.h>
   void main(){
    int i;
    char vetor[] = "OlaOleOli";
    char *c = vetor;
     printf("n%c ", vetor[2]);
     printf("%c ", *(c + 2));
     printf("n%c ", c[2]);
     printf("%c ", *(vetor + 2));
    getchar();
   }                                                              slide 43
Ponteiro e Vetor

Resumo
– Quando estamos manipulando ponteiros para vetores:


*c ⇒ representa o conteúdo da variável.


*(c + i) ⇒ representa o i-ésimo elemento do vetor referenciado
pelo ponteiro.


c[i] ⇒ também representa o i-ésimo elemento do vetor
referenciado pelo ponteiro.


c ⇒ representa o endereço de memória da variável.



                                                           slide 44
Ponteiro e Vetor

Exercício:


– Dadas as variáveis float vet_notas[5] e float *pont_notas,
imprimir a primeira e a quinta nota usando as duas variáveis.


printf(“A primeira nota: %i”, vet_notas[0]);
printf(“A primeira nota: %i”, *pont_notas);
printf(“A quinta nota: %i”, vet_notas[4];
printf(“A quinta nota: %i”, *(pont_notas+4));




                                                                slide 45
Passagem de Vetores para funções

Sempre que invocamos uma função e lhe passamos um vetor
como parâmetro, essa na realidade não recebe o vetor na sua
totalidade, mas apenas o endereço inicial do vetor, pois estamos
passando v que é igual a &v[0].


Se passarmos um endereço, então a variável que recebe terá
que ser um ponteiro para o tipo dos elementos do vetor.


Por essa razão é que no cabeçalho de uma função que recebe
um vetor como argumento aparece um ponteiro recebendo o
respectivo parâmetro.




                                                             slide 46
Passagem de Vetores para funções

#include <stdio.h>
void exibirVetor( int * ptr, int tam ){
int i;
for (i = 0; i < tam; i++) {
    printf("%i ", *(ptr + i));
}
}
void main(){
int vetor[] = {1, 2, 3, 4, 5};


exibirVetor(vetor, 5);
getchar();
}

                                          slide 47
Ponteiro e Vetor

Conclusão
– Variáveis para vetores podem ser declaradas como sendo
apontadores, pois os elementos do vetor, individualmente, têm
o mesmo tratamento independente se a variável que os
armazena é um vetor ou um apontador.


char vetor[5] equivale a char *vetor;


– Versões com ponteiros são mais rápidas!




                                                          slide 48
Ponteiro e String

Pode-se criar e manipular uma string fazendo uso apenas de um
ponteiro.


Exemplo:


char *palavra = “exemplo”;
for(; *palavra != ‘0’; palavra++)
printf(“%c ”, *palavra);




                                                          slide 49
Ponteiro e String

Exercícios:


– Usando ponteiro para string escreva um função que mostre a
string recebida por parâmetro na tela pela ordem em que está
escrita e pela ordem contrária.


– Usando ponteiro para string e diferença entre ponteiros
escreva uma função que calcule o comprimento de uma string
recebida por parâmetro. Obs.: Não pode usar a função strlen!




                                                               slide 50
Ponteiro para Ponteiro

Uma vez que os ponteiros ocupam espaço em memória, é
possível obter a sua posição através do operador de endereço &


Pergunta: Se quisermos armazenar o endereço de um ponteiro,
qual o tipo da variável que irá recebê-lo?




                                                           slide 51
Ponteiro para Ponteiro

Resposta:
– Suponha uma variável do tipo int chamada x
– Se quisermos armazenar seu endereço, declaramos um
ponteiro para o tipo da variável (int), isto é, colocamos um
asterisco entre o tipo da variável para que queremos apontar e
o nome do ponteiro

int *ptr_x;
– Se quisermos armazenar o endereço desse ponteiro, seguimos
exatamente os mesmos passos

int **ptr_ptr_x;

                                                           slide 52
Ponteiro para Ponteiro

Resposta:
– Os passos podem ser aplicados sucessivamente para qualquer
número de asteriscos
• int ****ptr = NULL; /* Válido */


– Ponteiro para ponteiro para ponteiro para ponteiro para
inteiro!
– Para chegar no destino final temos que fazer 4 indireções! Um
programa que use isso é difícil de entender e pode ficar muito
lento!




                                                            slide 53
Ponteiro para Ponteiro

#include <stdio.h>
void main(){
int x = 5;
int * ptr_x; /* Ponteiro para x */
int ** ptr_ptr_x; /* Ponteiro para o ponteiro de x */
/* Carga inicial dos ponteiros */
ptr_x = &x;
ptr_ptr_x = &ptr_x;


printf("x = %i e &x = %in", x, &x);
printf("x = %i e &x = %in", *ptr_x, ptr_x);
printf("x = %i e &x = %in", **ptr_ptr_x, *ptr_ptr_x);
getchar();
}
                                                         slide 54
Ponteiro para Ponteiro

int x = 5;
int * ptr_x;
int ** ptr_ptr_x;
/* Carga inicial dos ponteiros */
ptr_x = &x;
ptr_ptr_x = &ptr_x;




                                    slide 55
Ponteiro para Ponteiro

Aplicação
– Uma aplicação de ponteiros para ponteiros está nas strings, já
que strings são vetores, que por sua vez são ponteiros.


– Um vetor de strings seria justamente um ponteiro para um
ponteiro.




                                                             slide 56
Ponteiro para Ponteiro

ATENÇÃO
– Ponteiro é uma variável que não tem memória própria
associada, ela apenas contém o espaço para conter um
endereço.


– Portanto, só se pode utilizar o endereçamento através do
ponteiro depois que este está apontando para algum objeto já
existente.


– Não se deve fazer cargas iniciais de objetos apontados
por um ponteiro ainda não iniciado
– Exemplo:
int * p;
*p = 234;
                                                         slide 57
Ponteiro para Void

O termo void aplicado a um ponteiro significa um ponteiro que
pode acessar qualquer tipo de dado


Sintaxe: void * ptr;


Exemplo:
void main(){
void * ptr;
int x = 10;
ptr = &x;
 printf("x = %d e &x = %p", x, &x);
 printf("nx = %d e &x = %p", *((int*)ptr), ptr);
getchar();

                                                          slide 58
ALOCAÇÃO
DINÂMICA


           slide 59
Memória

Alocação
– Processo de vinculação de uma variável de programa a uma
célula de memória de um pool de memória disponível


Desalocação
– Processo    de   devolução   de   uma   célula de   memória
desvinculada de uma variável ao pool de memória disponível




                                                          slide 60
Memória

Tempo de vida de uma variável é o tempo durante o qual
ela está vinculada a uma localização de memória específica;


Tempo de vida de uma variável é o intervalo de tempo
decorrente   entre   a sua   alocação   (criação)   e   a   sua
desalocação (deleção).




                                                            slide 61
Memória

Classificação das Variáveis


Variáveis Globais
Variáveis Locais
Variáveis Heap-Dinâmicas Explícitas




                                      slide 62
Memória

– uso de variáveis globais (e estáticas):
       • espaço reservado para uma variável global existe
       enquanto o programa estiver sendo executado
– uso de variáveis locais:
       • espaço existe apenas enquanto a função que declarou
       a variável está sendo executada
       • liberado para outros usos quando a execução da função
       termina
– variáveis globais ou locais podem ser simples ou vetores:
       • para vetor, é necessário informar o número máximo de
       elementos pois o compilador precisa calcular o espaço a
       ser reservado



                                                              slide 63
Memória

– alocação dinâmica:
       • espaço de memória é requisitada em tempo de
       execução
       •     espaço     permanece     reservado    até   que     seja
       explicitamente liberado


– depois de liberado, espaço estará disponibilizado para outros
usos e não pode mais ser acessado


–   espaço    alocado    e   não    liberado   explicitamente,   será
automaticamente liberado quando ao final da execução




                                                                  slide 64
Memória

– memória estática:
      • código do programa
      • variáveis globais
      • variáveis estáticas
– memória dinâmica:
      • variáveis alocadas
        dinamicamente
      • memória livre
      • variáveis locais




                              slide 65
Memória

– alocação dinâmica de memória:
   • usa a memória livre
   • se o espaço de memória livre for
   menor que o espaço requisitado,
   a alocação não é feita e
   o programa pode prever
   tratamento de erro
– pilha de execução:
   • utilizada para alocar memória
   quando ocorre chamada de função
       – sistema reserva o espaço
        para as variáveis locais da função
        – quando a função termina,
        espaço é liberado (desempilhado)
   • se a pilha tentar crescer mais do
   que o espaço disponível existente,
   programa é abortado com erro
                                             slide 66
Memória




          slide 67
Variáveis Globais

São alocadas automaticamente no início da execução do
programa;


São desalocadas automaticamente no final da execução do
programa.


Não ocorre overhead em tempo de execução para alocação e
desalocação de memória!




                                                     slide 68
Variáveis Locais

São alocadas automaticamente no início da execução do
subprograma no qual foram declaradas;


São desalocadas automaticamente no final da execução do
subprograma no qual foram declaradas.


A alocação e desalocação destas variáveis é gerenciada pelo SO!




                                                            slide 69
Variáveis Heap-Dinâmicas Explícitas

Uma variável heap-dinâmica explícita é uma variável dinâmica
que pode ser alocada e desalocada pelo programador a
qualquer momento durante a execução do programa.


Uma variável heap é uma variável anônima, ou seja, sem nome,
e por isso só pode ser acessada através de seu endereço.


São usadas freqüentemente para estruturas dinâmicas que
precisam crescer e encolher durante a execução.




                                                           slide 70
Variáveis Heap-Dinâmicas Explícitas

A alocação de uma variável heap-dinâmica explícita é feita por
uma função chamada alocadora que retorna o endereço da
variável heap alocada.


Sintaxe da função malloc do C:

void * malloc (size_t n_bytes)




                                                           slide 71
Variáveis Heap-Dinâmicas Explícitas

Malloc
– recebe como parâmetro o número de bytes que se deseja
alocar
– retorna um ponteiro genérico para o endereço inicial da área
de
memória alocada, se houver espaço livre:
• ponteiro genérico é representado por void*
• ponteiro é convertido automaticamente para o tipo apropriado
• ponteiro pode ser convertido explicitamente
– retorna um endereço nulo, se não houver espaço livre:
• representado pelo símbolo NULL



                                                            slide 72
Variáveis Heap-Dinâmicas Explícitas

Função malloc do C


– Exemplo:
   void main () {
   int *ptrInt;
   ...
   ptrInt = malloc(sizeof(int));
   ...
   }

          Cria uma variável heap
             do tipo int e coloca
              seu endereço no
              ponteiro ptrInt.

                                      slide 73
Variáveis Heap-Dinâmicas Explícitas

Exemplo da função malloc do C


(i) int * ptrInt;




(ii) ptrInt = malloc(sizeof(int));


                                     Variável do tipo int,
                                     anônima, alocada
                                     dinamicamente (HEAP).

                                                             slide 74
Variáveis Heap-Dinâmicas Explícitas




                                      slide 75
Variáveis Heap-Dinâmicas Explícitas

Malloc
– v armazena endereço inicial de uma área contínua de
memória suficiente para armazenar 10 valores inteiros
– v pode ser tratado como um vetor declarado estaticamente
   • v aponta para o inicio da área alocada
   • v[0] acessa o espaço para o primeiro elemento
   • v[1] acessa o segundo
   • .... até v[9]




                                                             slide 76
Variáveis Heap-Dinâmicas Explícitas

Exemplo da função malloc do C


typedef struct {
 int dia, mes, ano;
} data;
data *d;
d = malloc (sizeof (data));
d->dia = 31; d->mes = 12; d->ano = 2008;




                                           slide 77
Variáveis Heap-Dinâmicas Explícitas
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    // ponteiro para uma variável do tipo inteiro
    int *ponteiro;
    // aloca memória para um int
    ponteiro = malloc(sizeof(int));
    // testa se a memória foi alocada com sucesso
    if(ponteiro)
     printf("Memoria alocada com sucesso.n");
    else
     printf("Nao foi possivel alocar a memoria.n");
    // atribui valor à memória alocada
    *ponteiro = 45;
    // obtém o valor atribuído
    printf("Valor: %dnn", *ponteiro);
    // libera a memória
    free(ponteiro);
    system("PAUSE");
    return 0;
}                                                      slide 78
Variáveis Heap-Dinâmicas Explícitas
#include <stdio.h>                                  // atribui valores aos elementos do array
#include <stdlib.h>                                  for(i = 0; i < quant; i++){
int main(int argc, char *argv[])                            ponteiro[i] = i * 2;
{                                                       }
 int i;
 // quantidade de elementos na matriz                   // exibe os valores
 int quant = 10;                                        for(i = 0; i < quant; i++){
 // ponteiro para o bloco de memória                      printf("%d ", ponteiro[i]);
 int *ponteiro;                                         }
 // aloca memória para uma matriz de inteiros
 ponteiro = malloc(quant * sizeof(int));                // libera a memória
 // testa se a memória foi alocada com sucesso          free(ponteiro);
 if(ponteiro)
   printf("Memoria alocada com sucesso.n");            printf("nn");
 else{                                                  system("PAUSE");
  printf("Nao foi possivel alocar a memoria.n");       return 0;
     exit(1);                                       }
 }



                                                                                           slide 79
Variáveis Heap-Dinâmicas Explícitas

Exemplo da função malloc do C


int *vector = NULL; /* declaração do ponteiro */
/* alocação de memória para o vector */
vector = (int*) malloc(25 * sizeof(int));
/* altera o valor da posição dez para trinta e quatro */
vector[10] = 34;
free(vector); /* liberta a área de memória alocada */




                                                           slide 80
Variáveis Heap-Dinâmicas Explícitas

A desalocação de uma variável heap-dinâmica explícita é feita
por uma função chamada desalocadora.


Sintaxe da função free do C:

void free( void *)




                                                          slide 81
Variáveis Heap-Dinâmicas Explícitas

Free
– recebe como parâmetro o ponteiro da memória a ser liberada
       • a função free deve receber um endereço de memória
que tenha sido alocado dinamicamente




                                                          slide 82
Variáveis Heap-Dinâmicas Explícitas

Exemplo da função free do C:
void main () {
int *ptrInt;
...
ptrInt = malloc(sizeof(int));
...
free(ptrInt);
}




                                      slide 83
Variáveis Heap-Dinâmicas Explícitas

Função free do C


– IMPORTANTE:
• A função free não desaloca o ponteiro.


Ela desaloca apenas a variável heap cujo endereço estava
armazenado no ponteiro;


• A função free também não “limpa” o ponteiro, ele permanece
com o mesmo endereço, mas este se refere a uma variável que
não mais lhe pertence!




                                                         slide 84
Variáveis Heap-Dinâmicas Explícitas

– Uma variável heap permanece acessível enquanto houver uma
variável do tipo ponteiro que armazene seu endereço.


Problemas
– Referência Perdida
– Variável Heap-Dinâmica Perdida
(Lixo de Memória)




                                                        slide 85
Variáveis Heap-Dinâmicas Explícitas

Problemas - Referência Perdida:
– É um ponteiro que contém o endereço de um variável heap-
dinâmica desalocada


– Exemplo 1:
void main(){
float *ptr;
…
    ptr = malloc(sizeof(float));
    …
free(ptr);
    …
    printf(“%f”, *ptr); ⇐ Referência Perdida
}
                                                       slide 86
Variáveis Heap-Dinâmicas Explícitas

Problemas - Referência Perdida:


– Exemplo 2:
void main(){
float *ptr1, *ptr2;
…
 ptr1 = malloc(sizeof(float));
 …
 ptr2 = ptr1;
 …
 free(ptr1);
 …
 *ptr2 = 15.9; ⇐ Referência Perdida

                                      slide 87
Variáveis Heap-Dinâmicas Explícitas

Problemas
– Heap-Dinâmica Perdida:


• É uma variável heap-dinâmica alocada porém não mais
acessível.


• Também conhecida como lixo de memória




                                                  slide 88
Variáveis Heap-Dinâmicas Explícitas

Problemas - Heap-Dinâmica Perdida:


– Exemplo:
void main(){
int *ptr1, i;
...
    for(i = 0; i < 10; i++){
      ptr1 = malloc(sizeof(int)); ⇐ cria lixo
      *ptr1 = i;
}
...
}



                                                slide 89
Variáveis Heap-Dinâmicas Explícitas

Exercícios:
– Dada a variável float *pont_notas, alocar dinamicamente
espaço para armazenar 5 notas, ler as 5 notas e imprimir
a primeira e a quinta nota lidas.


– Fazer um programa que permita a entrada de dez números e
que diga qual é o maior e o menor número. Use ponteiro e
alocação dinâmica de memória.




                                                           slide 90
Variáveis Heap-Dinâmicas Explícitas

Exercícios:
– Escreva um programa que leia um número inteiro positivo n
seguido de n números inteiros e imprima esses n números em
ordem invertida.
Por exemplo, ao receber 5 222 333 444 555 666 o seu
programa deve imprimir 666 555 444 333 222. O seu programa
não deve impor limitações sobre o valor de n.




                                                        slide 91
Referências

 • Material Professora Karina Oliveira (Unicap)
 • Introdução a Estruturas de Dados - com técnicas
   de programação em C
   W. Celes, R. Cerqueira, J.L. Rangel
   Ed. Campus - ISBN 85-352-1228-0
 • Material Professor Marco A. Casanova - PUC-Rio
 • C Completo e Total - 3ª ed. - Herbert Schildt -
   Makron Books




                                                slide 92
Estrutura de Dados I




          UNICAP




 Eduardo Araújo Oliveira
 http://eduoliveira.com




                           slide 93

Ponteiros e Alocação Dinâmica

  • 1.
    Estrutura de DadosI UNICAP Eduardo Araújo Oliveira http://eduoliveira.com Twitter: @oliveiraeduardo slide 1
  • 2.
    PONTEIROS slide 2
  • 3.
  • 4.
    Uma memória éuma seqüência de células de armazenamento (ou posições) slide 4
  • 5.
    “Uma variável éum aglomerado de uma ou mais células de memória”. slide 5
  • 6.
    Variável Atributos de umavariável – nome: seqüência de caracteres utilizada para identificar a variável; – tipo: é o tipo dos dados que serão armazenados na variável; – conteúdo: é o valor armazenado na variável; – endereço: é a localização (posição) da variável na memória; slide 6
  • 7.
  • 8.
    Variável – char sexo= ‘M’; • Nome da variável: sexo • Tipo da variável: char • Conteúdo da variável: ‘M’ • Endereço da variável: 5 slide 8
  • 9.
    Variável – int idade= 31; • Nome da variável: idade • Tipo da variável: int • Conteúdo da variável: 31 • Endereço da variável: 1 slide 9
  • 10.
    Variável – int idade= 31; • Nome da variável: idade • Tipo da variável: int • Conteúdo da variável: 31 • Endereço da variável: 1 slide 10
  • 11.
    Variáveis do tipoponteiro Ponteiros, como o próprio nome diz, é um tipo de variável que aponta para outra (de um tipo qualquer). • Um ponteiro guarda o endereço de memória de uma variável. • Sintaxe: Variáveis que são ponteiros são representadas da seguinte forma: tipo_dado_apontado *nome_variavel; – int *ptr1; – char *ptr2; slide 11
  • 12.
    Variáveis do tipoponteiro Utilizado para: – Substituir a manipulação de vetores/matrizes com eficiência; – Passar valores e mudar valores dentro de funções; – Manipular arquivos; – Aumento de eficiência para algumas rotinas; – Possibilitar a alocação dinâmica de memória. slide 12
  • 13.
    Variáveis do tipoponteiro Operadores de Endereço –O & (“endereço de”) antes de um nome de variável qualquer retorna o endereço desta variável. – O * (“conteúdo de”) antes de um nome de variável do tipo ponteiro retorna o conteúdo armazenado na posição de memória apontada. Operação chamada de indireção ou desreferenciamento. slide 13
  • 14.
    Variáveis do tipoponteiro char letra = ‘Z’; char *ptr = &letra; – Nome da variável: ptr – Tipo da variável: char * – Conteúdo da variável: 1 – Endereço da variável: 4 slide 14
  • 15.
    Variáveis do tipoponteiro void main(){ int x = 50; int * ptr; printf (“%i”,x); /*exibe o conteúdo da variável x (50)*/ printf (“%p”,&x); /*exibe o endereço da variável x*/ ptr = &x; /*armazena em ptr o endereço de x*/ printf (“%p”,ptr); /*exibe o conteúdo da variável ptr*/ printf (“%p”,&ptr); /*exibe o endereço da variável ptr*/ printf (“%i”,*ptr); /*exibe o conteúdo da variável x*/ } slide 15
  • 16.
    Variáveis do tipoponteiro void main () { int x; int *ptr; printf(“Digite um valor para x:”); scanf(“%i”, &x); ptr = &x; *ptr = *ptr + 1; printf(“O valor de x é %i”, *ptr); printf(“O endereço de x é %i”, ptr); } slide 16
  • 17.
    Variáveis do tipoponteiro *p ⇒ representa o conteúdo da variável apontada. p ⇒ representa o endereço de memória da variável apontada. slide 17
  • 18.
    Variáveis do tipoponteiro Observação – Quando queremos indicar que um ponteiro está “vazio”, ou seja, não contém um endereço de uma variável, atribuímos a ele o valor NULL. • NULL: Endereço de memória 0 (zero). Esta posição de memória não é utilizada para armazenar dados. – Exemplo: ptr = NULL; /* inicializa o ponteiro ptr*/ slide 18
  • 19.
    Variáveis do tipoponteiro int a = 5, b = 7; (neste exemplo, um int ocupa 2bytes na memoria, e não 4, como em outros exemplos). int *ptr = NULL; ptr = &a; slide 19
  • 20.
    Variáveis do tipoponteiro Atribuição – Podemos atribuir o conteúdo de um ponteiro a outro. Dessa forma, teremos dois ponteiros referenciando o mesmo endereço de memória. • Exemplo: int a = 5, *p1, *p2; p1 = &a; p2 = p1; slide 20
  • 21.
    Aritmética de ponteiros Sendoos ponteiros números que representam posições de memória, podem ser realizadas algumas operações aritméticas sobre eles: – Incremento; – Decremento; – Diferença; – Comparação. slide 21
  • 22.
    Aritmética de ponteiros Incrementoe Decremento – Um ponteiro pode ser incrementado como qualquer outra variável – Se ptr é um ponteiro para um determinado tipo, quando ptr é incrementado, por exemplo, de uma unidade, o endereço que passa a conter é igual ao endereço anterior de ptr + sizeof(tipo) para que o ponteiro aponta – A mesma situação ocorre para a operação de decremento, onde o endereço que passa a conter é igual ao endereço anterior de ptr - sizeof(tipo) para que o ponteiro aponta slide 22
  • 23.
    Aritmética de ponteiros intmain(int argc, char **argv) { int x = 5, *px = &x; double y = 5.0, *py = &y; printf("%i %in", x, px); printf("%i %in", x+1, px+1); 5 2293524 6 2293528 printf("%f %in", y, py); 5.000000 2293512 printf("%f %in", y+1, py+1); 6.000000 2293520 5 2293524 printf("%i %in", x, px); printf("%i %in", x-1, px-1); 4 2293520 5.000000 2293512 printf("%f %in", y, py); 4.000000 2293504 printf("%f %in", y-1, py-1); getchar(); return 0; } slide 23
  • 24.
    Aritmética de ponteiros Alémdas operações de incremento e decremento, só é possível somar ou subtrair inteiros de um ponteiro: – ptr = ptr + 7; • Esta expressão faz com que ptr aponte para o sétimo elemento a partir do seu elemento atual. – ptr = ptr – 5; • Esta expressão faz com que ptr aponte para o quinto elemento anterior ao elemento atual. Somar 2 a um ponteiro de inteiros de 4 bytes irá incrementá-lo em 8. Isso faz com que o incremento de um ponteiro o posicione para o próximo elemento em um arranjo, o que geralmente é o resultado intencionado. slide 24
  • 25.
    Aritmética de ponteiros • Adição: somar um inteiro a um ponteiro • Exemplo: float *p; p = p + 2; Fator de escala: é o tamanho (número de bytes) do objeto apontado. • Exemplo: fator de escala de uma variável do tipo float: 4 p = p + 2 significa p = p + 2 * fator de escala slide 25
  • 26.
    Aritmética de ponteiros • Subtração: subtrair um inteiro de um ponteiro • Exemplo: float *p; p = p - 3; p = p - 3 significa p = p - 3 * fator de escala slide 26
  • 27.
    Aritmética de ponteiros Aritmética de Ponteiros • Incremento: somar um a um ponteiro • Exemplo: float *p; p++; OU ++p; slide 27
  • 28.
    Aritmética de ponteiros Aritmética de Ponteiros • Decremento: subtrair um de um ponteiro • Exemplo: float *p; p--; OU --p; slide 28
  • 29.
    Aritmética de ponteiros Comparação –É possível comparar dois ponteiros em uma expressão relacional. -Exemplo: É possível verificar se um ponteiro aponta para um endereço menor que o endereço apontado por outro ponteiro: if (px < py) printf(“px aponta para memória mais baixa que py”); slide 29
  • 30.
    Aritmética de ponteiros Precedênciade Operadores – O operador * e & têm maior precedência que os operadores aritméticos, de forma que: // obtém o valor do objeto apontador por px, adiciona 1 e //atribui o valor a y y = *px + 1; // y recebe o conteúdo do endereço de px+1 y = *(px + 1); slide 30
  • 31.
    Expressões com ponteiros Precedênciade Operadores – Os operadores ++ e -- possuem precedência sobre o * e operadores matemáticos (*px)++; //soma 1 no conteúdo de px *px++; //adianta uma posição de memória e obtém o seu //conteúdo *(px--); //volta uma posição de memória e obtém //o seu conteúdo ⇒ o mesmo que *px-- Cuidado com ponteiros perdidos! slide 31
  • 32.
    Aritmética de ponteiros Diferença –A operação de diferença entre elementos de mesmo tipo permite saber quantos elementos existem entre um endereço e outro. – Exemplo: #include <stdio.h> void main(){ int x = 5, *px = &x; int y = 10, *py = &y; printf("%i %dn", x, px); printf("%i %dn", y, py); printf("A diferenca entre eles eh: %d", py - px); getchar(); slide 32 }
  • 33.
    Ponteiro para estrutura Comoos outros tipos do C, as estruturas podem ser referenciadas usando ponteiros. typedef struct { int numero; char nome[10]; float saldo; } conta; conta conta1; conta *ptr; ptr = &conta1; slide 33
  • 34.
    Ponteiro para estrutura Háduas formas para recuperar os valores de uma estrutura usando o ponteiro: – Se st é uma estrutura e ptr é um ponteiro para st, para referenciar a estrutura usamos: (*ptr).elemento ou ptr->elemento slide 34
  • 35.
    Ponteiro para estrutura contaconta1, *ptr; typedef struct { conta1.num_conta = 2; int num_conta; strcpy(conta1.nome, “Maria”); char nome[10]; conta1.saldo = 482.25; float saldo; ptr = &conta1; } conta; printf(“Numero = %in”, (*ptr).num_conta); printf(“Nome = %sn”, (*ptr).nome); pritnf(“Saldo = %fn”, (*ptr).saldo); /* ou */ printf(“Numero = %in”, ptr->num_conta); printf(“Nome = %sn”, ptr->nome); pritnf(“Saldo = %fn”, ptr->saldo); slide 35
  • 36.
    Ponteiro para estrutura typdefstruct{ char titulo [40]; float preco; } livro ; void main() { livro liv, *ptr; gets (liv.titulo); scanf(“%f”, &liv.preco); ptr = &liv; ptr->preco = ptr->preco * 0.1; printf(“O preço de %s eh %f.”, pt->titulo, pt->preco); } slide 36
  • 37.
    Ponteiro para estrutura Exercício –Fazer um programa que permita a entrada de registros com nome, cidade e telefone de 1 pessoa usando a sintaxe de ponteiro para estrutura. slide 37
  • 38.
    Ponteiro e Vetor Onome de um vetor corresponde ao endereço do seu primeiro elemento, isto é, se v for um vetor então v é igual ao &v[0]. Embora o nome de um vetor seja um ponteiro para o primeiro elemento do vetor, esse ponteiro não pode ser alterado durante a execução do programa a que pertence. slide 38
  • 39.
    Ponteiro e Vetor Existemduas formas de colocar um ponteiro apontando para o primeiro elemento de um vetor: int v[3] = {10, 20, 30}; int * ptr; ptr = &v[0]; // ou ptr = v; slide 39
  • 40.
    Ponteiro e Vetor Aocontrário de v, que é um vetor (ponteiro constante associado à sua própria memória), ptr é um ponteiro puro, e portanto pode receber endereços de diferentes elementos de um vetor. int v[3] = {10, 20, 30}; int *ptr; ptr = v; printf(“%i %i”, v[0], *ptr); /* 10 10 */ ptr = &v[2] printf(“%i %i”, v[2], *ptr); /* 30 30 */ slide 40
  • 41.
    Ponteiro e Vetor Outrasintaxe – Colocamos em c o endereço do terceiro elemento de vetor: char vetor[5] = { ‘a’, ‘e’, ‘i’, ‘o’, ‘u’ }; char *c; c = &vetor[2]; – Portanto: c[0] = ‘i’; c[1] = ‘o’ e c[2] = ‘u’. – Se tivéssemos feito c = &vetor[3], então: c[0] = ‘o’ e c[1] = ‘u’. slide 41
  • 42.
    Ponteiro e Vetor Usandoaritmética de ponteiros, como acessar os valores do vetor usando a variável ponteiro c ? #include <stdio.h> void main(){ int i; char vetor[5] = { 'a', 'e', 'i', 'o', 'u' }; char *c; c = vetor; for (i = 0; i < 5; i++) { printf("n%c ", c[i]); /* ou */ printf("%c ", *(c + i)); } getchar(); } slide 42
  • 43.
    Ponteiro e Vetor Usandoa sintaxe de ponteiro e de vetor de que forma poderemos acessar o caractere ‘a’ presente na string “OlaOleOli”? #include <stdio.h> #include <stdio.h> void main(){ int i; char vetor[] = "OlaOleOli"; char *c = vetor; printf("n%c ", vetor[2]); printf("%c ", *(c + 2)); printf("n%c ", c[2]); printf("%c ", *(vetor + 2)); getchar(); } slide 43
  • 44.
    Ponteiro e Vetor Resumo –Quando estamos manipulando ponteiros para vetores: *c ⇒ representa o conteúdo da variável. *(c + i) ⇒ representa o i-ésimo elemento do vetor referenciado pelo ponteiro. c[i] ⇒ também representa o i-ésimo elemento do vetor referenciado pelo ponteiro. c ⇒ representa o endereço de memória da variável. slide 44
  • 45.
    Ponteiro e Vetor Exercício: –Dadas as variáveis float vet_notas[5] e float *pont_notas, imprimir a primeira e a quinta nota usando as duas variáveis. printf(“A primeira nota: %i”, vet_notas[0]); printf(“A primeira nota: %i”, *pont_notas); printf(“A quinta nota: %i”, vet_notas[4]; printf(“A quinta nota: %i”, *(pont_notas+4)); slide 45
  • 46.
    Passagem de Vetorespara funções Sempre que invocamos uma função e lhe passamos um vetor como parâmetro, essa na realidade não recebe o vetor na sua totalidade, mas apenas o endereço inicial do vetor, pois estamos passando v que é igual a &v[0]. Se passarmos um endereço, então a variável que recebe terá que ser um ponteiro para o tipo dos elementos do vetor. Por essa razão é que no cabeçalho de uma função que recebe um vetor como argumento aparece um ponteiro recebendo o respectivo parâmetro. slide 46
  • 47.
    Passagem de Vetorespara funções #include <stdio.h> void exibirVetor( int * ptr, int tam ){ int i; for (i = 0; i < tam; i++) { printf("%i ", *(ptr + i)); } } void main(){ int vetor[] = {1, 2, 3, 4, 5}; exibirVetor(vetor, 5); getchar(); } slide 47
  • 48.
    Ponteiro e Vetor Conclusão –Variáveis para vetores podem ser declaradas como sendo apontadores, pois os elementos do vetor, individualmente, têm o mesmo tratamento independente se a variável que os armazena é um vetor ou um apontador. char vetor[5] equivale a char *vetor; – Versões com ponteiros são mais rápidas! slide 48
  • 49.
    Ponteiro e String Pode-secriar e manipular uma string fazendo uso apenas de um ponteiro. Exemplo: char *palavra = “exemplo”; for(; *palavra != ‘0’; palavra++) printf(“%c ”, *palavra); slide 49
  • 50.
    Ponteiro e String Exercícios: –Usando ponteiro para string escreva um função que mostre a string recebida por parâmetro na tela pela ordem em que está escrita e pela ordem contrária. – Usando ponteiro para string e diferença entre ponteiros escreva uma função que calcule o comprimento de uma string recebida por parâmetro. Obs.: Não pode usar a função strlen! slide 50
  • 51.
    Ponteiro para Ponteiro Umavez que os ponteiros ocupam espaço em memória, é possível obter a sua posição através do operador de endereço & Pergunta: Se quisermos armazenar o endereço de um ponteiro, qual o tipo da variável que irá recebê-lo? slide 51
  • 52.
    Ponteiro para Ponteiro Resposta: –Suponha uma variável do tipo int chamada x – Se quisermos armazenar seu endereço, declaramos um ponteiro para o tipo da variável (int), isto é, colocamos um asterisco entre o tipo da variável para que queremos apontar e o nome do ponteiro int *ptr_x; – Se quisermos armazenar o endereço desse ponteiro, seguimos exatamente os mesmos passos int **ptr_ptr_x; slide 52
  • 53.
    Ponteiro para Ponteiro Resposta: –Os passos podem ser aplicados sucessivamente para qualquer número de asteriscos • int ****ptr = NULL; /* Válido */ – Ponteiro para ponteiro para ponteiro para ponteiro para inteiro! – Para chegar no destino final temos que fazer 4 indireções! Um programa que use isso é difícil de entender e pode ficar muito lento! slide 53
  • 54.
    Ponteiro para Ponteiro #include<stdio.h> void main(){ int x = 5; int * ptr_x; /* Ponteiro para x */ int ** ptr_ptr_x; /* Ponteiro para o ponteiro de x */ /* Carga inicial dos ponteiros */ ptr_x = &x; ptr_ptr_x = &ptr_x; printf("x = %i e &x = %in", x, &x); printf("x = %i e &x = %in", *ptr_x, ptr_x); printf("x = %i e &x = %in", **ptr_ptr_x, *ptr_ptr_x); getchar(); } slide 54
  • 55.
    Ponteiro para Ponteiro intx = 5; int * ptr_x; int ** ptr_ptr_x; /* Carga inicial dos ponteiros */ ptr_x = &x; ptr_ptr_x = &ptr_x; slide 55
  • 56.
    Ponteiro para Ponteiro Aplicação –Uma aplicação de ponteiros para ponteiros está nas strings, já que strings são vetores, que por sua vez são ponteiros. – Um vetor de strings seria justamente um ponteiro para um ponteiro. slide 56
  • 57.
    Ponteiro para Ponteiro ATENÇÃO –Ponteiro é uma variável que não tem memória própria associada, ela apenas contém o espaço para conter um endereço. – Portanto, só se pode utilizar o endereçamento através do ponteiro depois que este está apontando para algum objeto já existente. – Não se deve fazer cargas iniciais de objetos apontados por um ponteiro ainda não iniciado – Exemplo: int * p; *p = 234; slide 57
  • 58.
    Ponteiro para Void Otermo void aplicado a um ponteiro significa um ponteiro que pode acessar qualquer tipo de dado Sintaxe: void * ptr; Exemplo: void main(){ void * ptr; int x = 10; ptr = &x; printf("x = %d e &x = %p", x, &x); printf("nx = %d e &x = %p", *((int*)ptr), ptr); getchar(); slide 58
  • 59.
  • 60.
    Memória Alocação – Processo devinculação de uma variável de programa a uma célula de memória de um pool de memória disponível Desalocação – Processo de devolução de uma célula de memória desvinculada de uma variável ao pool de memória disponível slide 60
  • 61.
    Memória Tempo de vidade uma variável é o tempo durante o qual ela está vinculada a uma localização de memória específica; Tempo de vida de uma variável é o intervalo de tempo decorrente entre a sua alocação (criação) e a sua desalocação (deleção). slide 61
  • 62.
    Memória Classificação das Variáveis VariáveisGlobais Variáveis Locais Variáveis Heap-Dinâmicas Explícitas slide 62
  • 63.
    Memória – uso devariáveis globais (e estáticas): • espaço reservado para uma variável global existe enquanto o programa estiver sendo executado – uso de variáveis locais: • espaço existe apenas enquanto a função que declarou a variável está sendo executada • liberado para outros usos quando a execução da função termina – variáveis globais ou locais podem ser simples ou vetores: • para vetor, é necessário informar o número máximo de elementos pois o compilador precisa calcular o espaço a ser reservado slide 63
  • 64.
    Memória – alocação dinâmica: • espaço de memória é requisitada em tempo de execução • espaço permanece reservado até que seja explicitamente liberado – depois de liberado, espaço estará disponibilizado para outros usos e não pode mais ser acessado – espaço alocado e não liberado explicitamente, será automaticamente liberado quando ao final da execução slide 64
  • 65.
    Memória – memória estática: • código do programa • variáveis globais • variáveis estáticas – memória dinâmica: • variáveis alocadas dinamicamente • memória livre • variáveis locais slide 65
  • 66.
    Memória – alocação dinâmicade memória: • usa a memória livre • se o espaço de memória livre for menor que o espaço requisitado, a alocação não é feita e o programa pode prever tratamento de erro – pilha de execução: • utilizada para alocar memória quando ocorre chamada de função – sistema reserva o espaço para as variáveis locais da função – quando a função termina, espaço é liberado (desempilhado) • se a pilha tentar crescer mais do que o espaço disponível existente, programa é abortado com erro slide 66
  • 67.
    Memória slide 67
  • 68.
    Variáveis Globais São alocadasautomaticamente no início da execução do programa; São desalocadas automaticamente no final da execução do programa. Não ocorre overhead em tempo de execução para alocação e desalocação de memória! slide 68
  • 69.
    Variáveis Locais São alocadasautomaticamente no início da execução do subprograma no qual foram declaradas; São desalocadas automaticamente no final da execução do subprograma no qual foram declaradas. A alocação e desalocação destas variáveis é gerenciada pelo SO! slide 69
  • 70.
    Variáveis Heap-Dinâmicas Explícitas Umavariável heap-dinâmica explícita é uma variável dinâmica que pode ser alocada e desalocada pelo programador a qualquer momento durante a execução do programa. Uma variável heap é uma variável anônima, ou seja, sem nome, e por isso só pode ser acessada através de seu endereço. São usadas freqüentemente para estruturas dinâmicas que precisam crescer e encolher durante a execução. slide 70
  • 71.
    Variáveis Heap-Dinâmicas Explícitas Aalocação de uma variável heap-dinâmica explícita é feita por uma função chamada alocadora que retorna o endereço da variável heap alocada. Sintaxe da função malloc do C: void * malloc (size_t n_bytes) slide 71
  • 72.
    Variáveis Heap-Dinâmicas Explícitas Malloc –recebe como parâmetro o número de bytes que se deseja alocar – retorna um ponteiro genérico para o endereço inicial da área de memória alocada, se houver espaço livre: • ponteiro genérico é representado por void* • ponteiro é convertido automaticamente para o tipo apropriado • ponteiro pode ser convertido explicitamente – retorna um endereço nulo, se não houver espaço livre: • representado pelo símbolo NULL slide 72
  • 73.
    Variáveis Heap-Dinâmicas Explícitas Funçãomalloc do C – Exemplo: void main () { int *ptrInt; ... ptrInt = malloc(sizeof(int)); ... } Cria uma variável heap do tipo int e coloca seu endereço no ponteiro ptrInt. slide 73
  • 74.
    Variáveis Heap-Dinâmicas Explícitas Exemploda função malloc do C (i) int * ptrInt; (ii) ptrInt = malloc(sizeof(int)); Variável do tipo int, anônima, alocada dinamicamente (HEAP). slide 74
  • 75.
  • 76.
    Variáveis Heap-Dinâmicas Explícitas Malloc –v armazena endereço inicial de uma área contínua de memória suficiente para armazenar 10 valores inteiros – v pode ser tratado como um vetor declarado estaticamente • v aponta para o inicio da área alocada • v[0] acessa o espaço para o primeiro elemento • v[1] acessa o segundo • .... até v[9] slide 76
  • 77.
    Variáveis Heap-Dinâmicas Explícitas Exemploda função malloc do C typedef struct { int dia, mes, ano; } data; data *d; d = malloc (sizeof (data)); d->dia = 31; d->mes = 12; d->ano = 2008; slide 77
  • 78.
    Variáveis Heap-Dinâmicas Explícitas #include<stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { // ponteiro para uma variável do tipo inteiro int *ponteiro; // aloca memória para um int ponteiro = malloc(sizeof(int)); // testa se a memória foi alocada com sucesso if(ponteiro) printf("Memoria alocada com sucesso.n"); else printf("Nao foi possivel alocar a memoria.n"); // atribui valor à memória alocada *ponteiro = 45; // obtém o valor atribuído printf("Valor: %dnn", *ponteiro); // libera a memória free(ponteiro); system("PAUSE"); return 0; } slide 78
  • 79.
    Variáveis Heap-Dinâmicas Explícitas #include<stdio.h> // atribui valores aos elementos do array #include <stdlib.h> for(i = 0; i < quant; i++){ int main(int argc, char *argv[]) ponteiro[i] = i * 2; { } int i; // quantidade de elementos na matriz // exibe os valores int quant = 10; for(i = 0; i < quant; i++){ // ponteiro para o bloco de memória printf("%d ", ponteiro[i]); int *ponteiro; } // aloca memória para uma matriz de inteiros ponteiro = malloc(quant * sizeof(int)); // libera a memória // testa se a memória foi alocada com sucesso free(ponteiro); if(ponteiro) printf("Memoria alocada com sucesso.n"); printf("nn"); else{ system("PAUSE"); printf("Nao foi possivel alocar a memoria.n"); return 0; exit(1); } } slide 79
  • 80.
    Variáveis Heap-Dinâmicas Explícitas Exemploda função malloc do C int *vector = NULL; /* declaração do ponteiro */ /* alocação de memória para o vector */ vector = (int*) malloc(25 * sizeof(int)); /* altera o valor da posição dez para trinta e quatro */ vector[10] = 34; free(vector); /* liberta a área de memória alocada */ slide 80
  • 81.
    Variáveis Heap-Dinâmicas Explícitas Adesalocação de uma variável heap-dinâmica explícita é feita por uma função chamada desalocadora. Sintaxe da função free do C: void free( void *) slide 81
  • 82.
    Variáveis Heap-Dinâmicas Explícitas Free –recebe como parâmetro o ponteiro da memória a ser liberada • a função free deve receber um endereço de memória que tenha sido alocado dinamicamente slide 82
  • 83.
    Variáveis Heap-Dinâmicas Explícitas Exemploda função free do C: void main () { int *ptrInt; ... ptrInt = malloc(sizeof(int)); ... free(ptrInt); } slide 83
  • 84.
    Variáveis Heap-Dinâmicas Explícitas Funçãofree do C – IMPORTANTE: • A função free não desaloca o ponteiro. Ela desaloca apenas a variável heap cujo endereço estava armazenado no ponteiro; • A função free também não “limpa” o ponteiro, ele permanece com o mesmo endereço, mas este se refere a uma variável que não mais lhe pertence! slide 84
  • 85.
    Variáveis Heap-Dinâmicas Explícitas –Uma variável heap permanece acessível enquanto houver uma variável do tipo ponteiro que armazene seu endereço. Problemas – Referência Perdida – Variável Heap-Dinâmica Perdida (Lixo de Memória) slide 85
  • 86.
    Variáveis Heap-Dinâmicas Explícitas Problemas- Referência Perdida: – É um ponteiro que contém o endereço de um variável heap- dinâmica desalocada – Exemplo 1: void main(){ float *ptr; … ptr = malloc(sizeof(float)); … free(ptr); … printf(“%f”, *ptr); ⇐ Referência Perdida } slide 86
  • 87.
    Variáveis Heap-Dinâmicas Explícitas Problemas- Referência Perdida: – Exemplo 2: void main(){ float *ptr1, *ptr2; … ptr1 = malloc(sizeof(float)); … ptr2 = ptr1; … free(ptr1); … *ptr2 = 15.9; ⇐ Referência Perdida slide 87
  • 88.
    Variáveis Heap-Dinâmicas Explícitas Problemas –Heap-Dinâmica Perdida: • É uma variável heap-dinâmica alocada porém não mais acessível. • Também conhecida como lixo de memória slide 88
  • 89.
    Variáveis Heap-Dinâmicas Explícitas Problemas- Heap-Dinâmica Perdida: – Exemplo: void main(){ int *ptr1, i; ... for(i = 0; i < 10; i++){ ptr1 = malloc(sizeof(int)); ⇐ cria lixo *ptr1 = i; } ... } slide 89
  • 90.
    Variáveis Heap-Dinâmicas Explícitas Exercícios: –Dada a variável float *pont_notas, alocar dinamicamente espaço para armazenar 5 notas, ler as 5 notas e imprimir a primeira e a quinta nota lidas. – Fazer um programa que permita a entrada de dez números e que diga qual é o maior e o menor número. Use ponteiro e alocação dinâmica de memória. slide 90
  • 91.
    Variáveis Heap-Dinâmicas Explícitas Exercícios: –Escreva um programa que leia um número inteiro positivo n seguido de n números inteiros e imprima esses n números em ordem invertida. Por exemplo, ao receber 5 222 333 444 555 666 o seu programa deve imprimir 666 555 444 333 222. O seu programa não deve impor limitações sobre o valor de n. slide 91
  • 92.
    Referências • MaterialProfessora Karina Oliveira (Unicap) • Introdução a Estruturas de Dados - com técnicas de programação em C W. Celes, R. Cerqueira, J.L. Rangel Ed. Campus - ISBN 85-352-1228-0 • Material Professor Marco A. Casanova - PUC-Rio • C Completo e Total - 3ª ed. - Herbert Schildt - Makron Books slide 92
  • 93.
    Estrutura de DadosI UNICAP Eduardo Araújo Oliveira http://eduoliveira.com slide 93