Revisão sobre ponteiros em C

355 visualizações

Publicada em

Material de apoio das aulas de tutoria de Algoritmos e Estrutura de dados da Universidade Federal de Ouro Preto, Campus João Monlevade. O conteúdo abordado é uma revisão sobre ponteiros na linguagem de programação C.

Publicada em: Educação
0 comentários
0 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

Sem downloads
Visualizações
Visualizações totais
355
No SlideShare
0
A partir de incorporações
0
Número de incorporações
3
Ações
Compartilhamentos
0
Downloads
14
Comentários
0
Gostaram
0
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

Revisão sobre ponteiros em C

  1. 1. Tutoria AEDSI Pablo Silva
  2. 2. Revisão Nesta revisão, iremos relembrar os conceitos importantes de ponteiros e aplicá-los para entender o que é passagem de parâmetros por referência para uma função e como ela se diferencia da passagem por valor.
  3. 3. Ponteiros? - Ponteiros são tipos de variáveis que recebem endereços. - Esses endereços são os lugares da memória onde estão armazenados valores de variáveis. - O nome ponteiro é uma analogia, referindo que esse tipo de variável “aponta”para um endereço de memória.
  4. 4. Estrutura de um ponteiro int * pont; Tipo do ponteiro: como dito o ponteiro é uma variável, logo precisa de um tipo. Este tipo, indica o tipo de valor que está armazendo no endereço associado com este ponteiro, ou em outras palavras, qual o tipo de dado que o espaço de memória aceita. Operador asterisco: o que diferencia ponteiros das outras variáveis é o operador asterisco. Nome do ponteiro: segue as mesmas regras de nomeação de variáveis.
  5. 5. Usando um ponteiro Vamos ver como é a estrutura de uma função int main() { int a = 10; int* p = &a; return 0; } Tipos compatíveis: note que o tipo de endereço que estamos passando para o ponteiro é int, uma vez que nosso ponteiro é do tipo inteiro. Se no lugar de a fosse atribuído um caracter por exemplo (char b), o programa não compilaria, pois estaríamos lidando com tipos incompatíveis. Operador endereço: Uma vez que um ponteiro recebe um endereço de uma variável, foi preciso definir um operador que extraísse esse endereço. O operador que faz isso é o operador de endereço. &a significa “O endereço da variável a”.
  6. 6. Desenhos para analogia A partir de agora, vamos pensar em variáveis como “caixinhas” que recebem valores dentro delas. Exemplo: int main() { int a = 10; return 0; } A representação destas variáveis ficariam assim: 10 a &43jjuiop a é o nome da caixa. 8 dígitos seguidos do símbolo de endereço, é o endereço desta caixa, chamado também de referência. Valor da caixa Um fato importante aqui: variáveis comuns só recebem valores (int, char, float...). Uma caixa que representa uma variável comum nunca receberá um endereço como seu valor (apesar de que toda caixa possui um endereço de memória)
  7. 7. Desenhos para analogia A partir de agora, vamos pensar em variáveis como “caixinhas” que recebem valores dentro delas. Exemplo: int main() { int a = 10; int* p = &a; return 0; } A representação da caixa de um ponteiro: &43jjuiop p &98sd66dd p é o nome da caixa. Por ser uma variável, um ponteiro também precisa ser armazenada na memória e por isso tem um endereço de caixa. Um fato importante aqui: variáveis ponteiros recebem somente endereços e não valores! O conteúdo de um ponteiro nunca será um int, char, etc. mas sim um endereço de um tipo. Para este caso, o endereço é do tipo inteiro (variável a é do tipo inteiro). Endereço de a (valor que o ponteiro recebeu)
  8. 8. Desenhos para analogia Uma possível representação da relação entre ponteiro e varíavel para qual ele aponta seria: int main() { int a = 10; int* p = &a; return 0; } &43jjuiop p &98sd66dd 10 a &43jjuiop O ponteiro aponta para a caixa de a, porque o endereço de a foi passado para ele. Uma característica do ponteiro importantíssima é que por apontar para a caixa de a, ele tem acesso ao seu valor (10) e pode alterá- lo a qualquer momento!
  9. 9. Características e funções de um ponteiro Como dito, um ponteiro tem acesso ao valor da variável para qual ele aponta. Vamos ver como alterar esse valor através do ponteiro. int main() { int a = 10; int* p = &a; *p = 5; return 0; } 10 a &43jjuiop 1 2 3 Passo 1: Variável a é criada.
  10. 10. Características e funções de um ponteiro Como dito, um ponteiro tem acesso ao valor da variável para qual ele aponta. Vamos ver como alterar esse valor através do ponteiro. int main() { int a = 10; int* p = &a; *p = 5; return 0; } &43jjuiop p &98sd66dd 10 a &43jjuiop 1 2 3 Passo 2: O ponteiro p é criado e o endereço de a é atribuído à ele. Portanto p aponta para a.
  11. 11. Características e funções de um ponteiro Como dito, um ponteiro tem acesso ao valor da variável para qual ele aponta. Vamos ver como alterar esse valor através do ponteiro. int main() { int a = 10; int* p = &a; *p = 5; return 0; } &43jjuiop p &98sd66dd 10 a &43jjuiop 1 2 3 Passo 3: O conteúdo de a é acessado e alterado utilizando o operador de acesso do ponteiro. O conjunto *(nome_do_ponteiro) é o operador de acesso ao conteúdo da variável para qual o ponteiro aponta. Ao utilizarmos este operador, é possível alterar o valor da variável para o ponteiro aponta ou somente acessá-lo. 5
  12. 12. Passagem por referência O exemplo anterior é somente uma demonstração. Utilizar um ponteiro para alterar o valor da variável neste caso não seria necessário, pois todo nosso programa está na main e conseguimos acessar esta variável a qualquer momento. Este fato muda, quando estamos trabalhando com funções. Lembre-se que as variáveis dentro do escopo de uma função, podem ser acessadas somente lá dentro. Portanto, se quisermos alterar o valor de uma variável que está na main dentro de uma função, a única maneira seria fazer esta função retornar o valor e atribuirmos este valor a variável para qual queremos alterar o valor. Vamos ver um exemplo.
  13. 13. Passagem por valor Suponha que queremos alterar o valor de t utilizando uma função e sem utilizar ponteiros. Teríamos que fazer algo do tipo: int main() { int t = 10; t = valor(5); return 0; } int valor(int a) { a = 5; return a; } O que acontece nesse caso é que t tinha 10 quando foi criado. Chamamos então a função valor com o novo valor que queremos para t (5) e atribuímos a chamada desta função para a variável t. Neste caso, quando o programa terminar t terá 5, que é o retorno função valor.
  14. 14. Passagem por valor Lembrando, isto é só um exemplo, sem muita utilidade somente para entendermos os conceitos de passagem por valor e referência. int main() { int t = 10; t = valor(5); return 0; } int valor(int a) { a = 5; return a; } Neste caso o que estamos fazendo, é passando um valor para função (para este exemplo o valor foi 5, mas poderia ter sido qualquer valor de inteiro). Tivemos que atribuir o valor de retorno para t, justamente porque a passagem de parâmetros para a função, foi feita através da passagem por valor. Quando utilizamos esta abordagem, tudo que acontece na função, tem alteração somente dentro dela e quando ela acaba, todos os valores gerados lá são destruídos e o que sobra é somente um retorno.
  15. 15. Passagem por valor Vamos analisar um outro exemplo de passagem por valor. Vamos ver o que acontece quando tentamos alterar o valor de uma varíavel através de uma função que não tem retorno e utilizamos passagem por valor. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 Vamos ver a execução passo a passo.
  16. 16. Passagem por valor Vamos analisar um outro exemplo de passagem por valor. Vamos ver o que acontece quando tentamos alterar o valor de uma varíavel através de uma função que não tem retorno e utilizamos passagem por valor. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 Passo 1 10 t &43jjuiop
  17. 17. Passagem por valor Vamos analisar um outro exemplo de passagem por valor. Vamos ver o que acontece quando tentamos alterar o valor de uma varíavel através de uma função que não tem retorno e utilizamos passagem por valor. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 Passo 2: a execução na main é interrompida e a função é chamada. O valor de t é enviado para a função. 10 t &43jjuiop 10
  18. 18. Passagem por valor Vamos analisar um outro exemplo de passagem por valor. Vamos ver o que acontece quando tentamos alterar o valor de uma varíavel através de uma função que não tem retorno e utilizamos passagem por valor. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 Passo 3: a variável que representa o parâmetro da função é criada. Note que o endereço de a é diferente do endereço de t. 10 t &43jjuiop 10 10 a &456699oo
  19. 19. Passagem por valor Vamos analisar um outro exemplo de passagem por valor. Vamos ver o que acontece quando tentamos alterar o valor de uma varíavel através de uma função que não tem retorno e utilizamos passagem por valor. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 Passo 4: a variável a tem seu valor alterado para 5. 10 t &43jjuiop 10 a &456699oo 5
  20. 20. Passagem por valor Vamos analisar um outro exemplo de passagem por valor. Vamos ver o que acontece quando tentamos alterar o valor de uma varíavel através de uma função que não tem retorno e utilizamos passagem por valor. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 Passo 5: a função termina e a execução na main é recomeçada e o programa se encerra. Neste momento nada que foi feito na função existe mais por que ela acabou. 10 t &43jjuiop
  21. 21. Passagem por valor O que aconteceu com a variável t que estava na main? NADA. Não tivemos sucesso em alterar seu valor desta vez, porque o que passamos para função foi somente o valor de t, que não tem nenhuma referência com a variável. int main() { int t = 10; valor(t); return 0; } void valor(int a) { a = 5; } 1 2 3 4 5 10 t &43jjuiop
  22. 22. Passagem por referência Vamos então agora, tentar utilizar a passagem por referência. Referência para nós é o mesmo que endereço. Vamos então agora, passar o endereço de t para função e não mais seu valor. Vamos alterar as funções para que isto ocorra. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 1 2 3 4 5 Vamos novamente analisar a execução passo a passo.
  23. 23. Passagem por referência Vamos então agora, tentar utilizar a passagem por referência. Referência para nós é o mesmo que endereço. Vamos então agora, passar o endereço de t para função e não mais seu valor. Vamos alterar as funções para que isto ocorra. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 1 2 3 4 5 Passo 1 10 t &43jjuiop
  24. 24. Passagem por referência Vamos então agora, tentar utilizar a passagem por referência. Referência para nós é o mesmo que endereço. Vamos então agora, passar o endereço de t para função e não mais seu valor. Vamos alterar as funções para que isto ocorra. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 1 2 3 4 5 Passo 2 10 t &43jjuiop Passo 2: a execução na main é interrompida e a função é chamada. Agora, a caixinha que está sendo mandada para a função possui o endereço de t e não mais o seu valor. &43jjuiop
  25. 25. Passagem por referência Vamos então agora, tentar utilizar a passagem por referência. Referência para nós é o mesmo que endereço. Vamos então agora, passar o endereço de t para função e não mais seu valor. Vamos alterar as funções para que isto ocorra. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 1 2 3 4 5 10 t &43jjuiop Passo 3: a variável a é criada. Note que ela é um ponteiro e não poderia ser diferente, pois passamos para a função um endereço e relembrando: variáveis que recebem endereços são ponteiros! Desta forma a aponta para t! &43jjuiop a &4558899
  26. 26. Passagem por referência Vamos então agora, tentar utilizar a passagem por referência. Referência para nós é o mesmo que endereço. Vamos então agora, passar o endereço de t para função e não mais seu valor. Vamos alterar as funções para que isto ocorra. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 1 2 3 4 5 10 t &43jjuiop Passo 4: o operador de acesso é utilizado para alterar o conteúdo da variável para qual a aponta. Neste caso então o valor da variável t será alterado de 10 para 5. &43jjuiop a &4558899 5
  27. 27. Passagem por referência Vamos então agora, tentar utilizar a passagem por referência. Referência para nós é o mesmo que endereço. Vamos então agora, passar o endereço de t para função e não mais seu valor. Vamos alterar as funções para que isto ocorra. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 1 2 3 4 5 5 t &43jjuiop Passo 5: a função termina e a execução, todas as suas variáveis são destruídas, a execução na main é continuada e o programa então se encerra.
  28. 28. Passagem por referência O que aconteceu com a variável t? Teve seu valor alterado, mesmo não estando dentro da função. Isso só foi possível, porque passamos uma referência (endereço) de t para a função e quando passamos uma referência, podemos utilizar o operador de acesso para acessar ou alterar o valor da variável onde quer que ela esteja, pois temos um link com a mesma. int main() { int t = 10; valor(&t); return 0; } void valor(int* a) { *a = 5; } 5 t &43jjuiop
  29. 29. Passagem por referência É claro que este foi um exemplo muito simples e parece inútil esta função para nós por agora. Porém, quando entrarmos em TAD, ficará claro como, utilizar a passagem por referência utilizando ponteiros, diminuirá a complexidade de nossas funções e evitará que a todo momento tenhamos que fazer re- atribuições de variáveis, o que pode tornar o código ineficiente e time-costing.

×