Baixe o app para aproveitar ainda mais
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
Compartilhar