Aula prática 8
Ponteiros
Monitoria de Introdução à Programação
Roteiro
Ponteirosl Definição.l Operadores.l Ponteiros e Variáveis.l Ponteiros e Vetores.l Ponteiros e Funções.
DuvidasExercícios
Ponteiros - Definição
Ponteiros são tipos de dados que referenciam (ou “apontam” para) endereços de memória.
Em algumas linguagens com maior abstração, ponteiros não existem
expostos ao programador (como em Java) ou tem alternativas mais seguras em outros tipos de dados (como o tipo “referência” em C++).
Acessar o valor nesse endereço é chamado de “dereferenciar” o ponteiro.
Em C, um ponteiro é um numero inteiro, referindo-se ao endereço na
memória.
Ponteiros - Declaração
A sintaxe para declarar um ponteiro, é, em C:
tipo *nome; ou tipo* nome;
Para declarar ponteiros em uma mesma linha deve-se usar o * para
cada ponteiro: tipo *pont1, *pont2, var, *pont3; Como qualquer outro tipo, podemos ter vetores de ponteiros:
tipo *vetorDePonteiros[tamanho];
E, como um ponteiro é um tipo, podemos ter ponteiros para ponteiros
(ad infinitum):
tipo **ponteiroDePonteiroDeTipo;tipo ***ponteiroDePonteiroDePonteiroDeTipo;
Ponteiros - Operadores - &
Para a atribuição de valores para ponteiros, usamos o operador “=“,
como fizemos com qualquer outro tipo, MAS: Em geral não se atribuem valores arbitrários aos ponteiros pois
raramente usa-se endereços que são constantes* para todas as execuções do programa. Em vez disso utiliza-se o operador & para se obter o endereço de variáveis.
Exemplo: int var; int* ponteiro;
ponteiro = &var; //ponteiro recebe o endereço de var
*: Exceção para o endereço NULL, que equivale ao endereço 0, normalmente retornadopor funções em caso de erro, ou para indicar que o ponteiro não aponta para lugar
nenhum.
Ponteiros - Operadores - * Mas somente obter o endereço não é o bastante. É preciso
também poder acessar o conteúdo de endereços. Para isso é utilizado o operador * (dereferenciador):
int var = 5; int* pont = &var; //Guarda o endereço de var printf(“%d”, *pont); /* Imprime o conteúdo do endereço
guardado por pont, isto é, 5 */
Podemos também usar a notação de vetores para acessar o conteúdo de um ponteiro (pois vetores são como ponteiros):
int vetorInt[6]; int* pvetor = vetorInt; //Recebe o endereço inicial do vetor int inteiro = pvetor[5];
O que é equivalente a: inteiro = *(pvetor + 5);
Ponteiros - Cuidados
Porém, ao acessar o conteúdo de um ponteiro, devemos ter cuidado:
um ponteiro com um endereço de memória inválido ou nulo, ao ser dereferenciado, irá causar um erro de “Falha de segmentação” (segmentation fault), finalizando forçadamente a execução de seu programa.
Coisas desse tipo devem ser evitadas:
double* pont; //Ponteiro não inicializado (possui lixo de memória)*pont = 2.5; //Altera conteúdo de endereço qualquer
long* pLong; int inteiro = *pLong; //Recebe conteúdo de endereço qualquer
char* string = NULL; puts(string); //Tenta alterar conteúdo do endereço NULL
Ponteiros - Aritmética
Podemos usar as operações de adição e subtração com ponteiros. Isso permite coisas desse tipo:
pInt = (pVetor + 5); pVetor++; pVetor--; pChar -= 3;
Multiplicação e divisão não são suportadas, nem soma de dois ponteiros,
pois isso não faz sentido se tratando de memória.
As operações de adição, subtração, incremento e decremento se dão em
função do tamanho do tipo para o qual o ponteiro aponta. Se tivermos um ponteiro para inteiro e incrementarmos esse ponteiro por um, ele apontará para o endereço de memória 4 bytes adiante.
Ponteiros e Variáveis – Acesso Indireto
Podemos, usando os operadores apresentados, fazer um ponteiro apontar
para um endereço de uma variável e com isso alterar, se quisermos, o valor dessa variável indiretamente.
float var = 2.0;float* pfloat = &var; //pfloat aponta para var *pfloat = 12.0; //Equivale a 'var = 12.0;'
Ponteiros - Vetores
Então podemos pensar, corretamente, que ao declarar uma variável da forma
float *pFloat;
Estamos declarando que o conteúdo ao qual pFloat aponta é do tipo float,
tornando pFloat um ponteiro para float.
Por isso, a seguinte declaração também é válida: char (*pString)[50];
Declarando que o conteúdo ao qual pString aponta é do tipo vetor de char
de 50 posições, tornando pString um ponteiro para vetor de char de 50 posições.
Como estamos declarando um ponteiro, e não a variável em si, memória
não é reservada para essa variável, que inicialmente aponta para um endereço qualquer na memória.
Ponteiros - Vetores
Como já foi dito, ponteiros guardam endereços. Vetores também. Portanto podemos acessar os valores do vetor usando ponteiros.
char string[20];char* pchar = string;
Para acessar cada elemento:
for(i = 0; i < 20; i++) { //Notação de vetor aux = pchar[i]; //Notação de ponteiro *(pchar + i) = funcao(); }
Ponteiros - Matrizes
Porém, para acessar matrizes através de ponteiros, temos que ter cuidado:
Uma matriz é um espaço contínuo na memória, sendo acessado diferenciando somente um endereço:
int matriz[20][10]; matriz[i][j]; //isso
*(matriz + i*10 + j); //Equivale a isso
Pode-se também olhar a matriz como um vetor de vetores e portanto
acessá-la usando ponteiro de ponteiro:
matriz[i][j]; //Isso*( *(matriz + i) + j); //Equivale a isso
Ponteiros - Passagem por referência Ponteiros, por serem endereços, permitem que acessemos e modifiquemos dados externos à função, de dentro da função, contornando a passagem de variáveis por cópia*:
void funcao(int* pont) { *pont = 5; /* A função irá modificar o conteúdo do endereço passado como parâmetro */ }
int main() { int var = 4; funcao(&var); //Passo o endereço de 'var' como parâmetro printf(“%d”, var); //E portanto o valor impresso será 5
return 0; } *: A passagem ainda é por cópia, mas o valor copiado é o endereço.
Ponteiros - Passagem por referência
Podemos, dessa forma, “retornar” mais de um valor por execução de função.
Isso é muito útil quando é preferível retornar o estado da execução da
função, como um código de erro ou de execução correta:
Dúvidas?
Exercício 1
Interferência!Um matemático estava avaliando um fenômeno e percebeu um comportamento estranho no seu sinal. Ele tentava produzir um sistema crescente, mas percebeu que ocorriam oscilações bruscas de sinal. Ele percebeu que a função do tempo que rege o sinal é:
se t é divisível por 3 , se t é divisível por 2 e não por 3 , se t não for divisível por 2 nem por 3.
Faça um programa que receba do usuário um inteiro ‘t’ e que useuma função void que receba um ponteiro desse inteiro, para que este seja modificado e no final seja printado o resultado de f(t).
Exs.: f(6) = 2; f(7) = 3; f(8) = 77844992; f(9) = 3, f(10) = 711312970
Exercício 2
l Faça um programa que receba um vetor de até 20 inteiros e o inverta l trocando os seus elementos seguindo a ordem: l O último elemento com o primeiro, o segundo com o penúltimo, l o terceiro com o antepenúltimo...
l Exemplo:l Entrada: 5 valores 1 5 8 9 10 l Saída: 10 9 8 5 1
l Obs: l Deve ser feita uma função void swap para trocar os elementos l e deve-se acessar o vetor usando notação de ponteiros