Laboratório de Programação II: Uso do ponteiro void *
1. Uso do ponteiro void *Uso do ponteiro void *
Prof. Alex Camargo
alexcamargoweb@gmail.com
UNIVERSIDADE FEDERAL DO PAMPA
CAMPUS BAGÉ
LABORATÓRIO DE PROGRAMAÇÃO II
2. Introdução
Ponteiros são variáveis cujos valores são endereços de
memória. Uma variável de ponteiro é uma variável especialmente
declarada para guardar um ponteiro para seu tipo especificado.
Ponteiros têm 3 funções principais em C:
fornecer uma maneira rápida de referenciar elementos de um
vetor;
permitem a modificação de argumentos de funções: variáveis
não globais e não locais a ela;
oportunizar o uso de alocação e desalocação de memória em
tempo de execução conforme a necessidade do programa.
Laboratório de Programação II – Uso do ponteiro void *
3. Introdução
A forma de declaração de uma variável ponteiro é:
tipo *nome_variável
Onde tipo é o tipo de variável apontada pela variável ponteiro.
Exemplo:
int *p; //p é um ponteiro de int
Ou seja, p apontará para uma região de memória que armazena
um valor inteiro.
Laboratório de Programação II – Uso do ponteiro void *
4. Operadores & e *
O primeiro operador de ponteiro é o &. Ele é um operador
unário que devolve o endereço na memória de seu
operando.
O segundo operador é o *. Ele é um operador unário que
devolve o valor da variável localizada no endereço que o
segue.
Operadores unários interagem com apenas um elemento (Ex.:
p++ e p--).
Laboratório de Programação II – Uso do ponteiro void *
5. Operadores & e *
Laboratório de Programação II – Uso do ponteiro void *
6. Ponteiros e vetores
Em C, ponteiros e vetores estão intimamente relacionados. O
nome de um vetor sem o índice é um ponteiro para o primeiro
elemento do vetor.
Exemplo:
Laboratório de Programação II – Uso do ponteiro void *
7. Ponteiros e vetores
Sabemos agora que, na verdade, o nome de um vetor é um
ponteiro constante.
Podemos ver que p[2] equivale a *(p+2).
Laboratório de Programação II – Uso do ponteiro void *
8. Ponteiros na passagem de parâmetros
por referência
Neste contexto, os ponteiros são utilizados para permitir que
seja acessada uma região de memória ocupada por uma variável
não "enxergada" pela função, ou seja, por uma variável fora do
escopo da função.
Para isso, a função recebe e armazena em variáveis ponteiro o
endereço das variáveis fora do seu escopo.
Para passarmos uma variável para uma função e fazer com que
ela seja alterada, precisamos passar a referência dessa
variável, em vez de seu valor.
Para fazer isso, basta colocar o operador & antes do argumento
que vamos enviar, e colocar um asterisco * no parâmetro da
função.
Laboratório de Programação II – Uso do ponteiro void *
9. Ponteiros na passagem de parâmetros
por referência
Neste contexto, os ponteiros são utilizados para permitir que
seja acessada uma região de memória ocupada por uma variável
não "enxergada" pela função, ou seja, por uma variável fora do
escopo da função.
Para isso, a função recebe e armazena em variáveis ponteiro o
endereço das variáveis fora do seu escopo.
Para passarmos uma variável para uma função e fazer com que
ela seja alterada, precisamos passar a referência dessa
variável, em vez de seu valor.
Para fazer isso, basta colocar o operador & antes do argumento
que vamos enviar, e colocar um asterisco * no parâmetro da
função.
Laboratório de Programação II – Uso do ponteiro void *
10. Alocação dinâmica de memória
É o meio pelo qual um programa pode obter memória
enquanto está em execução. Ponteiros fornecem o suporte
necessário para o poderoso sistema de alocação dinâmica em C.
Basicamente, utilizamos as seguintes funções (estão no cabeçalho
stdlib.h):
malloc: aloca memória
free: libera memória
Laboratório de Programação II – Uso do ponteiro void *
11. Alocação dinâmica de memória
malloc: aloca memória
(void*) malloc(tamanho em bytes)
Para que possamos determinar qual o tamanho em bytes que
queremos, utilizamos a função sizeof.
O retorno de malloc é um ponteiro genérico e deve ser
configurado para o tipo alocado. No entanto, é comum
fazermos a conversão explicitamente, utilizando o operador de
molde de tipo (cast).
Exemplo:
pt = (int*) malloc (sizeof(int));
Laboratório de Programação II – Uso do ponteiro void *
12. Alocação dinâmica de memória
free: libera memória
free(endereço)
Você é responsável por liberar a memória que você alocou!
Exemplo:
free(pt);
Laboratório de Programação II – Uso do ponteiro void *
13. Alocação dinâmica de memória
Um exemplo de desperdício de memória é quando um vetor de 1000
posições é declarado quando não se sabe, de fato, se as 1000 posições
serão necessárias.
Laboratório de Programação II – Uso do ponteiro void *
14. O tipo void
Antes de falarmos de ponteiros void *, veremos o tipo void. Isso
nos ajudará a entender o comportamento do ponteiro e a
diagnosticar possíveis problemas.
Laboratório de Programação II – Uso do ponteiro void *
15. O tipo void
O tipo void é uma das exceções das regras de tipo. Todos os
tipos básicos que existem servem para armazenar alguma espécie
de dado definido em um tamanho.
char: 1 byte
int: 4 bytes
float: 4 bytes
double: 8 bytes
O tipo void possui tamanho igual a 1 byte, mas não serve para
armazenar dados de qualquer tipo.
Laboratório de Programação II – Uso do ponteiro void *
16. O tipo void
Assim, o tipo void é usado quando não queremos dados
armazenados nele. Esse é um conceito fácil de se entender, pois
em funções que não retornam nada devemos colocar void.
Exemplo:
Laboratório de Programação II – Uso do ponteiro void *
17. void *
Chamamos de ponteiros vazios qualquer ponteiro que não
possua um tipo de dado específico, ou seja, se seu tipo de dado for
void. Ponteiros vazios são ponteiros genéricos que podem
apontar para qualquer outro tipo de dado, mas trazem consigo
problemas lógicos:
como são ponteiros vazios, não há um tipo de dado específico;
não podemos determinar seu tamanho;
assim como também não podemos dereferenciá-lo
diretamente.
Esses problemas são facilmente resolvidos se induzirmos o endereço
void para o endereço do tipo de dado correto, da mesma forma que
executamos qualquer typecast.
Laboratório de Programação II – Uso do ponteiro void *
18. void *
Entender o conceito de ponteiros genéricos é de suma
importância:
para a compreensão de estruturas dinâmicas de memória;
trabalharemos o tempo todo com funções que retornam
endereços de memória genéricos;
ou seja, podem ser qualquer tipo de endereço de memória,
portanto sem tipo de dado (void).
Laboratório de Programação II – Uso do ponteiro void *
20. Considerações finais
Um ponteiro genérico é um ponteiro que pode apontar para
qualquer tipo de dado:
O tipo de dado apontado por um ponteiro genérico deve ser
controlado pelo usuário, usando um type cast (conversão de
tipo).
Não podemos armazenar nada em tipo void.
Só se pode armazenar endereços de memória em void*.
Dentre as três regras, as duas primeiras já deveríamos estar
acostumados. A "nova" (a última regra) é o que garante generabilidade
para o tipo void.
Laboratório de Programação II – Uso do ponteiro void *
21. Exercícios
Responda as questões referentes ao uso do ponteiro void * contidas na
Lista de Exercícios 1 do Moodle, para isso utilize a Linguagem C.
Laboratório de Programação II – Uso do ponteiro void *