Buscar

05 - ponteiros-alocacao-dinamica

Prévia do material em texto

Italo Valcy Programação em C, 2013.1
Programação C
Italo Valcy <italo@dcc.ufba.br>
Aula 05 – Ponteiros e Alocação dinâmica
Italo Valcy Programação em C, 2013.1 2 / 40
Todo o material aqui disponível pode, posteriormente, ser utilizado sobre os termos da:
Creative Commons License: 
Atribuição - Uso não comercial - Permanência da Licença
http://creativecommons.org/licenses/by-nc-sa/3.0/
Licença de uso e distribuição
Este curso foi baseado no curso “Algoritmos e Programação de
Computadores” do prof. Centoducatte, da UNICAMP, disponível
em <http://www.ic.unicamp.br/~ducatte/mc102/mc102.html>, e 
nas aulas de prof. Luciano Oliveira.
Italo Valcy Programação em C, 2013.1 3 / 40
Ponteiros
Italo Valcy Programação em C, 2013.1 4 / 40
Ponteiros
Referência uma mesma variável em diversas partes 
do programa
Vários apontadores (ponteiros) espalhados pelo 
programa, apontando para a mesma variável
Qualquer modificação no valor da variável é refletido em 
todos os seus apontadores
Manipulação de endereços de memória
O C é altamente dependente dos ponteiros
O Ministério da Saúde adverte: o uso descuidado de ponteiros pode levar a 
sérios bugs e a dores de cabeça terríveis :-). 
Motivação
Italo Valcy Programação em C, 2013.1 5 / 40
Ponteiros
ints guardam inteiros. Os floats guardam 
números de ponto flutuante. Os chars guardam 
caracteres.
Ponteiros guardam endereços de memória!
Label Endereço Valor
x 0xf31d 30
str 0xf321 “hello world”
w 0xf33d 132.0074
... ... ...
p 0xff2c 0xf31d
... ... ...
Italo Valcy Programação em C, 2013.1 6 / 40
Ponteiros
Italo Valcy Programação em C, 2013.1 7 / 40
Ponteiros
Ao declararmos um ponteiro, precisamos informar o 
tipo a ser “apontado”
Um ponteiro int aponta para um inteiro, isto é, guarda o 
endereço de um inteiro. 
Forma geral:
 tipo_do_ponteiro *nome_da_variável;
O asterisco (*) faz o compilador saber que aquela 
variável é um ponteiro
Exemplos:
   int *pt;
   char *temp,*pt2;
Declaração
Italo Valcy Programação em C, 2013.1 8 / 40
Ponteiros
Outras formas de declarar (equivalentes):
   int * ptr;
   int* ptr;
   int *ptr;
Cuidado...
   int* a, b, c; // apenas 'a' é ponteiro
Declaração
Italo Valcy Programação em C, 2013.1 9 / 40
Ponteiros
Podemos representar um ponteiro P da 
seguinte maneira:
Exemplo:
Representação gráfica
Italo Valcy Programação em C, 2013.1 10 / 40
Ponteiros
Ao declarar um ponteiro P, o endereço inicial 
contido nesta variável deve ser considerado 
como "lixo":
   int *pt;
Lixo quer dizer: qualquer coisa, indefinido!
Representação gráfica
Italo Valcy Programação em C, 2013.1 11 / 40
Ponteiros
Supondo que temos uma variável, V, e 
queremos que um ponteiro, P (ponteiro para 
inteiro), aponte para esta variável. Para isso, 
usa-se o operador & para obter o endereço de 
V, da seguinte maneira:
   P = &V;
Existe um endereço especial, chamado “nulo”, 
para informar que o ponteiro é vazio:
   P = NULL;
Atribuindo um endereço para um ponteiro
Italo Valcy Programação em C, 2013.1 12 / 40
Ponteiros
Definição: o operador unário * trata seu 
operando como um endereço, e acessa esse 
endereço para buscar o conteúdo do objeto 
alvo.
   px = &x;
   y = *px;
Atribuindo um endereço para um ponteiro
Italo Valcy Programação em C, 2013.1 13 / 40
Ponteiros
Para acessar a memória que o ponteiro P está 
apontando, usamos o operador *, com a 
estrutura *P. Exemplo:
  int V = 10, *P = NULL;
  P = &V;
  *P = 30;
Acessando dados de um ponteiro
V = 10; P = NULL;
V = 10; P = &V;
V = 30; P = &V;
Italo Valcy Programação em C, 2013.1 14 / 40
Ponteiros
Aritmética de ponteiros
#include <stdio.h>
void main() {
  int vetor[] = { 10, 20, 30, 40, 50 };
  int *p1, *p2;
  int i = 100;
  p1 = &vetor[2];
  printf("%d\n", *p1);
  p2 = &i;
  printf("%d\n", *p2);
  p2 = p1;
  printf("%d\n", *p2);
}
Operadores básicos: & e *
Italo Valcy Programação em C, 2013.1 15 / 40
Ponteiros
Aritmética de ponteiros
Outros operadores:
Incremento
Decremento
Italo Valcy Programação em C, 2013.1 16 / 40
Ponteiros
Incremento/decremento de ponteiro
Quando você incrementa um ponteiro, ele 
passa a apontar para a próxima variável de 
mesmo tipo
Mais uma razão para definir o tipo do ponteiro!
Se você incrementa um ponteiro tipo char, ele 
avança 1 byte na memória; já se for do tipo 
double, ele avança 8 bytes
Análogo para decremento
Italo Valcy Programação em C, 2013.1 17 / 40
Ponteiros
Incremento/decremento de ponteiro
Exemplo (seja p um ponteiro devidamente inicializado):
   p++;
   p­­;
Atenção: estamos falando de operações com ponteiros e 
não com as variáveis que eles apontam:
   (*p)++;
   (*p)­­;
Se você quiser avançar 15 posições na memória:
   p = p + 15; // ou p+=15
Se você quiser apenas acessar o conteúdo 15 posições à 
frente (diferente dos anteriores):
   *(p + 15)
Italo Valcy Programação em C, 2013.1 18 / 40
Ponteiros
Incremento/decremento de ponteiro
#include <stdio.h>
int main() {
  float v[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0};
  int i;
  float *p;
  for (i=0; i<9; i++)
     printf("%.1f ", v[i]);
  printf("\n");
  for (i=0, p=v; i<9; i++, p++)
     printf("%.1f ", *p);
  printf("\n");
}
Italo Valcy Programação em C, 2013.1 19 / 40
Ponteiros
Vetores e ponteiros
Se v for um vetor ou um ponteiro para o 
primeiro elemento de um vetor, então para 
obter o elemento índice n desse valor pode-se 
fazer v[n] ou *(v+n)
Italo Valcy Programação em C, 2013.1 20 / 40
Ponteiros
Exercício 1
#include <stdio.h>
int main() {
    int y, *p, x;
    y = 0;
    p = &y;
    x = *p;
    x = 4;
    (*p)++;
    x­­;
    (*p) += x;
    printf ("y = %d\n", y);
    return(0);
}
Qual deveria ser a saída do programa abaixo?
Italo Valcy Programação em C, 2013.1 21 / 40
Ponteiros
Exercício 2
Crie uma versão da função strcpy, que copia o 
conteúdo de uma string para outra. O protótipo 
da sua função deve ser como o seguinte:
    void mystrcpy(char *destino, char *origem);
Dicas:
Fim de string: '\0' (condição de parada)
Pensar sobre incremento de ponteiros
Italo Valcy Programação em C, 2013.1 22 / 40
Ponteiros
Exercício 2 (solução)
#include <stdio.h>
void mystrcpy(char *destino, char *origem) {
   while (*origem) {
      *destino = *origem;
      origem++;
      destino++;
   }
   *destino = '\0';
}
int main () {
char str1[100],str2[100],str3[100];
printf("Entre com uma string: ");
scanf("\n\r%[^\n]", str1);
mystrcpy(str2, str1);
mystrcpy(str3,"Voce digitou a string ");
printf("\n%s'%s'\n",str3, str2);
return(0);
}
Italo Valcy Programação em C, 2013.1 23 / 40
Ponteiros
Um ponteiro para ponteiro é como se você 
anotasse o endereço de um papel que tem o 
endereço de onde tem um telefone.
Declaração:
tipo **nome_variavel;
Considerações:
**ptr – o conteúdo final da variável apontada
*ptr – o conteúdo do ponteiro intermediário
Em C é possível termos ponteiro para ponteiro 
para ponteiro para ponteiro ….
Ponteiro de ponteiro
Italo Valcy Programação em C, 2013.1 24 / 40
Ponteiros
Ponteiro de ponteiro (exemplo)
#include <stdio.h>
int main()
{
    float fpi = 3.1415, *pf, **ppf;
    pf = &fpi;                 /* pf armazena o endereco de fpi */
    ppf = &pf;                 /* ppf armazena o endereco de pf */
    printf("%f", **ppf);      /* Imprime o valor de fpi */
    printf("%f", *pf);         /* Tambem imprime o valor de fpi */
    return(0);
}
Uso comum: passagem de argumento ponteiro 
por referência para função
Italo Valcy Programação em C, 2013.1 25 / 40
Alocação dinâmica de memória
Italo Valcy Programação em C, 2013.1 26 / 40
Alocação dinâmica de memória
Até agora a memória que usamos tinha um 
espaço pré-alocado
Em algumas situações precisaremos fazer 
alocação dinâmica
Solicitar memória em tempo de execução
Ajuda a tratar estruturas de dados que não 
possuem tamanho fixo (vetores, listas, etc)
Permite reserva de recursos sob demanda
Italo Valcy Programação em C, 2013.1 27 / 40
Alocação dinâmica de memória
Funções para alocação dinâmica:malloc() / calloc()
free() 
realloc() 
Cabeçalho:
   #include <stdlib.h>
Mecanismo
Italo Valcy Programação em C, 2013.1 28 / 40
Alocação dinâmica de memória
malloc()
void * malloc ( size_t size );
A função malloc (memory allocation), aloca um 
bloco de memória, não inicializado, e retorna o 
endereço desse bloco:
   char *ptr;
   ptr = malloc(1);
   scanf ("%c", ptr);
Observações:
Retorna um endereço de tipo genérico (void *)
Se não souber quantos bytes alocar, use sizeof()
      ptr = malloc(sizeof(char));
Italo Valcy Programação em C, 2013.1 29 / 40
Alocação dinâmica de memória
malloc()
void * malloc ( size_t size );
Para corrigir o tipo de retorno de malloc 
(void *), use casting:
   double *x;
   x = (double *) malloc(sizeof(double));
   scanf ("%f", x); // nao usamos o &
Italo Valcy Programação em C, 2013.1 30 / 40
Alocação dinâmica de memória
malloc()
void * malloc ( size_t size );
Overhead: a memória alocada por malloc 
geralmente é maior que a solicitada, a fim de 
armazenar informações de controle.
Cuidado: evite chamar malloc várias vezes com 
argumento muito pequeno
Italo Valcy Programação em C, 2013.1 31 / 40
Alocação dinâmica de memória
malloc()
void * malloc ( size_t size );
Caso a memória já esteja totalmente ocupada, 
malloc falha na alocação e retorna NULL. 
Convém verificar:
   ptr = (double *)malloc(1024*sizeof(double));
   if (ptr == NULL) {
      printf("falha na alocacao de memoria\n");
      exit(EXIT_FAILURE);
   }
Italo Valcy Programação em C, 2013.1 32 / 40
Alocação dinâmica de memória
Exercício
Qual a saída do programa abaixo?
#include <stdio.h>
int main (void) {
   printf("sizeof (int) = %d\n", sizeof (int));
   printf("sizeof (void *) = %d\n", sizeof (void *));
   return 0;
}
Italo Valcy Programação em C, 2013.1 33 / 40
Alocação dinâmica de memória
Exemplo
#include <stdio.h>
#include <stdlib.h>
int main () {
  int i,len;
  char *buffer;
  printf ("How long do you want the string? ");
  scanf ("%d", &len);
  buffer = (char *)malloc((len+1)*sizeof(char));
  if (buffer==NULL) exit (1);
  for (i=0; i<len; i++)
    buffer[i] = rand()%26 + 'a';
  buffer[i]='\0';
  printf ("Random string: %s\n", buffer);
  return 0;
}
Italo Valcy Programação em C, 2013.1 34 / 40
Alocação dinâmica de memória
free()
void free ( void * ptr );
A função free libera um espaço reservado por 
malloc, calloc ou realloc
C não possui garbage collector
Exemplo:
   char *ptr;
   ptr = (char *) malloc(10*sizeof(char));
   // ... 
   free(ptr);
Italo Valcy Programação em C, 2013.1 35 / 40
Alocação dinâmica de memória
free()
void free ( void * ptr );
Note que o valor de ptr não é alterado, ou seja, 
ele ainda aponta para mesmo local (inválido)
Isso pode deixar seu programa vulnerável a 
ataques
Como se prefinir:
   free(ptr);
   ptr = NULL;
Por outro lado, não adianta simplesmente:
   ptr = NULL; // isso sozinho nao eh free()...
Italo Valcy Programação em C, 2013.1 36 / 40
Alocação dinâmica de memória
calloc()
void * calloc ( size_t num, size_t size );
A função calloc é bastante similar a malloc. 
Veja a construção abaixo:
   ptr = (int *) malloc(10*sizeof(int));
   ptr = (int *) calloc(10, sizeof(char));
A diferença é que calloc inicializa o bloco 
com zero
Se não houver memória suficiente, retorna 
NULL
Italo Valcy Programação em C, 2013.1 37 / 40
Alocação dinâmica de memória
realloc()
void * realloc(void * ptr, size_t size);
Usada para realocar um bloco de memória 
(aumentando ou diminuindo seu tamanho)
O conteúdo do bloco antigo é copiado para o novo
Se não houver memória suficiente, retorna NULL
   ptr = (int *) malloc(10*sizeof(int));
   // ...
   more_ptr = (int *)realloc(ptr,20*sizeof(int));
   if (more_ptr != NULL)
      ptr = more_ptr
   // ...
Italo Valcy Programação em C, 2013.1 38 / 40
Alocação dinâmica de memória
Ponteiro de ponteiro
Geralmente utilizados para implementar matrizes. 
Declaração:
   int **matriz;
Na primeira dimensão, vamos alocar um vetor de 
ponteiros:
   matriz = (int **)malloc(M*sizeof(int *));
Depois disso, para cada elemento, alocamos um 
novo vetor:
   for(i=0; i < M; i++)
      matriz[i] = (int *)malloc(N*sizeof(int));
Italo Valcy Programação em C, 2013.1 39 / 40
Alocação dinâmica de memória
Ponteiro de ponteiro (exemplo)
#include <stdio.h>
#include <stdlib.h>
int main() {
   int **m;
   int i,j;
   m = (int **)malloc(10*sizeof(int *));
   for(i=0; i<10; i++)
      m[i] = (int *)malloc(10*sizeof(int));
   for(i=0; i<10; i++)
      for(j=0; j<10; j++)
         m[i][j] = i*j;
   for(i=0; i<10; i++) {
      for(j=0; j<10; j++)
         printf("%3d ", m[i][j]);
      printf("\n");
   }
}
Italo Valcy Programação em C, 2013.1 40 / 40
Alocação dinâmica de memória
Exercício
Exer05 (contador.c): Escrever um programa que leia diversos 
números inteiros do usuário (entrada padrão), até que o 
usuário digite o número 0 (zero). A entrada pode conter 
números repetidos e sua missão é contar qual número aparece 
mais vezes. Como resultado, seu programa deve imprimir, na 
saída padrão, o número que mais se repete e a quantidade de 
vezes que ele ocorreu. Não haverá entrada com quantidade de 
números igual. A entrada estará ordenada. Exemplos:
Entrada1:
1
2
3
4
4
9
10
10
10
15
0
Saida1:
10 3
Entrada2:
1
2
2
3
3
3
4
4
4
4
0
Saida2:
4 4
	Slide 1
	Slide 2
	Slide 3
	Slide 4
	Slide 5
	Slide 6
	Slide 7
	Slide 8
	Slide 9
	Slide 10
	Slide 11
	Slide 12
	Slide 13
	Slide 14
	Slide 15
	Slide 16
	Slide 17
	Slide 18
	Slide 19
	Slide 20
	Slide 21
	Slide 22
	Slide 23
	Slide 24
	Slide 25
	Slide 26
	Slide 27
	Slide 28
	Slide 29
	Slide 30
	Slide 31
	Slide 32
	Slide 33
	Slide 34
	Slide 35
	Slide 36
	Slide 37
	Slide 38
	Slide 39
	Slide 40

Continue navegando