Modificadores de Acesso na Linguagem C

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

Olá, caro leitor! Já olhou um código C e viu as palavras const e volatile? Este artigo descreverá a função dessas palavras na linguagem C, apresentando alguns exemplos e situações onde devem ser utilizadas.

Os modificadores de acesso da linguagem C

A linguagem C permite que os tipos de dados sejam utilizados com modificadores precedendo-os. Por exemplo, uma variável int pode ser especificada como signed ou unsigned. Além dos modificadores de tipo, a linguagem C possibilita o uso de modificadores de acesso. Os modificadores de acesso determinam o modo como as variáveis podem ser acessadas e, além disso, especificam se a variável pode ser alterada ou não.

Os modificadores de acesso são descritos abaixo:

  • const: utilizado para declarar uma constante, isto é, uma variável que não pode ser modificada durante a execução do programa. Tais variáveis podem ser armazenadas em uma memória de apenas leitura;
  • volatile: utilizado para indicar ao compilador que a variável especificada pode ser alterada por eventos externos, isto é, o conteúdo da variável pode ser alterado de forma não explícita. Por exemplo, uma rotina de interrupção pode alterar o valor de uma variável que é utilizada em outro procedimento.

Esses modificadores devem preceder os modificadores de tipo e o nome que eles modificam [1], contudo não é um regra.

O modificador const

Para criar uma constante o modificador const é colocado antes do especificador de tipo da variável. Por exemplo, a declaração mostrada abaixo define uma variável que o programa não pode modificar, portanto a definição do seu valor deve ser realizada junto com a declaração.

Cabe ressaltar que qualquer tentativa de atribuição causará erro de compilação:

error: assignment of read-only variable ‘Referencia’

Esse modificador também pode ser utilizado nos parâmetros de funções, prevenindo que os parâmetros sejam modificados. Adiante veremos que isso é importante quando são utilizados ponteiros.

Ponteiros e o modificador const

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.

Quando os ponteiros são declarados como parâmetros de funções, os modificadores podem ser utilizados para proteger os dados apontados assim como o endereço informado. Por exemplo, uma função para calcular a média de valores de um vetor não necessita alterar os elementos.

Nesse exemplo o ponteiro também pode ser constante.

O modificador volatile

Como dito anteriormente, o modificador volatile indica para o compilador que a variável pode ser modificada de uma forma não explícita. Em outros casos, esse modificador previne que otimizações realizadas pelo compilador gerem um resultado inesperado.

Para exemplificar, considere os casos mostrados abaixo de um código compilado para o microcontrolador Atmega8:

Quando o programa mostrado acima é compilado sem otimização (-O0), pode-se verificar que a operação de incremento (SUBI R24, 0xFF) é realizada e o valor resultante é armazenado na variável novamente, isto é, a variável é referenciada a cada operação (o valor não é mantido em registrador). Esse caso é mostrado na Figura 1.

Modificadores de Acesso na Linguagem C: Otimização -O0
Figura 1: Otimização -O0.

Na Figura 2 é mostrado o resultado da compilação com otimização O1. Note que o compilador otimizou o código de forma que as operações foram removidas, pois a variável não era utilizada em outra expressão.

Otimização -O1.
Figura 2: Otimização -O1.

Adicionando o modificador volatile o código compilado apresenta o mesmo comportamento mostrado na primeira situação. Esse caso é mostrado na Figura 3.

Variável volatile e Otimização -O1.
Figura 3: Variável volatile e Otimização -O1.

No contexto de interrupções o mesmo ocorre.

O código acima, quando compilado sem otimizações, sempre faz a leitura da variável. A Figura 4 mostra esse caso, onde bloco preto destaca a região da função main, já o vermelho o contexto da interrupção. Note que em ambos a variável que está no endereço 0x0060 é acessada diretamente.

Interrupção e Otimização -O0.
Figura 4: Interrupção e Otimização -O0.

Compilando o mesmo código com otimização O1 obtém-se um resultado diferente no contexto da main. Na Figura 5 é possível observar que o valor da variável utilizado na main é copiado para um registrador (R24) antes de iniciar o loop. Cabe ressaltar que após a atribuição do valor no registrador, o valor real da variável não é lido.

Interrupção e Otimização -O1.
Figura 5: Interrupção e Otimização -O1.

Portanto, a interrupção altera o valor da variável e o procedimento na main não tem conhecimento. Alterando a variável global para volatile, a comparação será sempre realizada com o valor real da variável. Isso pode ser observado na Figura 6.

Interrupção, variável volatile e Otimização -O1.
Figura 6: Interrupção, variável volatile e Otimização -O1.

Utilizando modificadores em conjunto

Os modificadores const e volatile podem ser usados na mesma declaração. Considere, por exemplo, o acesso a um determinado registrador de periférico.

Essa declaração define um ponteiro constante para um valor uint8_t. O modificador volatile determina que o acesso ao registrador sempre será realizado, evitando problemas com otimizações do compilador. Com a Figura 7, note que o código acima gera a mesma operação que a definição de PORTB na biblioteca de IO.

PortMap
Figura 7: Mapeamento de memória.

Conclusão

Este artigo apresentou os conceitos de modificadores de acesso na linguagem C. Esses modificadores podem ser utilizados para aumentar a segurança e evitar efeitos indesejáveis que podem ocorrer quando o código é otimizado pelo compilador. Outro tópico brevemente apresentado foi o de mapeamento de memória, o qual faz uso dos ponteiros e modificadores de acesso para realizar operações em registradores.

Referências

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

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

Veja + conteúdo

Fascinado por computação, especialmente na interface entre hardware e software, me engajei na área de sistemas embarcados. Atuo com desenvolvimento de sistemas embarcados e sou docente da Faculdade de Engenharia de Sorocaba.

Para mais informações: https://about.me/fdelunogarcia

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.

Comentários:
Notificações
Notificar
guest
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
PaulinoJr.c Costa
PaulinoJr.c
15/03/2019 16:43

int AjustarValor(const int atual)
{
/*…*/

atual = atual – OFFSET; //causará erro de compilação

/*…*/
}

Não faz sentido proteger uma cópia. já que ela vai ser destruída após o escopo final }

Talvez você goste:

Séries

Menu