Ponteiros & tabelas (cont.) K&R: Capítulo 5
Sinopse da aula de hoje Pointers in a nutshell & alocação dinâmica de memória Estruturas, funções e apontadores Estruturas auto-referenciadas Exemplo de aplicação: Listas ligadas uma Pilha 2
Pointers in a nutshell 3245434 3245435 Na memória do computador cada posição é referenciada por um endereço 3245436 3245437 Um apontador é uma variável que contém um endereço de outra variável. 3
Pointers in a nutshell 3245434 3245435 Na memória do computador cada posição é referenciada por um endereço 3245436 3245437 Um apontador é uma variável que contém um endereço de outra variável. Na sua declaração temos de indicar ao compilador para que tipo de variável estamos a apontar. char *cptr; /* ponteiro para caracter */ int *iptr; /* ponteiro para inteiro */ double *dptr; /* ponteiro para double */ 4
Pointers in a nutshell 3245434 3245435 Na memória do computador cada posição é referenciada por um endereço 3245436 3245437 Um apontador é uma variável que contém um endereço de outra variável. O endereço de uma variável é dado pelo operador & int a=43; /* um inteiro inicializado a 43 */ int *iptr; /* ponteiro para inteiro */ iptr=&a; /* iptr passa a guardar o endereço de a */ 5
Pointers in a nutshell 3245434 3245435 Na memória do computador cada posição é referenciada por um endereço Um apontador é uma variável que contém um endereço de outra variável. O valor guardado num determinado endereço é dado por pelo operador * 6 3245436 3245437 int b; int a=43; /* um inteiro inicializado a 43 */ int *iptr; /* ponteiro para inteiro */ iptr=&a; /* iptr passa a guardar o endereço de a */ b=*iptr; /* b passa a guardar o valor 43*/ */
O que é um ponteiro em C? 7 IAED, 2012/2013
Um ponteiro em C é um endereço de memória 8 IAED, 2012/2013
Pointers in a nutshell Em C os parâmetros são passados por valor void swap(int a, int b) { int aux; } aux = a; a = b; b = aux; swap(x, y) não funciona como necessário! 9
Pointers in a nutshell Passagem por referência consegue-se enviando ponteiros void swap(int *a, int *b) { int aux; } aux = *a; *a = *b; *b = aux; Chamada deverá ser swap(&x, &y) 10
Ponteiro Nulo / Endereço Zero Ponteiro especial para representar o endereço 0 int *ptr = NULL; Definido em stdlib.h Necessário #include <stdlib.h> Utilizado para indicar situações especiais Na realidade NULL == 0 11
Pointers in a nutshell Em C existe uma relação entre ponteiros e tabelas #include <stdio.h> int main() { int a[6] = {1, 2, 3, 4, 5, 6 }; int *pa = a; Os apontadores têm uma aritmética própria printf("%d %d %d\n", a[2], *(a+2), *(pa+2)); } return 0; a é um ponteiro para a primeira posição da tabela pa pode ser alterado; a não pode! 12
Pointers in a nutshell É possível efectuar + e - com ponteiros int x = 10; int *px = &x; px x 13
Pointers in a nutshell É possível efectuar + e - com ponteiros int x = 10; int *px = &x; px++; px x Incrementa/decrementa na dimensão do tipo para o qual aponta sizeof(int) neste caso 14
Passagem de Parâmetros para Funções Quando fazemos int a; scanf( %d,&a); o que estamos a passar ao scanf? char s[100]; scanf( %s,s); Por que não precisamos do &? 15
Passagem de Parâmetros para Funções Passagem por referência consegue-se enviando ponteiros void levector(int v[], int tamanho) { int i; } for (i=0 ; i<tamanho ; i++) scanf( %d,&v[i]} Podemos escrever o argumento como int*v ou int v[] Como v já é um endereço podemos alterar o v dentro da função. 16
Passagem de Parâmetros para Funções Passagem por referência consegue-se enviando ponteiros void levector(int *v, int tamanho) { int i; } for (i=0 ; i<tamanho ; i++) scanf( %d,v++} Podemos escrever o argumento como int*v ou int v[] Como v já é um endereço podemos alterar o v dentro da função. 17
Endereços de ponteiros É possível declarar um ponteiro para um ponteiro #include <stdio.h> int main() { int x = 10; int *px = &x; int **ppx = &px; printf("%d %d %d\n", x, *px, **ppx); } return 0; 18
Argumentos da Linha de Comandos argv[0] é o nome o programa argv[i] é i-ésimo argumento Programa escreve" $./escreve hello world gera hello world int main(int argc, char *argv[]) { int i; } for(i=1; i < argc; i++) printf("%s ", argv[i]); printf("\n"); return 0; array de pointers Tamanho do array (número de argumentos introduzidos) 19
ATENÇÃO! Ao fazer int *a; apenas estamos a reservar memória para 1 endereço de memória e não para um inteiro. Por esta razão, não devemos inicializar o conteúdo de um ponteiro sem que saibamos exactamente onde ele está a escrever. Ex.: int *a; *a=12; /* A evitar!!!*/ 20
Ponteiros para Funções É possível ter ponteiros para funções O nome de uma função é um ponteiro para essa função int soma(int a, int b) { return a+b; } int main() { int (*ptr)(int, int); ptr = soma; } printf("%d\n", (*ptr)(3,4)); return 0; 21
Ponteiros para Funções (2º exemplo) É possível ter ponteiros para funções O nome de uma função é um ponteiro para essa função int modulo(int a) { return a < 0? a : a; } int dobro(int a) { return a*2; } void escreve(int (*func)(int), int valor){ } 22 printf( %d\n,(*func)(valor)); int main() { } int x=-10; int (*f)(int); f=modulo; escreve(f,x); return 0;
Alocação dinâmica de memória
Alocação Dinâmica de Memória Alocação estática int tab[100]; Memória alocada durante o scope da variável Não é possível libertar quando não necessária Não é possivel utilizar fora do scope Solução: alocação dinâmica! 24
Alocação Dinâmica de Memória Função malloc void *malloc(size_t size); Recebe como argumento o número de bytes Tipo size_t representa uma dimensão em bytes Devolve um ponteiro (endereço) para o primeiro byte do bloco de memória contígua alocada void * indica um ponteiro para um tipo não especificado permite utilização com qualquer tipo de dados posteriormente faz-se conversão para o tipo correcto por type cast int *vec; vec = (int*) malloc(sizeof(int)*100); 25
Alocação Dinâmica de Memória Função realloc void *realloc(void *ptr, size_t size); Recebe ponteiro ptr para bloco de memória antigo e dimensão size do novo bloco de memória Devolve ponteiro para novo bloco de memória Copia valores do bloco antigo para o novo Se novo for mais pequeno, só copia até caber Se novo for maior, copia tudo e deixa o resto sem ser inicializado vec = (int*) realloc(vec, sizeof(int)*250); 26
Alocação Dinâmica de Memória Libertação de memória é efectuada com a função free void free(void *ptr); Recebe como argumento o ponteiro para a primeira posição do bloco de memória contígua a libertar Não devolve nada Como libertar a memória reservada com o malloc anterior? free(vec); Tanto malloc como free estão definidas em stdlib.h 27 Necessário #include <stdlib.h> Sugestão: Usem o valgrind
Sinopse da aula de hoje Pointers in a nutshell: Dúvidas? Estruturas, Funções e apontadores Estruturas Auto-Referenciadas Exemplo de aplicação: Listas ligadas uma Pilha 28
Ponteiros para estruturas
Sinopse da aula de hoje Pointers in a nutshell Estruturas, Funções e apontadores Estruturas Auto-Referenciadas Exemplo de aplicação: Listas ligadas uma Pilha 30
Declaração de Estruturas (recapitulação) Declaração de variável do tipo estrutura: typedef struct ponto { double x; double y; } Ponto Ponto centro; Manipulação : <variavel>.<membro> centro.x = 12.3; centro.y = 5.2; 31
Declaração de Estruturas Declaração de variável do tipo estrutura: typedef struct ponto { double x; double y; } Ponto Ponto centro; Ponto *pcentro = ¢ro; Manipulação : (*<variavel>).<membro> (*pcentro).x = 12.3; (*pcentro).y = 5.2; 32 Mas isto é aborrecido!
Declaração de Estruturas (operador ->) Declaração de variável do tipo estrutura: typedef struct ponto { double x; double y; } Ponto Ponto centro; Ponto *pcentro = ¢ro; Manipulação : pcentro->x = 12.3; pcentro->y = 5.2; (*<ponteiro>).<membro> é equiv. a <ponteiro>-><membro> 33
Ponteiros para Estruturas Declaração de ponteiro para uma estrutura: Ponto *pcentro; A declaração de um ponteiro não aloca memória!! Se quisermos alocar memória de forma explícita fazemos: pcentro = (Ponto*) malloc(sizeof(ponto)); 34
Estruturas e Funções Funções podem receber e retornar estruturas Ponto adicionaponto(ponto p1, Ponto p2) { Ponto res; } res.x = p1.x + p2.x; res.y = p1.y + p2.y; return res; Função retorna cópia da estrutura res Passagem de argumentos feita por valor Chamada adicionaponto(pa, pb) não altera valores de pa nem pb. 35 Adicionaponto cria cópias de pa e pb que só existem dentro da função! Será eficiente?
Estruturas, Funções e Ponteiros Passagem de estruturas grandes como parâmetros é ineficiente É necessário efectuar a cópia de todos os campos Utilizam-se normalmente ponteiros para estruturas Podemos alterar o conteúdo dos argumentos! void adicionaponto(ponto *p1, Ponto *p2, Ponto *res) { res->x = p1->x + p2->x; } res->y = p1->y + p2->y; 36
Estruturas, Funções e Ponteiros Também podemos reservar memória para uma estrutura que será utilizada fora da função Reservamos memória com o malloc e retornamos o pointer para a memória alocada. Ponto *adicionaponto(ponto *p1, Ponto *p2) { Ponto *res; res = (Ponto *) malloc(sizeof(ponto)); res->x = p1->x + p2->x; res->y = p1->y + p2->y; } return res; 37
Estruturas, Funções e Ponteiros Também podemos reservar memória para uma estrutura que será utilizada fora da função Reservamos memória com o malloc e retornamos o pointer para a memória alocada. Ponto *adicionaponto(ponto *p1, Ponto *p2) { Ponto *res; ATENÇÃO: em geral, para cada res = (Ponto *) malloc(sizeof(ponto)); malloc tem de haver um res->x = p1->x + p2->x; free!! } 38 res->y = p1->y + p2->y; return res; Uma memory leak ocorre sempre que perdemos o endereço de memória do objecto alocado.
Estruturas, Funções e Ponteiros Exemplo de fuga de memória (ERRO!!): void printsoma(ponto *p1, Ponto *p2) { Ponto *res; } 39 res = (Ponto *) malloc(sizeof(ponto)); res->x = p1->x + p2->x; res->y = p1->y + p2->y; printf( (%d, %d)\n,res->x,res->y); ATENÇÃO: em geral, para cada malloc tem de haver um free!! Uma memory leak ocorre sempre que perdemos o endereço de memória do objecto alocado.
Estruturas, Funções e Ponteiros Exemplo (corrigido): void printsoma(ponto *p1, Ponto *p2) { Ponto *res; } 40 res = (Ponto *) malloc(sizeof(ponto)); res->x = p1->x + p2->x; res->y = p1->y + p2->y; printf( (%d, %d)\n,res->x,res->y); free (res); ATENÇÃO: em geral, para cada malloc tem de haver um free!! Uma memory leak ocorre sempre que perdemos o endereço de memória do objecto alocado.
Estruturas auto-referenciadas
Sinopse Pointers in a nutshell Estruturas, Funções e apontadores Estruturas Auto-Referenciadas: das tabelas às listas Exemplo de aplicação: Listas ligadas uma Pilha 42
Tabelas/Vectores Colecção de items Inteiros, reais, caracteres Estruturas Tabelas, Ponteiros Guardados em posições consecutivas de memória int tab[n]; Programador é responsável por respeitar limites 43
Tabelas/Vectores Em C tabelas podem ser De dimensão fixa Alocadas dinamicamente #define N 100 int tab1[n]; int *tab2 = (int *) malloc(n*sizeof(int)); Acesso alternativo a tabelas x = tab2[i]; y = *(tab2+i); 44
Tabelas/Vectores Vantagens Manipulação simples Facilidade de acesso ao n-ésimo elemento Desvantagens Tamanho limitado Necessidade de realocar e copiar todos os elementos se desejar aumentar dimensão da tabela Desperdicio de memória Alternativa: Listas head 45 NULL
O que é uma estrutura auto-referenciada? Estrutura em que um campo da estrutura é um apontador para outra estrutura do mesmo tipo typedef struct ponto { double x; double y; struct ponto *origem; } Ponto; As estruturas auto-referenciadas permitem criar estruturas de dados dinâmicas, utilizando ponteiros: 47 Listas (simplesmente e duplamente ligadas) Árvores etc.