Baixe o app para aproveitar ainda mais
Prévia do material em texto
Laboratório de Programação II Departamento de Ciência da Computação UFJF Aula de hoje • Ponteiros • Ponteiros • Ponteiros 2 Introdução • Cada objeto (variável, string, vetor etc.) que reside na memória do computador ocupa um certo número de bytes: – char: 1 byte – short: 2 bytes – int: 4 bytes – float: 4 bytes – double: 8 bytes 3 Introdução • A memória de qualquer computador é uma sequência de bytes. • Cada byte na memória é identificado por um endereço numérico, independente do seu conteúdo. • Muitas vezes endereços de memória são representados no formato hexadecimal: – 0x0065FD40 – 0x0065FD44 4 Introdução • Ou seja, sempre que declaramos uma variável, temos associados a ela: – um nome (ou identificador) – um endereço de memória – um valor • Sempre que declaramos uma variável, o programa aloca espaço de memória para ela e sabe internamente aonde ela está armazenada. 5 Endereços • Antes de falar de ponteiros, vamos ver como descobrir o endereço de variáveis. • Para isso é preciso usar o operador “endereço de”, representado por &, a uma variável para descobrir o seu endereço. • Se total é a variável, então &total é o seu endereço. 6 Endereços #include <iostream> using namespace std; int main() { int biscoitos = 6; double peso = 4.5; cout << “valor de biscoitos = “ << biscoitos ; cout << “ e endereco de biscoitos = “; cout << &biscoitos << endl; cout << “valor de peso = “ << peso; cout << “e endereco de peso = “ << &peso << endl; return 0; } 7 Endereços #include <iostream> using namespace std; int main() { int biscoitos = 6; double peso = 4.5; cout << “valor de biscoitos = “ << biscoitos ; cout << “ e endereco de biscoitos = “; cout << &biscoitos << endl; cout << “valor de peso = “ << peso; cout << “e endereco de peso = “ << &peso << endl; return 0; } 8 Saída valor de biscoitos: 6 e endereco de biscoitos: 0x0065FD40 valor de peso: 6 e endereco de peso: 0x0065FD44 Ponteiros • Um ponteiro é simplesmente uma variável que armazena o endereço de outra variável ao invés do conteúdo desta. • Temos assim um tipo de dado especial – o ponteiro – o qual armazena o endereço de um valor. • O nome da variável de um tipo ponteiro representa a posição de memória daquele valor. • Aplicando o operador * (“conteúdo de”) recuperamos o valor armazenado naquela posição de memória. 9 Ponteiros • Isto é, se pt_x é um ponteiro, então *pt_x é o valor naquele endereço. • Através do operador *, podemos alterar o valor que está armazenado em uma posição de memória *pt_x = 100; 10 Ponteiros int main() { int updates = 6; int *p_updates; p_updates = &updates; // imprime valores cout << “Valor: updates = “ << updates; cout << “, *p_updates = “ << *p_updates << endl; // imprime enderecos cout << “Endereço: &updates = “ << &updates; cout << “, p_updates = “ << p_updates << endl; // usa ponteiro para alterar o conteudo *p_updates = *p_updates + 1; cout << “Valor atualizado updates = “ << updates << endl; return 0; } 11 Ponteiros int main() { int updates = 6; int *p_updates; p_updates = &updates; // imprime valores cout << “Valor: updates = “ << updates; cout << “, *p_updates = “ << *p_updates << endl; // imprime enderecos cout << “Endereço: &updates = “ << &updates; cout << “, p_updates = “ << p_updates << endl; // usa ponteiro para alterar o conteudo *p_updates = *p_updates + 1; cout << “Valor atualizado updates = “ << updates << endl; return 0; } Saída Valor: updates = 6 , *p_update = 6 Endereço: &updates = 0x0065FD48, p_updates = 0x0065FD48 Valor atualizado updates = 7 12 Ponteiros • Declarando ponteiros: • Podemos ler a declaração da seguinte forma: • *pt_var é do tipo int • ou • pt_var é do tipo int* (isto é ponteiro para int) • Podemos usar: • Ou ainda declarar ponteiros para outros tipos: int *pt_var; int *pt_var; int* pt_var; int * pt_var; char *pt_var1; float *pt_var2; double *pt_var3; 13 Usar esta (convenção) Ponteiros • Podemos declarar e inicializar um ponteiro • o que inicializa pt_num com o endereço da variável numero. • É preciso tomar cuidado ao usar variáveis do tipo ponteiro! int numero = 10; int *pt_num = № // erro int *pt_var; *pt_var = 2058; Quando declaramos uma variável do tipo ponteiro, espaço para armazenar um endereço de memória é criado, mas isso não significa que memória foi alocada para armazenar a informação que o ponteiro aponta. 14 Ponteiros 15 Aritmética de Ponteiros • Somar 1 a uma variável inteira incrementa o seu valor de uma unidade. • O que significa somar 1 a uma variável do tipo ponteiro? – Depende do tipo do ponteiro. – Nesse exemplo, a adição soma 4 ao valor numérico que representa o endereço contido em pt_num, pois um inteiro ocupa 4 bytes na memória. – Ou seja, ao somar 1 a um ponteiro, você está indo para o próximo endereço de memória do tipo de dado especificado. – Por outro lado, somar 1 a um ponteiro para double, adiciona 8 ao valor numérico do endereço que o ponteiro armazena. – Generalizando: int numero = 10; int *pt_num = № pt_num = pt_num + 1; 16 pt_tipo = pt_tipo + tam(tipo)*n; Aritmética de Ponteiros int x; int *ap; cout << “Digite x: “; cin >> x; ap = &x; cout << “Endereço de x = “ << &x; cout << endl; cout << “Valor de ap= “ << ap; cout << endl; cout << “Valor de ap+1= “ << ap+1; cout << endl; cout << “Valor de ap+2= “ << ap+2; cout << endl; Saída Endereço de x = 0x7FFFE5B7B53C Valor de ap = 0x7FFFE5B7B53C Valor de ap+1 = 0x7FFFE5B7B540 Valor de ap+2 = 0x7FFFE5B7B544 17 Ponteiros e Arrays • C++ interpreta o nome de um array como o endereço de memória do primeiro elemento do array. int vet[3] = {10,20,30}; cout << "enderecos" << endl; cout << vet << endl; cout << vet+1 << endl; cout << vet+2 << endl; cout << "valores" << endl; cout << vet[0] << endl << vet[1] << endl << vet[2] << endl; 18 Ponteiros e Arrays 19 Ponteiros e Arrays • Ou seja, temos duas formas de acessar os elementos de um array: – usando o operador [] • vet[2] • vet[i] – usando aritmética de ponteiros • *(vet+2) • *(vet+i) 20 Ponteiros e Funções • Em C/C++ existem dois tipos de passagem de parâmetros para funções: – Passagem por valor • Uma cópia do valor é passado para a função • Mesmo que a função altere o valor, esta alteração não permanecerá no parâmetro original após o retorno da função – Passagem por referência • Se a função alterar o valor do objeto passado, essa alteração será realizada no objeto original 21 22 void troca(int a, int b) { int aux = b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } Saída x=2 , y=30 !Errado! Ponteiros e Funções 22 23 Ponteiros e Funções void troca(int a, int b) { int aux = b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 23 24 Ponteiros e Funções void troca(int a, int b) { int aux = b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 24 25 Ponteiros e Funções void troca(int a, int b) { int aux= b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 25 26 Ponteiros e Funções void troca(int a, int b) { int aux = b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 26 27 Ponteiros e Funções void troca(int a, int b) { int aux = b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 27 28 Ponteiros e Funções void troca(int a, int b) { int aux = b; b = a; a = aux; } int main() { int x=2, y=30; troca(x, y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } Saída x=2 , y=30 28 29 void troca(int *a, int *b); Ponteiros e Funções • Para alterar o conteúdo de uma variável passada para uma função como argumento é preciso usar passagem por referência. • Passamos então o endereço (ponteiro) do objeto que desejamos que a função altere o conteúdo • Na função manipulamos o conteúdo do objeto usando o operador * (“conteúdo de”) para alterar o seu valor. • O protótipo correto da função é: 29 30 void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } Saída x=30 , y=2 Ponteiros e Funções 30 31 Ponteiros e Funções void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 31 32 Ponteiros e Funções void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 32 33 Ponteiros e Funções void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 33 34 Ponteiros e Funções void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 34 35 Ponteiros e Funções void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } 35 36 void troca(int *a, int *b) { int aux = *b; *b = *a; *a = aux; } int main() { int x=2, y=30; troca(&x, &y); cout << “x = “ << x << “ , “; cout << “y = “ << y << endl; return 0; } Saída x=30 , y=2 OK! Ponteiros e Funções 36 37 Ponteiros e Funções • Como arrays são ponteiros (nome do array é um ponteiro para o primeiro elemento), então a passagem de arrays para funções é sempre por referência. • Sendo assim, podemos alterar os valores dos elementos do array dentro da função. • Os elementos do array não são copiados, apenas o endereço do primeiro elemento do array. 37 38 void incr_vet(int tam, int vet[]) { int i; for(i=0; i<tam; i++) vet[i] = vet[i] + 1; } int main() { int v[]={10, 20, 5}; incr_vet(3, v); cout << v[0] << endl; cout << v[1] << endl; cout << v[2] << endl; return 0; } Ponteiros e Funções Saída 11 21 6 38 Ponteiros e Funções • Podemos usar os seguintes protótipos para declarar uma função que recebe um array: • Todas são equivalentes, e no final das contas o que é passado para a função é o endereço do primeiro elemento do array. • Por fim, vale lembrar que funções também podem retornar ponteiros. Veremos mais detalhes na próxima aula. void incr_vet(int tam, int vet[]); void incr_vet(int tam, int vet[3]); void incr_vet(int tam, int *vet); 39 Ponteiros e structs • Também podemos ter ponteiros para tipos de dados definidos pelo programador. • Como? struct aluno_t { int matricula; int idade; int notas[10]; char sexo; }; typedef struct aluno_t Aluno; Aluno a; a.matricula = 1005; a.idade = 23; a.notas[0] = 60; a.notas[1] = 86; // … a.sexo = ‘m’; cout << “Idade: “; cout << a.idade << endl; // … 40 Ponteiros e structs • Declaramos como um ponteiro qualquer Aluno *pta; • Quando temos um ponteiro para uma estrutura, acessamos os campos da estrutura usando o operador -> ao invés de usar o operador . Aluno a; pta = &a; pta->matricula = 1005; pta->idade = 23; pta->notas[0] = 60; pta->notas[1] = 86; // … pta->sexo = ‘m’; cout << “Idade: “ << pta->idade << endl; // … 41 Exercícios 1. Ponteiro e aritmética de ponteiro (faça com auxílio do computador). Se i e j são variáveis inteiras e p e q ponteiros para int, quais das seguintes expressões de atribuição são incorretas? a) p = &i; b) *q=&j; c) p=&*&i; d) i=(*&)j; e) i=*&*&j; f) q=&p; g) i=(*p)++ + *q; h) if(p==i)i++; 42 Exercícios 2. Dados dois números inteiros num e div. Fazer uma função para calcular e retornar o quociente e o resto da divisão inteira de num por div. Considerar o seguinte protótipo: void divisao(int num, int div, int *q, int *r); • onde: – num: dividendo – div: divisor – q: quociente – r: resto 43 Exercícios 3. Implementar uma única função que recebe um vetor de números inteiros (vet) e o seu tamanho (tam) e – conte o total de elementos pares – conte o total de elementos impares – conte o total de elementos negativos – e por fim, retorne verdadeiro se existirem números negativos no vetor, ou retorne falso, caso contrário Considere o seguinte protótipo: bool func(int tam, int vet[], int *par, int *imp, int *neg); 44 Exercícios 4. Considere o seguinte tipo de dados para representar um ponto no espaço 2D: Fazer uma função que determina se um ponto p está dentro ou fora do retângulo definido pelo vértice inferior esquerdo v1 e pelo vértice superior direito v2. Considere o protótipo: bool dentroRet(Ponto *v1, Ponto *v2, Ponto *p); struct ponto_t { float x; float y; }; typedef struct ponto_t Ponto; 45 Exercícios (*) Ponteiro e aritmética de ponteiro (faça com auxílio do computador, http://inforum.insite.com.br/arquivos/17035/Ponteiros1.pdf) • Escreva uma instrução que imprima o endereço da variável x. • O que significa o operador asterisco em cada um dos seguintes casos: a) int *p; b) cout << *p; c) *p = x*5; d) cout << *(p+1); • Assuma as declarações: int i=3, j=5; int *p = &i; *q = &j;. Indique qual o valor das seguintes expressões: a) p == &q; b) *q-*; c) **&p; d)3*-*p/(*q)+7; • Qual é a saída deste programa? #include <iostream.h> void main( ) { int i=5, *p; p = &i; cout << p << ‘\t’ << (*p+2) << ‘ \t’ << **&p << ‘\t’ << (3**p) << ‘ \t’ << (**&p+4); } 46 Exercícios (*) Ponteiro e aritmética de ponteiro (faça com auxílio do computador) Qual a diferença entre: mat[3] e *(mat+3)? O que fazem os seguintes programas: void main( ) { int mat[ ] = {4, 5, 6}; for(int j=0; j<3; j++) cout << “ \n” << *(mat+j);} void main( ){ int mat[ ] = {4, 5, 6}; for(int j=0; j<3; j++) cout << “ \n” << (mat+j); } void main( ) { int mat[ ] = {4, 5, 6}; int *p=mat; for(int j=0; j<3; j++) cout << “ \n” << *p++;} 47 Exercícios (*) Passagem de parâmetros por referência • Implementar uma função que calcule as raízes de uma equação do segundo grau, do tipo ax2+bx+c=0. Essa função deve obedecer ao protótipo: int raizes(float a, float b, float c, float *x1, float *x2); Essa função deve ter como valor de retorno o número de raízes reais e distintas da equação. Se existirem raízes reias, seus valores devem ser armazenados nas variáveis apontadas por x1 e x2. • Implementar um função que calcule a área da superfície e o volume de uma esfera de raio r. Essa função deve obedecer ao protótipo: void calc_esfera(float r, float *area, float *volume); A área da superfície e o volume são dados, respectivamente, por 4r2 e 4r3/3. Use a função pow(x, y) para calcular xy. Deve-se usar #include <cmath> 48
Compartilhar