Apontadores ou Ponteiros T.L.P. 11º Ano Conceitos básicos Quando se define uma variável é guardado um espaço em memória do tamanho do tipo da variável int x; 2 bytes float y; 4 bytes char n[10]; 10 bytes Miguela Fernandes 2 Conceitos básicos Exemplo int x= 10; 12 100 101 102 103 104 105 Não esquecer que ocupa 2 bytes. Então x está no endereço 102 O operador de endereço como já é vosso conhecido é & Então o endereço de x posso obtê-lo da seguinte forma: printf(“Endereço de x = %d ”, &x); // Endereço de x = 102 Exemplo char z =‘A’ 12 ‘A’ 100 101 102 103 104 105 printf(“Endereço de z = %d ”, &z); // Endereço de x = 105 Miguela Fernandes 3 Apontadores/Ponteiros Os apontadores são variáveis que guardam os endereços de memória (&) de outras variáveis. Então podemos fazer ptr = &x // x 12 100 101 102 Estou a atribuir o endereço de x a ptr z ‘A’ 104 105 Miguela Fernandes ptr 102 4 Declarar Ponteiros Sintaxe tipo_dado *nome_ponteiro; Exemplo: int x; int *ptr; /* compilador sabe que ptr ponteiro para inteiro */ é um *- O asterisco indica que é um ponteiro Exemplos char x, *p, y, * ap; Float *p_num, num; //char * p ou char* Miguela Fernandes p 5 Operadores dos Ponteiros * desreferenciador Devolve o valor apontado pelo ponteiro. & referenciador Devolve o endereço de memória do seu operando. Precedência: Tanto o & como o * possuem precedência maior que qualquer outro operadore, com excepção do menos unário, que possui a mesma. Miguela Fernandes 6 Iniciar Ponteiros Já vimos que o operador “&” retorna endereço de uma variável Para iniciar ponteiros temos que: int x = 10, *ptr; ptr = &x; //Defino a var e o ponteiro //Inicio o ponteiro printf(“&x: %x ptr: %x”, &x, ptr); &x: 0x03062fd8 Miguela Fernandes ptr: 0x03062fd8 7 Iniciar Ponteiros c/ NULL Para iniciar ponteiros temos que: int x = 10, *ptr; ptr = &x; //Defino a var e o ponteiro //Inicio o ponteiro Quando não pretendo iniciar logo o ponteiro com valores tenho de: ptr = NULL Miguela Fernandes 8 Manipular Ponteiros Imprimir o conteúdo de uma variável x 10 int x = 10, *ptr = &x; printf(“ x = %d”, x); // x = 10 102 ptr Imprimir o conteúdo de um ponteiro printf(“ ptr = %d”,ptr); // x = 102 102 200 Imprimir o valor para onde ele aponta printf(“ x = %d”, *ptr); // x = 10 Miguela Fernandes 9 Manipular Ponteiros Se py e px são pointers para inteiros, então podemos fazer a seguinte declaração: py=px; py fica a apontar p/ o mesmo que px void main(){ int x,*px,*py; x=9; px=&x; py=px; printf("x= %d\n",x); printf("&x= %d\n",&x); printf("px= %d\n",px); printf("*px= %d\n",*px); printf("*py= %d\n",*py); } Miguela Fernandes 10 Exercício 1 1- Declara uma variável para guardar a tua idade e inicial um ponteiro para a variável idade. 2- O que estou a fazer com seguinte declaração: int* x, y, z; 3- Fazer um programa que veja os endereços de memória de uma variável do tipo inteiro, char e float. Pretende-se que o faças 1º sem recorrer a ponteiros e depois com recurso a estes. Miguela Fernandes 11 Cuidado com os tipos de dados Os ponteiros são variáveis tipadas: (int *) ≠ (float *) ≠ (char *) void main() { int *p1, x; float *p2, y; p1 = &x; /* p2 = &y; /* p1 = &z; /* p2 = &x; /* } OK */ OK */ Erro */ Erro */ Um apontador para o tipo xpty, avança sempre o sizeof(xpty) Miguela Fernandes 12 Cuidado com os tipos de dados c i ‘A’ 12 100 101 100 140 102 103 102 141 ptr_c 142 f 3.1415 104 105 106 107 108 109 110 111 112 107 143 ptr_i 144 145 146 147 148 149 150 151 152 ptr_f Sizeof(char) ≠ Sizeof(int) ≠ Sizeof(float) Não posso fazer: ptr_c = &i; ptr_i=&f; Miguela Fernandes 13 Exercício 2 Pratique a declaração e utilização de ponteiros. defina e inicialize uma variável inteira defina um ponteiro para inteiro modifique o valor da variável através do ponteiro verifique os novos valores da variável usando printf Miguela Fernandes 14 Ponteiros Genéricos Um ponteiro genérico é um ponteiro que pode apontar para qualquer tipo de dado Define-se um ponteiro genérico utilizando o tipo void: void *ptr; int x=10; float y=88.2; ptr = &x; ptr = &f; /* neste caso ptr aponta para um inteiro */ /* agora para um float */ Miguela Fernandes 15 Ponteiros Genéricos O tipo de dado apontado por um void pointer deve ser controlado pelo utilizador através do cast Sintaxe: * (tipo *) ponteiro ptr = &x; printf(“Inteiro: %d\n”, *(int *) ptr ); /* Inteiro = 10*/ ptr = &f; printf(“Float: %4.2f\n”, *(float *) ptr ); /*Float = 88.20*/11 Miguela Fernandes 16 Ponteiros e Vectores Relembrar: O nome de um vector é um ponteiro (constante) para o 1º elemento do vector Exemplo: int v[10]; v == &v[0] Int *ptr; ptr= &v[0] // ou ptr =v; Então: *(ptr+1) é igual a v[1], *(ptr+2) é igual a v[2], *(ptr+n) == v[n] Miguela Fernandes 17 Ponteiros e Vectores Não esquecer: float x[20] x é um apontador constante que endereça o primeiro elemento do vector, como tal, não é possível mudar o seu valor. Exemplo: float x[10], y[10]; float *ptr_f; x = y; /* NUNCA ERRO: x é constante ! */ ptr_f = x; /* SEMPRE OK */ Miguela Fernandes 18 Ponteiros e Vectores Pode referenciar-se os elementos de um vector através de ponteiros: float x[] = { 11.0, 2.0, 5.4, 7.987 }; float *ptr_f; ptr_f= &x[1]; printf(“%f”, *ptr_f); /* 2.0 */ printf(“%f”, *(ptr_f +1)); /* 5.4 */ Miguela Fernandes 19 Ponteiros e Vectores Pode utilizar-se ponteiros e parênteses rectos: float x[] = { 11.0, 2.0, 5.4, 7.987 }; float *ptr_f; ptr_f = &x[1]; printf(“%f”, ptr_f[0]); /* ==> 2.0 */ Nota O valor entre chavetas é o deslocamento a ser considerado a partir do endereço de referência ptr_f[n] => indica enésimo elemento a partir de Miguela Fernandes 20 Ponteiros e Vectores Exemplo 1 /*exercicio ptr_5*/ #include <stdio.h> #include <conio.h> #define TAM 10 char v[TAM] = "0123456789"; void main() { clrscr(); int i; for (i = 0; i < TAM; ++i) { printf("&v[i]=%p (v+i)=%p v[i]=%c\n", &v[i], (v+i), v[i]); } getch(); } Miguela Fernandes 21 Ponteiros e Vectores Exemplo 2 Programa que dá o numero de elementos de um vector antes de encontrar o 0. #include <stdio.h> #include <conio.h> void main() { int v[] = {4, 5, 8, 9, 8, 1, 0, 1, 9, 3}; int i = 0; while (v[i] != 0) ++i; printf("Nº de elementos antes de 0 %d\n",i); getch(); } /*exercício n 6*/ #include <stdio.h> #include <conio.h> void main() { int v[] = {4, 5, 8, 9, 8, 1, 0, 1, 9, 3}; int *ptr; ptr=v; while ((*ptr) != 0) ++ptr; printf("Nº de elementos antes de 0 %d\n",ptr-v); getch(); } Miguela Fernandes 22 Aritmética de Ponteiros É possível fazer operações aritméticas e relacionais entre ponteiros e inteiros Soma ou subtracção int x[10], *ptr; ptr[2] equivale a *(ptr+2) *(ptr + n) acede ao conteúdo de n elementos a frente *(ptr - n) acede ao conteúdo de n elementos atrás ptr++ acede ao próximo endereço elemento vector * ptr-acede ao endereço anterior do vector * * Não esquecer que avança o tamanho do tipo int 2 bytes, float 4 bytes, etc. Miguela Fernandes 23 Aritmética de Ponteiros vect 2 100 0 1 3 5 .. .. .. 102 104 106 108 110 112 114 .. .. … …… 148 149 .. .. .. 150 151 152 100 140 ptr 141 142 143 144 145 146 147 Quais os seguintes resultados: ptr_10 printf(“%d”, ptr); printf(“%d”, *ptr); printf(“%d”, ++ptr); // Porque não printf(“%d”, ptr++); ??? printf(“%d”, vect [1]); printf(“%d”, (*ptr)++); printf(“%d”, vect [1]); printf(“%d”, *(ptr++)); printf(“%d”, *(ptr+2)); printf(“%d”, ptr); Miguela Fernandes 24 Aritmética de Ponteiros vect 2 100 0 1 3 5 .. .. .. 102 104 106 108 110 112 114 .. .. … …… 148 149 .. .. .. 150 151 152 100 140 141 142 143 144 145 146 147 ptr printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, ptr); //100 *ptr); // 2 ++ptr); vect [1]); (*ptr)++); vect [1]); *(ptr++)); *(ptr+2)); ptr); Miguela Fernandes 25 Aritmética de Ponteiros vect 2 100 0 1 3 5 .. .. .. .. 102 104 106 108 110 112 114 .. … …… 148 149 .. .. .. 150 151 152 102 140 141 142 143 144 145 146 147 ptr printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, ptr); *ptr); ++ptr); vect [1]); (*ptr)++); vect [1]); *(ptr++)); *(ptr+2)); ptr); Miguela Fernandes //100 // 2 // 2 // 0 // 0 26 Aritmética de Ponteiros vect 2 100 1 1 3 5 .. .. .. .. 102 104 106 108 110 112 114 .. … …… 148 149 .. .. .. 150 151 152 102 140 141 142 143 144 145 146 147 ptr printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, ptr); *ptr); ++ptr); vect [1]); (*ptr)++); vect [1]); *(ptr++)); *(ptr+2)); ptr); Miguela Fernandes //100 // 2 // 2 // 0 // 0 // 1 27 Aritmética de Ponteiros vect 2 100 1 1 3 5 .. .. .. 102 104 106 108 110 112 114 .. .. … …… 148 149 .. .. .. 150 151 152 104 140 141 142 143 144 145 146 147 ptr printf(“%d”, ptr); printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, *ptr); ++ptr); vect [1]); (*ptr)++); vect [1]); *(ptr++)); *(ptr+2)); ptr); Miguela Fernandes //100 // 2 // 2 // 0 // 0 // 1 // 1 28 Aritmética de Ponteiros vect 2 100 1 1 3 5 .. .. .. 102 104 106 108 110 112 114 .. .. … …… 148 149 .. .. .. 150 151 152 104 140 141 142 143 144 145 146 147 ptr printf(“%d”, ptr); printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, printf(“%d”, //100 *ptr); // 2 ++ptr); // 2 vect [1]); // 0 (*ptr)++); // 0 vect [1]); // 1 *(ptr++)); // 1 *(ptr+2)); // 5 ptr); Miguela Fernandes // 104 29 Utilizando Ponteiros void main() { int x = 10; int *pi, *pj; pi = &x;/* *pi == 10 */ pj = pi;/* *pj == 10 */ (*pi)++;/* (*pi, *pj, x) == 11 */ (*pj)++; /* (*pi, *pj, x) == 12 */ printf(“%d”, x); /* ==> 12 */ } Miguela Fernandes 30 Utilizando Ponteiros void main() { int x = 10; int *pi; pi = &x;/* *pi == 10 */ (*pi)++;/* *pi == 11 */ printf(“%d”, x); } ==> 11 Ao alterar *pi estamos a alterar o conteúdo de x Miguela Fernandes 31 Exemplo void { int int int main () arint[] = size = 7; i, *pi; { 1,2,3,4,5,6,7 }; /* tamanho do array */ for (pi=arint, i=0; i < size; i++, pi++) printf(“ %d “, *pi); } ==> 1 2 3 4 5 6 7 Miguela Fernandes 32 Exemplo - variação void main () { int arint[] = { 1,2,3,4,5,6,7 }; int size = 7; /* tamanho do array */ int i, *pi; pi = arint; printf(“ %d “, *pi); pi += 2; printf(“ %d “, *pi); pi += 2; printf(“ %d “, *pi); pi += 2; printf(“ %d “, *pi); } ==> 1 3 5 7 Miguela Fernandes 33 Exemplo - variação void { int int int main () arint[] = size = 7; i, *pi; { 1,2,3,4,5,6,7 }; /* tamanho do array */ for (pi=arint, i=0; i < size; i++) printf(“ %d “, *pi++); } ==> 1 2 3 4 5 6 7 Miguela Fernandes 34 Operações Válidas Sobre Ponteiros Podemos: somar ou subtrair um inteiro a um ponteiro (ptr ± int) incrementar ou decrementar ponteiros (ptr++, ptr--) subtrair ponteiros (produz um inteiro) (ptr1 – ptr2) comparar ponteiros ( >, >=, <, <=, == ) Não podemos: somar ponteiros (ptr1 + ptr2) multiplicar ou dividir ponteiros (ptr1*ptr2, ptr1/ptr2) usar ponteiros com double ou float (pi ± 2.0) Miguela Fernandes 35 Exercício 3 Escreva um programa que imprima um vector de inteiros na ordem inversa endereçando os elementos com um ponteiro Miguela Fernandes 36 Cuidados... C não controla os limites dos arrays, o programador deve fazê-lo Ex: encontrar o erro: void main () { int arint[] = { 1,2,3,4,5,6,7 }; int size = 7, i, *pi; for (pi=arint, i=0; i < size; i++, pi += 2) printf(“ %d “, *pi); } Miguela Fernandes 37 Cuidados... void main () { int arint[] = { 1,2,3,4,5,6,7 }; int size = 10; int i; for (pi=arint, i=0; i < size; i++) printf(“ %d “, arint[i]); } Miguela Fernandes 38 Cuidados... Um ponteiro deve sempre apontar para um local válido antes de ser utilizado Ex: void main () { int i=10, *pi; *pi = i; } /*erro ! pi nao tem endereco valido*/ Miguela Fernandes 39 Ponteiros e Strings strings são vectores de caracteres e podem ser acedidos através de char * void main () { char str[]=“abcdef”, *pc; for (pc = str; *pc != ‘\0’; pc++) putchar(*pc); } ==> abcdef o incremento de pc o posiciona sobre o próximo caracter (byte a byte) Miguela Fernandes 40 Ponteiros e Strings operações sobre strings com ponteiros void StrCpy (char *destino, char *origem) { while (*origem) /* *origem==0 encerra while */ { *destino=*origem; origem origem++; destino++; a b c d e \0 } *destino='\0'; a b } destino Miguela Fernandes 41 Ponteiros e Strings variação de strcpy: void strcpy (char *destino, char *origem) { while ((*destino = *origem) != ‘\0’) destino++, origem++; } Miguela Fernandes 42 Arrays Multidimensionais Arrays podem ter diversas dimensões, cada uma identificada por um par de colchetes na declaração Ex: [0,0] char matriz[5][10]; declara uma matriz de 5 linhas e 10 colunas: na memória, entretanto, os caracteres são armazenados linearmente: [0,0] [0,9] [4,9] [1,9] Miguela Fernandes [4,9] 43 Array de Caracteres Percorrendo array com ponteiro: void main () { char matriz[5][10]; char *pc; int i; for (i=0, pc=matriz[0]; i < 50; i++, pc++) *pc = ‘ ‘; } Miguela Fernandes 44 Array de Caracteres Percorrendo array com indices: void main () { char matriz[5][10]; int i, j; for (i=0, j=0; i<5; i++) for (; j<10; j++) matriz[i][j] = ‘ ‘; } as colunas (dimensões mais a direita) mudam mais rápido Miguela Fernandes 45 Array de Inteiros Exemplo: considere o problema de conversão de data dia_do_ano: um dos 365 dias do ano, convertido a partir do mes e dia do mes Tabela que indica dias dos meses incluindo bissexto static char tabela_dias[2][13] = { { 0,31,28,31,30,31,30,31,31,30,31,30,31 } { 0,31,29,31,30,31,30,31,31,30,31,30,31 } }; Miguela Fernandes 46 Conversão de Data Organização lógica e física da tabela: tabela_dias 0 31 28 31 30 31 31 30 31 30 31 30 31 0 31 29 31 30 31 31 30 31 30 31 30 31 28 31 ... 31 30 memória 0 31 0 31 28 Miguela Fernandes 31 ... 31 47 Conversão de Data /* dia_do_ano: calcula dia do ano a partir do dia do mes */ int dia_do_ano(int ano, int mes, int dia) { int i, bis; bis = (ano%4)==0 && (ano%100)!=0 || (ano%400)==0; for (i = 1; i < mes; i++) dia += tabela_dias[bis][i]; return dia; } Miguela Fernandes 48 Array de Strings Neste caso, cada elemento do array é um ponteiro para um caracter Declaração: char *arstr[] = {“Joao”, “Maria”, “Antonio”, “Zacarias”, “Carlos”}; arstr é um array de ponteiros para char, iniciado com os strings indicados Miguela Fernandes 49 Array de Strings Comparando array de string com matriz de char char *as[]= {“Joao”,“Maria”,“Antonio”,“Zacarias”,“Carlos”}; char ma[5][10]= {“Joao”,“Maria”,“Antonio”,“Zacarias”,“Carlos”}; Ponteiros (as) “Joao” “Maria” “Antonio” “Zacarias” “Carlos” Matriz (ma) J M A Z C Miguela Fernandes o a n a a a r t c r o \0 i a \0 o n i o \0 a r i a s \0 l o s \0 50 Cuidados com Strings É comum esquecer de alocar uma área para armazenamento de caracteres void main() { char *pc; char str[] = “Um string”; strcpy(pc, str); /* erro! pc indeterminado */ ... } Miguela Fernandes 51 Ponteiros para Ponteiros É possível definir ponteiros para ponteiros até um nível arbitrário de indirecção Ex: char *pc; /* ponteiro para char */ char **ppc;/* ponteiro para ponteiro para char */ pc = “teste”; ppc = &pc; putchar(**ppc); /* ==> ‘t’ */ Miguela Fernandes 52 Ponteiros para Ponteiros Ponteiro para ponteiro para ponteiro... Ex: char *pc, **ppc, ***pppc; Um ponteiro permite modificar o objeto apontado ou apontar para outro objeto do mesmo tipo Miguela Fernandes 53