21
Uso do ponteiro void * Uso do ponteiro void * Prof. Alex Camargo [email protected] UNIVERSIDADE FEDERAL DO PAMPA CAMPUS BAGÉ LABORATÓRIO DE PROGRAMAÇÃO II

Laboratório de Programação II: Uso do ponteiro void *

Embed Size (px)

Citation preview

Uso do ponteiro void *Uso do ponteiro void *

Prof. Alex [email protected]

UNIVERSIDADE FEDERAL DO PAMPACAMPUS BAGÉ

LABORATÓRIO DE PROGRAMAÇÃO II

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 *

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 *

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 *

Operadores & e *

Laboratório de Programação II – Uso do ponteiro void *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

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 *

Laboratório de Programação II – Uso do ponteiro void *

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 *

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 *