Download - Linguagem C - Ponteiros
PONTEIROSProfª Ms. Engª Elaine Cecília GattoCurso de Bacharelado em Engenharia de ComputaçãoUniversidade do Sagrado CoraçãO – USCBauru/SP
Introdução• Ponteiros estão entre as capacidades mais difícieis de se
dominar na linguagem C;• Ponteiros permitem:
Simular uma chamada por referência;Criar e manipular estruturas dinâmicas de dados;Estruturas de dados podem crescer e encolher no tempo de
execução;Listas interligadas, filas, pilhas, árvores, etc., são exemplos
de estruturas de dados dinâmicas;• Ponteiros são variáveis cujos valores são endereços de
memória;• Uma variável comum contém claramente um valor específico;
Introdução• Ponteiros são utilizados em situações em que o uso do nome
de uma variável não é permitido ou é indesejável;• Ponteiros fornecem maneiras com as quais as funções podem
realmente modificar os argumentos que recebem – passagem por referência;
• Ponteiros passam matrizes e strings mais convenientemente de uma função para outra;
• Ponteiros manipulam os elementos de matrizes mais facilmente, por meio da movimentação de ponteiros para elas – ou parte delas – no lugar de índices entre colchetes;
Introdução• Ponteiros alocam e desalocam memória dinamicamente no
sistema;• Ponteiros passam para uma função o endereço de outra
função.• Um ponteiro é um tipo especial de variável que foi concebida
para conter o endereço de outra variável;• Um ponteiro armazena um endereço de memória, que é a
localização de outra variável;• Uma variável aponta para outra variável quando a primeira
contém o endereço da segunda;
Introdução• A memória do computador é dividida em bytes, numerados de
zero até o limite da memória do computador;• Esses números são chamados de endereços de bytes;• Um endereço é uma referencia que o computador usa para
localizar variáveis;• Seu endereço é o do primeiro byte ocupado por ela;• Os programas, quando carregados, ocupam uma certa parte
da memória;• Toda variável e toda função dos programas em C começam em
um endereço particular, que é chamado endereço da variável ou da função;
Introdução• Um ponteiro, diferentemente de uma variável comum, contém
um endereço de uma variável que contém um valor específico;• Uma variável comum referencia um valor diretamente;• Um ponteiro referencia um valor indiretamente;• INDIREÇÃO: é a referência de valor por meio de um ponteiro;• Ponteiros devem ser definidos antes de sua utilização, como
qualquer outra variável;• Exemplo:
int *countPtr, count;
• O que faz esse linha de código? Pense!
Introdução• A linha acima pode ser reescrita da seguinte forma:
int *countPrt;int count;
• Note que foram criadas duas variáveis do tipo INT, entretanto, uma delas é um ponteiro;
• Todo ponteiro necessita do símbolo * antes do nome da variável;
• Portanto, int *countPtr especifica que a variável countPtr é do tipo int * - um ponteiro para um inteiro, e pode ser lido de duas formas:countPtr é um ponteiro para int;countPtr aponta para um objeto do tipo int;
Introdução• A variável count é definida para ser um inteiro, e não um
ponteiro para int;• Se quisermos que count seja um ponteiro, devemos alterar a
declaração int count para int *count;
7
count countPtr
count referencia diretamente uma variável que contém o valor 7
7
count
countPtr referencia indiretamente uma variável que contém o valor 7
Introdução• Cuidado:
A notação *, usada para declarar variáveis de ponteiro, não distribui para todos os nomes de variáveis em uma declaração;
Cada ponteiro precisa ser declarado com o * prefixado ao nome;
Exemplo: se você quiser declarar xPtr e yPtr como ponteiros int, então use int *xPtr, *yPtr;
• Dica;Inclua as letras Ptr nos nomes de variáveis de ponteiros
para deixar claro que essas variáveis são ponteiros, e, portanto, precisam ser tratadas de modo apropriado;
Inicialize os ponteiros para evitar resultados inesperados;
Introdução• Ponteiros devem ser inicializados quando são definidios ou
então em uma instrução de atribuição;• Ponteiros podem ser inicializados com NULL, zero, ou um
endereço;• NULL: não aponta para nada, é uma constante simbólica;• Inicilizar um ponteiro com zero é o mesmo que inicializar com
NULL;• Nesse caso zero é convertido em um ponteiro apropriado;• Zero é o único valor inteiro que pode ser atribuídó
diretamente a um ponteiro. int *xPtr = NULL;
int *yPtr = 0;
Introdução• Exemplo: para conhecer o endereço ocupado por uma variável
usamos o operador de endereços &. O resultado da operação é um ponteiro constante.
#include <stdio.h>#include <stdlib.h>
int main(){int i, j, k;printf(“Endereço de i = %p \n”, &i);printf(“Endereço de j = %p \n”, &j);printf(“Endereço de k = %p \n”, &k);system(“Pause”);return 0;
}
Operadores de Ponteiros • & = operador de endereço = é um operador unário que
retorna o endereço de seu operando;• Exemplo:
int y = 5;int *yPtr;yPtr = &y
• A terceira instrução atribui o endereço da variável y à variável de ponteiro yPtr;
• A variável yPtr aponta para y;
Operadores de Ponteiros yPtr
5
y
Representação gráfica de um ponteiro apontando para uma variável inteira na memória
60.000
yPtr
5
yLocal na memória da variável yPtr:50.000
Local na memória da variável y:60.000
Representação gráfica de y e yPtr na memória
• Suponha que a variável y esteja armazenada no local 60.000 da memória;• Suponha que a variável yPtr esteja armazenada no local 50.000 da memória;• O operando do operador de endereço precisa ser uma variável;• O operador de endereço não pode ser aplicado a constantes, expressões ou
variáveis declaradas da classe register.
Operadores de Ponteiros • O operador unário *, retorna o valor do objeto apontado por
seu operando;• O operardor indireto * é unário e opera sobre um endereço ou
ponteiro;• O resultado da operação é o nome da variável localizada nesse
endereço – apontada;• O nome da variável representa o seu valor ou conteúdo;• Por fim, resulta o valor da variável apontada;• Exemplo:
printf(“%d”, *yPtr);• Imprime o valor da variável y;• O uso de * é chamado de desreferenciação de um ponteiro;
Operadores de Ponteiros • O operador de endereços & opera sobre o nome de uma
variável e resulta o seu endereço, já o operador indireto * opera sobre o endereço de uma variável e resulta o seu nome.
• Dica: • Acessar um conteúdo com um ponteiro que não foi
devidamente inicializado ou que não foi designado para apontar um local específico na memória é um erro. Isso poderia causar um erro fatal no temp de execução, ou poderia acidentalmente modificar dados e permitir que o programa fosse executado até o fim com resultados incorretos;
Operadores de Ponteiros • Exemplo: o código a seguir demonstra a utilização dos operadores & e *. %p mostra o
local da memória como um inteiro hexadecimal na maioria das plataformas. #include <stdio.h>#include <stdlib.h>int main(){
int a;int *aPtr;a = 7;aPtr = &a;printf("Endereco de a = %p \n", &a);printf("O valor de aPtr = %p \n", aPtr);printf("O valor de a = %d \n", a);printf("O valor de aPtr = %d \n", *aPtr);printf("* e & sao complementos um do outro: \n");printf("&*aPtr = %p \n", &*aPtr);printf("*&aPtr = %p \n", *&aPtr);system("Pause");return 0;
}
Operadores de Ponteiros #include <stdio.h>#include <stdlib.h>int main(){
int a;int *aPtr;a = 7;aPtr = &a;printf("Endereco de a = %p \n", &a);printf("O valor de aPtr = %p \n", aPtr);printf("O valor de a = %d \n", a);printf("O valor de aPtr = %d \n", *aPtr);printf("* e & sao complementos um do outro: \n");printf("&*aPtr = %p \n", &*aPtr);printf("*&aPtr = %p \n", *&aPtr);system("Pause");return 0;
}
Nesta parte do programa, as variáveis estão sendo declaradas e inicializadas. A variável a do tipo int recebe o valor 7. O ponteiro para um inteiro, aPtr, recebe o endereço da variável a, que é do tipo int.
aPtr
7
a
0022FF44 0022FF44
Operadores de Ponteiros #include <stdio.h>#include <stdlib.h>int main(){
int a;int *aPtr;a = 7;aPtr = &a;printf("Endereco de a = %p \n", &a);printf(“Valor de aPtr = %p \n", aPtr);printf(“Valor de a = %d \n", a);printf(“Valor de aPtr = %d \n", *aPtr);printf("* e & sao complementos um do outro: \n");printf("&*aPtr = %p \n", &*aPtr);printf("*&aPtr = %p \n", *&aPtr);system("Pause");return 0;
}
Imprimindo o local da memória da variável int a como um inteiro hexadecimal. Observe que o valor do ponteiro aPtr é o endereço da variável int a.
aPtr
7
a
0022FF45 0022FF44
Operadores de Ponteiros #include <stdio.h>#include <stdlib.h>int main(){
int a;int *aPtr;a = 7;aPtr = &a;printf("Endereco de a = %p \n", &a);printf(“Valor de aPtr = %p \n", aPtr);printf(“Valor de a = %d \n", a);printf(“Valor de aPtr = %d \n", *aPtr);printf("* e & sao complementos um do outro: \n");printf("&*aPtr = %p \n", &*aPtr);printf("*&aPtr = %p \n", *&aPtr);system("Pause");return 0;
}
Imprimindo os valores da variável int a e do ponteiro para um inteiro aPtr. Observe que aqui utiliza-se %d e não %p. Note que aqui estamos usando *aPtr e não apenas aPtr. Também na variável a, usa-se apenas a e não &a.
aPtr
7
a
0022FF45 0022FF44
Operadores de Ponteiros #include <stdio.h>#include <stdlib.h>int main(){
int a;int *aPtr;a = 7;aPtr = &a;printf("Endereco de a = %p \n", &a);printf(“Valor de aPtr = %p \n", aPtr);printf(“Valor de a = %d \n", a);printf(“Valor de aPtr = %d \n", *aPtr);printf("* e & sao complementos um do outro: \n");printf("&*aPtr = %p \n", &*aPtr);printf("*&aPtr = %p \n", *&aPtr);system("Pause");return 0;
}
Quando os dois operadores são aplicados consecutivamente à variável aPtr em qualquer ordem,então os operadores & e * são complementos um do outro
aPtr
7
a
0022FF44 0022FF44
Recapitulando: precedência de operadoresOperadores Associatividade Tipo
( ) [ ] Esquerda para direita Mais alta
+ - ++ -- ! * & Direita para esquerda Unário
* / % Esquerda para direita Multiplicativo
+ - Esquerda para direita Aditivo
< <= > >= Esquerda para direita Relacional
== != Esquerda para direita Igualdade
&& Esquerda para direita And lógico
|| Esquerda para direita Or lógico
?: Direita para esquerda Condicional
= += -= *= /= %= Direita para esquerda Atribuição
, Esquerda para direita Vírgula
Operações com ponteiros• C permite operações básicas com ponteiros;• Ponteiros são operandos válidos em expressões aritméticas,
atribução e comparação;• Nem todos os operadores normalmente usados nessas
expressões são válidos em conjunto com variáveis de ponteiro;
• Um conjunto limitado de operações aritméticas pode ser realizado com ponteiros;
• Um ponteiro pode ser incrementado ou decrementado;• Um inteiro pode ser somado/subtraído a um ponteiro;• Um ponteiro pode ser subtraído de outro ponteiro;
Operações com ponteiros#include <stdio.h>#include <stdlib.h>
int main(){
//declarações unsigned int x=5, y=6; unsigned int *px, *py;
//atribuições px = &x; py = &y;
printf("\n Comparacoes "); if(px<py){ printf("py-px = %u \n", (px-py)); //subtraindo }else{printf("px-py = %u \n", (py-px));//subtraindo }
Operações com ponteirosprintf("\n px = %p \n", px);
printf("*px = %u \n", *px); //operador indireto
printf("&px = %p \n", &px); //operador de endereços
printf("\n py = %p \n", py);
printf("*py = %u \n", *py); //operador indireto
printf("&py = %p \n", &py); //operador de endereços
py++; //incrementando py printf("\n Apos incrementar py: \n ");
printf("\n py = %p \n", py);
printf("*py = %u \n", *py);//operador indireto
printf("&py = %p \n", &py);//operador de endereços
Operações com ponteiros
px=py+5; //somando inteiros
printf("\n Apos somar py+5 \n ");printf("\n px = %p \n", px);printf("*px = %u \n", *px); //operador indiretoprintf("&px = %p \n", &px); //operador de endereços
printf("\n px-py = %u \n \n", (px-py)); system("Pause");return 0;}
Operações com ponteiros
Operações com ponteiros• Exemplo de soma:
int a[5];aPtr = v;
aPtr = &a[0];aPtr += 2;
• Suponho que o primeiro elemento esteja no local 3000 e que o inteiro esteja armazenado em 4 bytes da memória, o resultado de aPtr+=2 será 3008 pois (3000 + 2 * 4) = 3008.
• Se o inteiro estiver armazenado em 2 bytes da memória, então o resultado será 3004, pois (3000 + 2 * 2) = 3004.
Operações com ponteiros• Ilustrando:
a[0] a[1] a[2] a[3] a[4]
3000 3004 3008 3012 3016
Variável de ponteiro aPtr
Local na memória
a[0] a[1] a[2] a[3] a[4]
3000 3004 3008 3012 3016
Variável de ponteiro aPtr
Local na memória
Antes da soma
Após a soma
Operações com ponteiros• Se um ponteiro estiver sendo incrementado ou decrementado
em um, os operadores de incremento e decremento poderão ser usados. As instruções abaixo incrementam e decrementam o ponteiro para que ele aponte para o próximo local ou para o local anterior. No caso do vetor, aponta para o próximo elemento ou elemento anterior.
• ++aPtr;• aPtr++;• --aPtr;• aPtr--;
Operações com ponteiros• Suponha que aPtr tem o local 3000 e a2Ptr tem o local 3008,
então:x = a2Ptr – aPtr;
• Faz com que x receba o número de elementos do vetor aPtr para a2Ptr, resultado = 2 (duas posições).
• Aritmética de ponteiros só funciona com ARRAYS (matrizes e vetores);
• A menos que as variáveis de mesmo tipo sejam elementnos adjacentes de um array, elas não serão armazenadas em posições contíguas na memória (uma após a outra)
Operações com ponteiros• Cuidado:• Usar aritmética de ponteiro em um ponteiro que não se
aplique a um elemento em um array causa um erro;• Subtrair ou comparar dois ponteiros que não se referem a
elementos do mesmo array causa também um erro;• Ultrapassar o final de um array ao usar a aritmética de
ponteiro também causa um erro;• Dica:• A maioria dos computadores de hoje tem inteiros de 2 bytes
ou 4 bytes. Algumas das máquinas mais novas utilizam inteiros de 8 bytes. Como os resultados da aritmética de ponteiro dependem do tamanho dos objetos que um ponteiro aponta, a aritmética de ponteiro é dependente da máquina.
Ponteiro Void• Um ponteiro pode ser atribuído a outro se ambos forem do
mesmo tipo, exceto quando o ponteiro for um ponteiro para void;
• Exemplo: não podemos atribuir um endereço de uma variável int a um ponteiro float;
• Um ponteiro para void é um ponteiro genérico que pode representar qualquer tipo de ponteiro;
• Todos os tipos de ponteiro podem receber um ponteiro para void, e este pode receber um ponteiro de qualquer tipo;
• Um ponteiro para void não pode ser desreferenciado;• Um ponteiro para int refere-se a 4 bytes de memória em uma
máquina com inteiros de 4 bytes;
Ponteiro Void• O compilador então sabe quanto de espaço reservar para um
ponteiro do tipo int;• Mas o compilador não sabe quanto de espaço reservar, na
memória, para um tipo void;• Um ponteiro para void simplesmente contém um local da
memória para um tipo de dado desconhecido;• O número exato de bytes aos quais o ponteiro se refere não é
conhecido pelo compilador;• Um ponteiro void é um ponteiro de propósito geral;• Atribuir um ponteiro de um tipo a um ponteiro de outro tipo
se nenhum deles for do tipo void * consistem em um erro de sintaxe;
Ponteiro Void• São usados em situações em que seja necessário que uma
função receba ou retorne um ponteiro genérico e opere independentemente do tipo de dado apontado;
• Qualquer endereço pode ser atribuído a um ponteiro void;void *p;
• O conteúdo da variável de um ponteiro void não pode ser acessado por meio desse ponteiro;
• Para isso é necessário criar outro ponteiro e fazer a conversão de tipo na atribuição;
• Exemplo:
Ponteiro Void#include <stdio.h>#include <stdlib.h>
int main(){ int i=5, *pi; float f=3.2, *pf; void *pv; pv = &i; pi=(int *)pv; //CONVERSÃO printf("\n *pi = %d \n ", *pi); pv = &f; pf =(float *)pv; //CONVERSÃO printf("\n *pf = %3.2f \n \n", *pf); system("PAUSE"); return 0;}
Passando argumentos por referência com ponteiros• Uma função pode receber diversos argumentos, mas só
consegue retornar um único valor por meio do comando return;
• Usando ponteiros, é possível que uma função retorne mais de um valor para a função chamadora;
• Duas são as formas de passar argumentos para uma função: chamada por valor e chamada por referência;
• Exemplos:
Passando argumentos por referência com ponteiros• USANDO CHAMADA POR VALOR#include <stdio.h>#include <stdlib.h>int cubo(int n);int main(){ int numero = 5, resultado; printf("\n Valor da variavel numero: %d ", numero); //passando o número por valor à função cubo resultado = cubo(numero); printf("\n Valor da varivel resultado: %d ", resultado); printf("\n \n"); system("PAUSE"); return 0;}//função para calcular o cubo de um numero inteiroint cubo(int n){ return n*n*n;}
Passando argumentos por referência com ponteiros• USANDO CHAMADA POR REFERÊNCIA#include <stdio.h>#include <stdlib.h>void cubo(int *nPtr);int main(){ int numero = 5, resultado; printf("\n Valor da variavel numero: %d ", numero); //passando o número por valor à função cubo cubo(&numero); printf("\n Valor da varivel resultado: %d ", numero); printf("\n \n"); system("PAUSE"); return 0;}//função para calcular o cubo de um numero inteirovoid cubo(int *nPtr){ *nPtr = (*nPtr) * (*nPtr) * (*nPtr);}
*nPtr na verdade é a variável numero
Passando argumentos por referência com ponteiros• USANDO CHAMADA POR REFERÊNCIA
#include <stdio.h>#include <stdlib.h>void reajuste(float *, float *);int main(){ float preco, reaj; do { printf("\n Insira o preco atual: "); scanf("%f", &preco); reajuste(&preco, &reaj); printf("\n Novo preco: %3.2f ", preco); printf("\n O aumento foi de %3.2f ", reaj); printf("\n"); } while(preco!=0.0); system("PAUSE"); return 0;}//função para calcular o cubo de um numero inteirovoid reajuste(float *preco, float *reaj){ *reaj = *preco * 0.2; *preco *= 1.2; }