Ponteiro em C: Definição

funções X macros compilação condicional Diagnóstico

Olá, caro leitor! Este artigo tem o intuito de detalhar um recurso muito importante da linguagem C, o ponteiro! Veremos que a definição geral é simples de compreender, porém a utilização incorreta deste recurso pode ocasionar problemas muito graves para uma aplicação.

Afinal, o que é um ponteiro? 

Em poucas palavras a definição de um ponteiro é bem simples: um ponteiro é uma variável que contém um endereço de memória [1]. Esta definição é simples, mas o entendimento correto acerca da utilização deste recurso começa pela compreensão de como a memória de um programa é organizada, ou ainda, o que é um endereço de memória.

Memória

Grosso modo, uma memória pode ser definida como um conjunto de elementos que armazenam informação. Esses elementos são chamados de palavras, sendo cada palavra identificada de maneira unívoca a partir de um endereço, isto é, um endereço de memória! Uma característica da palavra é a sua capacidade de armazenar informação, isto é, a quantidade de bytes que a palavra representa. A memória é ilustrada na Figura 1.

Ponteiro em C: Memória
Figura 1: Representação da memória.

Agora sabemos que uma memória é composta por palavras e que toda palavra possui um endereço único, assim sendo, é importante saber que um programa é um conjunto de informações armazenadas na memória, logo o próximo passo é entender como um programa é organizado na memória.

Organização de um programa na memória

Este tópico é importante para sabermos o que acontece quando um programa em C é compilado. Um programa é um conjunto de informações armazenadas na memória, desta maneira podemos dividir um programa em duas categorias: instruções e dados. As instruções são as operações (o programa propriamente dito) realizadas pela máquina, já os dados são as variáveis, ou valores, processados nessas operações.

Embora o programa seja composto por instruções e dados, tem-se, conceitualmente, quatro regiões de memória [1]. Abaixo segue uma breve descrição de cada segmento:

  • Stack – A Stack (pilha) é uma região dinâmica, isto é, varia conforme a execução do programa. É utilizada para armazenar o endereço nas chamadas de funções e interrupções, passagem de parâmetros para funções, armazenar variáveis locais ou pode ser manipulada para armazenar dados em determinada operação;
  • Heap – A região Heap também é dinâmica, porém difere da pilha. Essa região é considerada livre e é utilizada por mecanismos de alocação dinâmica de memória;
  • Dados – A região de dados corresponde à área onde as variáveis globais e estáticas são armazenadas;
  • Programa – Essa região armazena as instruções do programa.

Variáveis e endereço de variáveis

Vimos que tudo que é posto em memória possui um endereço e que a definição de um ponteiro é: uma variável que contém um endereço de memória. Deste modo, se um ponteiro armazena o endereço de outra variável, então temos a relação de que uma variável aponta para outra.

Ponteiro que armazena o endereço da variável X
Figura 2: Ponteiro que armazena o endereço da variável X.

Na Figura 2, é ilustrado uma memória sendo que um dos seus elementos é um ponteiro e outro é uma variável comum. O conteúdo do ponteiro é um endereço, portanto a seta indica a relação descrita anteriormente. Adiante veremos que utilizando ponteiros podemos realizar o acesso indireto a outras variáveis, isto é, podemos ler ou alterar o conteúdo de uma variável sem utilizar o nome desta variável.

Declaração de ponteiro

Para declarar uma variável como um ponteiro deve-se utilizar o símbolo ‘*’ entre o tipo e o nome da variável. A forma geral da declaração é:

Tipo_da_variável * Nome_da_Variável;

Uma vez que o ponteiro realiza o acesso indireto a uma variável, devemos ter o mesmo tipo de ponteiro e variável apontada. Esta relação não é uma regra, pois um ponteiro pode armazenar um endereço independente do tipo de variável armazenada no endereço especificado, contudo veremos no próximo artigo que o tipo do ponteiro é importante para o que é chamado de aritmética de ponteiros.

Operadores

Ao trabalhar com ponteiros fazemos o uso de dois operadores, são eles:

  • o operador de indireção ‘*’;
  • o operador unário ‘&’ para obter o endereço de uma variável (também chamado de ponteiro constante, pois representa um endereço de memória fixo).
 

Para exemplificar, considere o trecho de código abaixo. Temos a definição de uma variável inteira e de um ponteiro para o tipo de dados inteiro.

A Figura 3 ilustra a indireção simples mostrada no código acima.

Ponteiro em C: Indireção simples
Figura 3: Indireção simples.

O conteúdo do ponteiro foi determinado na quarta linha, logo o mesmo armazena o endereço da variável varX. Para acessar o conteúdo do endereço armazenado no ponteiro basta utilizar o operador ‘*’ antes do nome do ponteiro.

Considere o caso abaixo em que o valor da variável varX é alterado a partir do ponteiro. Essa situação é ilustrada na Figura 4.

Observe que o conteúdo de varX foi alterado de forma indireta, isto é, sem fazer referência ao nome da variável varX. Cabe ressaltar que esse ponteiro é chamado de ponteiro variável, pois possibilita armazenar qualquer endereço.

Alteração do conteúdo a partir do ponteiro
Figura 4: Alteração do conteúdo a partir do ponteiro.

O conceito de NULL

Ao declarar uma variável sem atribuir um valor direto, o valor armazenado nela vai depender do contexto onde a variável foi declarada, por exemplo, uma variável local de uma função. A não inicialização de um ponteiro e posteriormente a sua utilização podem trazer sérias consequências para execução do programa, pois ao alterar o seu conteúdo podemos modificar regiões de memória importantes para o controle de execução, por exemplo, o endereço de retorno de uma função armazenado na Stack. Para evitar problemas é possível utilizar um valor que determina se um ponteiro não foi inicializado com um endereço válido, esse valor é o NULL.

Geralmente, o valor NULL é definido como:

Assim, é possível inicializar um ponteiro como NULL e antes de usa-lo pode ser realizado um teste condicional. Por exemplo, a função de alocação dinâmica de memória malloc retorna um ponteiro para o bloco de memória alocado, caso contrário o valor retornado é NULL.

O conceito de atribuir NULL ao ponteiro é simples, contudo não deve ser compreendido como um ponteiro que aponta para o endereço zero. O NULL tem como função determinar que o ponteiro não aponta para um endereço válido. De fato, isso não impede erros de execução, porém fornece uma alternativa para testar se o ponteiro foi inicializado.

Modificadores de Acesso

Assim como qualquer variável os ponteiros também podem ser declarados utilizando modificadores de acesso.

Declaração sem modificadores: O conteúdo de ‘var’ pode ser alterado utilizando o ponteiro e o ponteiro também pode ser alterado.

Ponteiro para um valor constante: O conteúdo de ‘var’ não pode ser alterado utilizando o ponteiro, no entanto o ponteiro pode ser alterado.

Ponteiro constante: O conteúdo de ‘var’ pode ser alterado utilizando o ponteiro, porém o ponteiro não pode ser alterado.

Ponteiro constante para um valor constante: O conteúdo de ‘var’ não pode ser alterado pelo ponteiro e o ponteiro também não pode ser alterado.

Indireção Múltipla

O endereço do ponteiro pode ser obtido da seguinte forma:

Desse modo, um ponteiro pode armazenar o endereço de outro ponteiro, ocasionando uma indireção múltipla. Para declarar um ponteiro de indireção múltipla utiliza-se N vezes o operador *, sendo N o nível de indireção. No exemplo abaixo é declarado um ponteiro para ponteiro.

Do código acima:

  • &ptr2: Endereço do ponteiro ‘ptr2’;
  • &ptr1: Endereço do ponteiro ‘ptr1’;
  • ptr2: Conteúdo de ‘ptr2’, ou seja, o endereço de ‘ptr1’.
  • ptr1: Conteúdo de ‘ptr1’, ou seja, o endereço de ‘var’.
  • *ptr2: Conteúdo do endereço apontado, ou seja, o conteúdo de ‘prt1’.
  • *ptr1: Conteúdo do endereço apontado, ou seja, o conteúdo de ‘var’.
  • **ptr2: Acessa o conteúdo do endereço armazenado no ponteiro que é referenciado por ‘ptr2’, ou seja, acessa ‘var’ indiretamente.

A Figura 5 ilustra as operações listadas acima.

Ponteiro4
Figura 5: Indireção Múltipla

Conclusão

Nesse artigo foi apresentada a definição geral de ponteiro e algumas características desse recurso poderoso. A capacidade de armazenar endereços e poder manipular, de forma indireta, o conteúdo de variáveis, permite ao desenvolvedor modelar aplicações C com bastante flexibilidade. Cabe ressaltar, novamente, que o uso incorreto pode gerar graves problemas para aplicação.

Com essa introdução outros pontos podem ser explorados: alocação dinâmica de memória, acesso de membros de estruturas, ponteiros para funções, passagem de parâmetros por referência e técnicas de orientação ao objeto. Esses temas serão explorados em outros artigos.

Referências

[1] – Livro: C, completo e total – 3ª edição revista e atualizada. Herbert Schildt.

[2] – Livro: Sistemas Digitais – Princípios e Aplicações – 8ª edição. Ronald J. Tocci e Neal S. Widmer.

Fonte da imagem destacada: http://listamaze.com/top-10-programming-languages-for-job-security/

Outros artigos da série

<< Ponteiro em C: ResumoPonteiro em C: Aritmética de ponteiro >>
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Software » Ponteiro em C: Definição
Comentários:
2 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Frederico Fernandes Oliveira
Frederico Fernandes Oliveira
19/02/2016 00:26

Parabéns pelo artigo Fernando, muito bom e educativo. Gostaria apenas de apontar um erro na seção ‘Modificadores de Acesso’ onde no segundo exemplo
int var = 10;
int const * ptr = &var;
você escreve: ‘O conteúdo de ‘var’ não pode ser alterado utilizando o ponteiro e o ponteiro não pode ser alterado.’ Enquanto na verdade o ponteiro pode sim ser alterado. Os exemplos onde ele não pode ser alterado estão corretamente descritos nos dois exemplos seguintes.

Fernando Deluno Garcia
Fernando Deluno Garcia
Reply to  Frederico Fernandes Oliveira
19/02/2016 16:55

Olá, Frederico.

Olhos de águia! Obrigado pelo retorno.

Talvez você goste:

Séries



Outros da Série

Menu

WEBINAR
 

BlueNRG-LP – Bluetooth 5.2 de longo alcance para aplicações industriais

Data: 05/11 às 15:00h - Apoio: STMicroelectronics
 
INSCREVA-SE AGORA »



 
close-link