39
Programação de Computadores Aula 6 06/09/2012 Profª Ms. Engª Elaine Cecília Gatto Unidade 5: Ponteiros Curso de Bacharelado em Engenharia de Computação Universidade do Sagrado Coração – USC Bauru/SP

Aula 6 pc - slides

Embed Size (px)

Citation preview

Page 1: Aula 6   pc - slides

Programação de Computadores Aula 6

06/09/2012

Profª Ms. Engª Elaine Cecília Gatto

Unidade 5: Ponteiros

Curso de Bacharelado em Engenharia de Computação

Universidade do Sagrado Coração – USC

Bauru/SP

Page 2: Aula 6   pc - slides

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;

Page 3: Aula 6   pc - slides

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 agrumentos 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;

Page 4: Aula 6   pc - slides

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;

Page 5: Aula 6   pc - slides

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;

Page 6: Aula 6   pc - slides

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!

Page 7: Aula 6   pc - slides

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;

Page 8: Aula 6   pc - slides

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

Page 9: Aula 6   pc - slides

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;

Page 10: Aula 6   pc - slides

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;

Page 11: Aula 6   pc - slides

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;

}

Page 12: Aula 6   pc - slides

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;

Page 13: Aula 6   pc - slides

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

y

Local 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.

Page 14: Aula 6   pc - slides

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;

Page 15: Aula 6   pc - slides

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;

Page 16: Aula 6   pc - slides

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;

}

Page 17: Aula 6   pc - slides

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

Page 18: Aula 6   pc - slides

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

Page 19: Aula 6   pc - slides

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

Page 20: Aula 6   pc - slides

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

Page 21: Aula 6   pc - slides

Recapitulando: precedência de operadores Operadores 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

Page 22: Aula 6   pc - slides

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;

Page 23: Aula 6   pc - slides

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

}

Page 24: Aula 6   pc - slides

Operações com ponteiros

printf("\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

Page 25: Aula 6   pc - slides

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 indireto

printf("&px = %p \n", &px); //operador de endereços

printf("\n px-py = %u \n \n", (px-py));

system("Pause");

return 0;

}

Page 26: Aula 6   pc - slides

Operações com ponteiros

Page 27: Aula 6   pc - slides

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.

Page 28: Aula 6   pc - slides

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

Page 29: Aula 6   pc - slides

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--;

Page 30: Aula 6   pc - slides

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)

Page 31: Aula 6   pc - slides

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.

Page 32: Aula 6   pc - slides

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;

Page 33: Aula 6   pc - slides

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;

Page 34: Aula 6   pc - slides

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:

Page 35: Aula 6   pc - slides

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; }

Page 36: Aula 6   pc - slides

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:

Page 37: Aula 6   pc - slides

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 inteiro int cubo(int n){ return n*n*n; }

Page 38: Aula 6   pc - slides

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 inteiro void cubo(int *nPtr){ *nPtr = (*nPtr) * (*nPtr) * (*nPtr); }

*nPtr na verdade é a variável numero

Page 39: Aula 6   pc - slides

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 inteiro void reajuste(float *preco, float *reaj){ *reaj = *preco * 0.2; *preco *= 1.2; }