- 1 Discussão
-
C/Faq/Ponteiros
< C | Faq
- Este artigo faz parte do FAQ para a linguagem C. Para o índice do FAQ, confira C/Faq. Para a linguagem em si, confira C.
Antes de acrescentar dúvidas a este FAQ, confira a Política de FAQ!
Tabela de conteúdo |
O que são ponteiros?
Editar
Ponteiros são tipos de variáveis destinadas a armazenar endereços de memória. Na linguagem C, ponteiros são normalmente associados a um tipo do dado nativo ou definido pelo usuário, que corresponde ao tipo do dado armazenado na posição de memória para a qual o ponteiro efetivamente aponta.
Abaixo há alguns exemplos de declarações de ponteiros.
int *p_i; /* p_i é uma variável que provavelmente armazenará o endereço de um dado do tipo "int". */
struct meu_tipo_de_dado_composto *p_mtdc;
/* p_mtdc é uma variável que provavelmente armazenará o endereço de um dado do tipo
"struct meu_tipo_de_dado_composto". */
int **p_p_i; /* p_p_i é uma variável que provavelmente armazenará o endereço de um dado do tipo
"ponteiro que armazena o endereço de um dado do tipo int". */
char (*p_a_c)[5];
/* p_a_c é uma variável que provavelmente armazenará o endereço de um dado do tipo
"array de char com cinco posições. */
char *a_p_c[5];
/* a_p_c é um array de cinco posições de ponteiros que possivelmente terão os endereços
de dados do tipo "char". */
void *p; /* p é uma variável que armazena o endereço de qualquer tipo de dados. */
Para que servem ponteiros?
Editar
Ponteiros são úteis para diminuir a necessidade de copiar dados de um lugar para outro, em parte por sua capacidade de se tornarem lvalues do tipo do dado para o qual apontam.
Imagine, por exemplo, o caso de uma variável X que receba uma valor inicial dentro de uma função func1(). Suponha que, após ser utilizada por algum tempo nessa função, decida-se usar uma função func2(), que usa o valor atual como base de processamento e retorna um resultado, que será gravado novamente em X, já que o valor antigo não será mais necessário e não se quer desperdiçar memória com a criação de diversas variáveis temporárias do mesmo tipo. O código abaixo seria uma possível implementação de algo deste tipo em C (supondo que o tipo de X seja int).
int func2(int);
void func1(void){
int X = 0;
/* Usa X por algum tempo, talvez até alterando seu valor. */
X = func2(X); /* Usa o valor atual de X como base de cálculo de func2(), e grava o resultado em X. */
/* Usa o novo valor de X */
}
int func2(int valor_inicial){
int result;
/* Realiza alguma computação baseada em valor_inicial e salva o resultado em result.
No nosso exemplo, a operação será apenas calcular o dobro do valor. */
result = 2 * valor_inicial;
return result;
}
Quando o código acima é compilado, a linha que contém o comando X=func2(X); produz a seguinte seqüência de operações de máquina:
- copia o valor atual de
Xpara uma área onde são guardados os parâmetros que serão passados à funçãofunc2(); - chama
func2(); - copia o valor armazenado na área de resultados produzidos pela função
func2() para a variávelX.
Uma vez que X é do tipo int, as operações de cópia antes da chamada de func2() e após tal função ter retornado não parecem um grande problema.
Imagine, porém, um problema semelhante, não com uma variável do tipo int, mas com uma estrutura complexa e que ocupasse vários bytes na memória. Se fosse utilizado o mesmo algoritmo, as operações de cópia que ocorrem antes e depois da chamada a func2() certamente teriam mais impacto sobre o desempenho do programa.
Uma forma de evitar a cópia de dados de um lado para outro é usar apenas variáveis globais, mas isso acabaria com a estruturação e modularidade do programa. Outra forma, mais simples e elegante, é passar apenas o endereço de memória onde se encontra o dado a ser manipulado, a fim de que a função use esse endereço para acessar indiretamente tal dado.
O código acima poderia ser reescrito usando ponteiros da forma como mostrado abaixo.
void func2(int *);
void func1(void){
int X = 0;
/* Usa X por algum tempo, talvez até alterando seu valor. */
func2(&X); /* Envia o endereço de X a func2(), que poderá usá-lo para ler e alterar seu valor. */
/* Usa o novo valor de X */
}
void func2(int *p_valor){
/* Realiza alguma computação baseada no valor inicial, que pode ser obtido a partir do endereço
fornecido, e salva o resultado na mesma região de memória. No nosso exemplo, essa computação
será apenas calcular o dobro do valor inicial. */
*p_valor = *p_valor * 2;
}
Ponteiros são elementos de baixo nível ou de alto nível?
Editar
Em C, ponteiros são um dos elementos de mais baixo nível da linguagem, pois geralmente armazenam diretamente o endereço de memória onde se encontram dados. Isso é útil para muitas coisas -- inclusive a maioria das que fazem com que C seja útil para o desenvolvimento de sistemas operacionais, por exemplo --, mas requer a devida dose de cuidado.
É verdade que ponteiros são uma fonte comum de problemas em programas escritos em C? Se é, qual o motivo?
Editar
Sim, muitos dos bugs que acometem programas em C são devidos a maus usos de ponteiros.
Freqüentemente tais problemas são causados por um ou mais dos seguinte motivos:
- entendimento inadequado sobre o que são ponteiros;
- entendimento inadequado sobre operações com ponteiros;
- tentar de-referenciar ponteiros nulos;
- tentar de-referenciar ponteiros não inicializados;
- misturar ponteiros para tipos diferentes, de modo que, quando de-referenciados, os dados obtidos não sejam consistentes;
- usar ponteiros para alocar blocos de memória e esquecer de liberar a memória apontada por esses ponteiros quando tais blocos não são mais necessários;
- usar ponteiros para alocar blocos de memória e liberar tais blocos, através de seus ponteiros, mais de uma vez;
- tratar como relativos a alocação dinâmica de memória ponteiros que apontam para outras partes da memória.
void e uma funçao que nao nao retorna nehuma valor exemplo de funcao que retorna um valor inteiro(int): int soma(int x,int y) {
return x+y;
} a funçao soma retorna a soma de x + y agora um exemplo de funçao que nao retorna nenhum valor ou seja void
void olamundo {
puts("ola mundo");
} esta funçao nao retornou nenhum valor apenas executou o procedimento dentro dela
Qual é o problema com este código...? (return &p)
Editar
int *ponteiro(int n)
{
int p = n;
return &p;
}
A variável p é um objeto de escopo função. Funções de escopo de função deixam de existir quando a execução da função finaliza. Assim, o retorno de ponteiro() será um endereço de memória inválido.
Retornar o endereço de uma variável local é "quase sempre" um erro.