Programar em C++/Variáveis e constantes
1 Constantes
Compatível com a linguagem C, o C++ mantém as constantes básicas e introduz algumas novas funcionalidades possibilitadas pelo modificador const.
O uso do modificador const tem duas funções principais:
1. Resguarda da inviolabilidade de valores apontados
por ponteiros;
2. Auxílio na compreensão das características de funções,
durante a implementação.
1.1 Simbólicas
Constantes simbólicas podem ser criadas com as diretivas
do preprocessador #define. Neste modo os valores, de
fato, não são interpretados imediatamente pelo compilador,
antes são identificados e substituidos pelo preprocessador
no estágio anterior à compilação. Por exemplo:
#define BUFFER_LENGTH 2048 ... ... ... char
data[BUFFER_LENGTH];
Observe que o valor 2048 será usado logo abaixo no código,
depois que o preprocessador substituir a constante
simbólica BUFFER_LENGTH pelo valor que lhe foi atribuído.
Note que as constantes são escritas com todas as letras
maiúsculas, isso não é uma regra, mas ajuda a identificar
o que é constante simbólica dentro do programa, sendo
adotado pela maioria dos desenvolvedores como uma boa
prática de programação.
Neste caso, podemos definir valores simbólicos compostos,
por exemplo:
#define BUFFER_LENGTH 2048 #define
N_BUFFERS 100 #define MASTER_LENGTH (
BUFFER_LENGTH * N_BUFFERS ) ... ... ... char
screen[MASTER_LENGTH];
Os valores podem ser simbólicos em formato de código,
o que permite criar programas com melhor legibilidade.
Para isso podemos colocar expressões com funcionalidades
bem definidas substituídas por nomes que as identifiquem.
Por exemplo:
float a[3]; #define PRINT_VECTOR cout << a[0] << " , "
<< a[1] << " , " << a[2] << endl ... ... PRINT_VECTOR;
1
Desta forma, todas as vezes que quisermos mostrar o valor
do vetor de três coordenadas podemos usar a constante
PRINT_VECTOR.
1.2 Literais
Constantes literais podem ser declaradas da mesma forma
que na linguagem “C”, ou seja, podemos definir valores fixos
em qualquer parte do programa, expressando-os diretamente
no código através de seu valor significativo. Por
exemplo, podemos definir números:
256 //decimal 0400 //octal 0x100 //hexadecimal
Também podemos definir valores para caracteres ou cadeias
de caracteres, como segue:
'a' // um caractere “abc” // uma cadeia de caracteres
"\xF3\x23\x12” // uma cadeia de caracteres representada
por seus valores em hexadecimal
Temos ainda a possibilidade de declarar constantes compostas
por valores e operadores:
(4.23e14 * (12.75 + 12976.18/36)) // constante composta
1.3 Enumerações
Valores enumerados são muito recorrentes nos ambientes
de programação, por isso podemos contar com a declaração
de enum em C++ também, o que segue a mesma
sintaxe que temos em “C":
enum seq {A,B,C,D}; seq x;
ou ainda:
enum nomes {LANY=100,SANDRA=200,MARCIA=300,RODRIGO=400};
nomes x;
Porém, observamos uma diferença: a palavra enum pode
ser dispensada na declaração da variável, enquanto que
em C é obrigatório,apesar desta pequena diferença a funcionalidade
do recurso é a mesma, ou seja, pode-se definir
variáveis que assumem estritamente os valores presentes
na declaração de enumeração.
Este recurso torna-se útil na padronização de valores a se-
2 2 VARIÁVEIS
rem usados como entrada de funções, por exemplo. Pode
ser considerada como uma funcionalidade mnemônica,
seu uso não altera o código final caso modifiquemos o
programa para que use variáveis inteiras ou strings de
mesmo valor do enum.
A seguinte sintaxe:
seq x = 3;
Não é permitida, mesmo que o valor presente no enum
seja avaliado como 3 pelo compilador em tempo de compilação.
Isso pode parecer confuso, mas lembre-se de
que os valores serão atribuidos pelo compilador, logo isso
evita que o mesmo programa seja compilado em ambientes
diferentes e tenha comportamento diferente.
2 Variáveis
As variáveis no C++ podem ser usadas da mesma forma
que na linguagem “C”, porém algumas poucas diferenças
podem ser destacadas, principalmente aquelas que trazem
à linguagem C++ características próprias da orientação a
objetos.
2.1 Tipos
Como na linguagem “C”, os tipos nativos do compilador
em uso são referenciados por:
char int float double
Que correspondem a números com tamanho relativos,
com os significados respectivos: caractere, inteiro, ponto
flutuante e ponto flutuante de dupla precisão. De qualquer
forma a extensão dos mesmos depende da máquina
que se pretende programar. Considerando que nem sempre
teremos que programar apenas computadores, poderemos
ter extensões bem distintas dependendo do hardware
a ser programado, por exemplo, computadores domésticos
tipicamente tem processadores de 32 ou 64 bits
hoje em dia, enquanto que dispositivos embarcados podem
ter processadores de 8, 16 ou 32 bits. Portanto, o
compilador para cada caso atribui faixas diferentes para
cada tipo em cada situação.
A linguagem C++ introduz o tipo bool, que representa o
valor booleano, falso ou verdadeiro, o que não existe na
linguagem “C”, porém seu tamanho na memória depende
da capacidade de otimização do compilador usado. Tipicamente
os compiladores para computadores usam uma
variável do tamanho de char para representar o valor, o
que poderia ser considerado um desperdício, mas devido
à abundância de memória não chega a ser inadequado.
Porém em sistemas pequenos há compiladores que armazenam
o valor booleano em apenas um bit. Obviamente,
se o processador possuir recursos de manipulação
de bits isso é muito útil e pode ser usado como um fator
de melhoria da qualidade do software desenvolvido. Em
outros ambientes, onde a manipulação de bits traga prejuízo
para o desempenho usa-se a estratégia padrão de
desperdiçar um pouco de espaço em favor de uma agilidade
maior nas operações. Portanto, embora as variações
de utilização do espaço sejam muitas, o compilador sempre
fará a mais apropriada para cada ambiente de utilização
da linguagem.
2.2 Modificadores
O C++ conta com os modificadores de amplitude
(short,long) presentes na linguagem “C” e modificadores
de acesso, alguns exclusivos do C++, que estão diretamente
ligados a características da POO (programação
orientada a objetos). Desta forma descreveremos apenas
os tipos relevantes exclusivamente para a programação
na linguagem escopo do livro presente sem nos aprofundarmos
na teoria por trás dos mesmos. A prática do uso
dos mesmos é melhor indicada como meio de aprofundamento
do tema.
Assim contamos com os modificadores da linguagem
“C":
static short long unsigned signed
2.2.1 const
A linguagem C++ introduz um novo modificador chamado
const, que tem comportamento variado dependendo
do local onde está sendo declarado. Sua função,
basicamente, é estabelecer um vínculo entre declaração
e obrigatoriedade da coerência no uso do símbolo declarado.
A princípio, quando declaramos uma constante com este
modificador fazemos com que seja obrigatório o uso do
símbolo de forma que o mesmo não possa ter seu valor
alterado. Assim, se fizermos:
const int x = 4;
O inteiro x não poderá deixar de ter valor igual a 4. Qualquer
tentativa de modificar o valor da constante ao longo
do programa será reportada como erro pelo compilador.
Porém podemos considerar esta funcionalidade como óbvia
e trivial, ainda temos o uso do modificador de uma
forma mais proveitosa, na passagem de parâmetros para
funções, por exemplo:
void inhibitX(const int *x) { ... ... BASEADDRESS =
z*((*x) - 23p*71); ... ... }
Neste caso, a função acima recebe um valor inteiro através
de um ponteiro, que não obrigatoriamente precisa
ser constante no escopo fora da função, porém dentro da
2.3 Nomeando tipos 3
mesma a variável será constante. Fazendo este simples
procedimento teremos como fazer com que um símbolo
seja variável fora da função e constante dentro da mesma,
de forma que dentro do escopo da mesma só façamos leituras
do seu valor. O artifício cria duas consequëncias
importantes: a primeira é a melhor legibilidade do código,
visto que ao usarmos uma função teremos certeza
de que os valores não serão alterados dentro da função;
a segunda é que poderemos evitar erros inadvertidos de
atribuição de valores à variável quando da construção da
função.
2.2.2 volatile
Uma variável "volátil", como a própria expressão sugere,
é uma variável que pode ser modificada sem o conhecimento
do programa principal, mesmo que esta ainda
esteja declarada dentro do escopo onde o programa está
sendo executado. Isso está relacionado, principalmente a
processos concorrentes e “threads”, estes podem alterar o
conteúdo da variável em eventos fora da previsibilidade
do tempo de compilação. Em outras palavras, o compilador
não pode prever com segurança se pode otimizar
trechos de programa onde esta variável se encontra.
A palavra reservada volatile é destinada as situações onde
uma variável pode ter seu valor alterado por fatores diversos,
e portanto, não pode ser otimizada. Usando-a o
programador informa ao compilador que não deve interferir
na forma com que o programa foi escrito para acesso
a esta variável. Desta forma impede que erros inseridos
por otimização estejam presentes na versão final do executável.
O uso desta palavra implica em mudança no comportamento
do compilador durante a interpretação do código.
As classes de objetos do tipo voláteis só poderão ser acessadas
por rotinas que declarem aceitar como entrada dados
voláteis, da mesma forma que apenas objetos voláteis
podem acessar variáveis voláteis. Esta “amarração” faz
com que o uso de tais variáveis se torne mais seguro.
Podemos declarar variáveis voláteis da seguinte forma:
volatile int x;
Enquanto que para funções que acessam tais variáveis teremos
consequências visíveis na montagem do código,
por exemplo, se tivermos o seguinte trecho de programa:
int x = 1265; void main_loop() { while( x == 1265) { //
fazer alguma coisa } }
Poderemos ter uma otimização gerada pelo compilador
como segue:
int x = 1265; void main_loop_optimized() { while( true
) { // fazer alguma coisa } }
Considerando que em um programa que foi desenhado
para ambiente multitarefa isso não pode ser considerado
verdadeiro, pois o programa pode estar esperando que
uma das tarefas modifique o estado da variável para prosseguir
seu curso, a otimização acima será um desastre,
uma vez que a função acima jamais será encerrada.
Para evitar isso fazemos:
volatile int x = 1265; void main_loop() { while( x ==
1265) { // fazer alguma coisa } }
E o compilador não poderá mais avaliar que o valor de
x pode ser otimizado para o valor corrente, pois informamos
na declaração que o valor da variável pode ser
alterado sem seu conhecimento. Desta forma o mesmo
não alterará o algorítmo e fará o teste da variável dentro
do while.
2.3 Nomeando tipos
A linguagem “C” possui recursos de nomeação de tipos
simples e compostos através das palavras chaves typedef
e struct. Adicionada a estas o C++ acrescenta a palavra
chave class. Vejamos como devemos definir um novo
tipo através desta palavra chave.
A palavra class atribui a um conjunto de tipos de dados o
estado de modelo de objeto. Este conceito é fundamental
para o modo avançado de programar usando o C++. Com
este identificador declaramos objetos, da mesma forma
que declaramos estruturas.
Uma classe pode ser definida em um cabeçalho “header”,
da seguinte forma:
class nome_da_classe { <tipo_1> variavel_1; <tipo_2>
variavel_2; . . . <tipo_n> variavel_n; ----- <tipo_n>
nome_funcao ( <tipo_1> variavel_1, <tipo_2> variavel_2,
<tipo_3> variavel_3 ...); };
O mais interessante de observar é a presença de uma função
dentro da declaração acima. Então poderíamos perguntar:
Por que colocar uma função dentro de um tipo?
A resposta é simples: Para manipular os dados dentro do
tipo! Não apenas por esta característica, mas por várias
outras que aboradaremos nos capítulos seguintes, o tipo
class é extremamente flexível e poderoso.
É importante ressaltar que em C++ a declaração do
identificador: enum, struct, class, etc... é dispensado
quando se declara uma variável ou objeto para o referido
tipo. Desta forma podemos ter também as seguintes declarações
como válidas, além do uso padrão da linguagem
“C":
struct data{ int a; int b; int c; }; class object{ int a; char
b; long w; float p; void getData(); }; ... ... ... ... void
func() { data x; object y; ... ... y.getData(); }
Como podemos ver na função acima se a variável x for de-
4 2 VARIÁVEIS
clarada para uma estrutura data o uso da palavra struct
não é obrigatório, assim como também não o é para outros
tipos de dados compostos.
Compatível com a linguagem C, o C++ mantém as constantes básicas e introduz algumas novas funcionalidades possibilitadas pelo modificador const.
O uso do modificador const tem duas funções principais:
1. Resguarda da inviolabilidade de valores apontados
por ponteiros;
2. Auxílio na compreensão das características de funções,
durante a implementação.
1.1 Simbólicas
Constantes simbólicas podem ser criadas com as diretivas
do preprocessador #define. Neste modo os valores, de
fato, não são interpretados imediatamente pelo compilador,
antes são identificados e substituidos pelo preprocessador
no estágio anterior à compilação. Por exemplo:
#define BUFFER_LENGTH 2048 ... ... ... char
data[BUFFER_LENGTH];
Observe que o valor 2048 será usado logo abaixo no código,
depois que o preprocessador substituir a constante
simbólica BUFFER_LENGTH pelo valor que lhe foi atribuído.
Note que as constantes são escritas com todas as letras
maiúsculas, isso não é uma regra, mas ajuda a identificar
o que é constante simbólica dentro do programa, sendo
adotado pela maioria dos desenvolvedores como uma boa
prática de programação.
Neste caso, podemos definir valores simbólicos compostos,
por exemplo:
#define BUFFER_LENGTH 2048 #define
N_BUFFERS 100 #define MASTER_LENGTH (
BUFFER_LENGTH * N_BUFFERS ) ... ... ... char
screen[MASTER_LENGTH];
Os valores podem ser simbólicos em formato de código,
o que permite criar programas com melhor legibilidade.
Para isso podemos colocar expressões com funcionalidades
bem definidas substituídas por nomes que as identifiquem.
Por exemplo:
float a[3]; #define PRINT_VECTOR cout << a[0] << " , "
<< a[1] << " , " << a[2] << endl ... ... PRINT_VECTOR;
1
Desta forma, todas as vezes que quisermos mostrar o valor
do vetor de três coordenadas podemos usar a constante
PRINT_VECTOR.
1.2 Literais
Constantes literais podem ser declaradas da mesma forma
que na linguagem “C”, ou seja, podemos definir valores fixos
em qualquer parte do programa, expressando-os diretamente
no código através de seu valor significativo. Por
exemplo, podemos definir números:
256 //decimal 0400 //octal 0x100 //hexadecimal
Também podemos definir valores para caracteres ou cadeias
de caracteres, como segue:
'a' // um caractere “abc” // uma cadeia de caracteres
"\xF3\x23\x12” // uma cadeia de caracteres representada
por seus valores em hexadecimal
Temos ainda a possibilidade de declarar constantes compostas
por valores e operadores:
(4.23e14 * (12.75 + 12976.18/36)) // constante composta
1.3 Enumerações
Valores enumerados são muito recorrentes nos ambientes
de programação, por isso podemos contar com a declaração
de enum em C++ também, o que segue a mesma
sintaxe que temos em “C":
enum seq {A,B,C,D}; seq x;
ou ainda:
enum nomes {LANY=100,SANDRA=200,MARCIA=300,RODRIGO=400};
nomes x;
Porém, observamos uma diferença: a palavra enum pode
ser dispensada na declaração da variável, enquanto que
em C é obrigatório,apesar desta pequena diferença a funcionalidade
do recurso é a mesma, ou seja, pode-se definir
variáveis que assumem estritamente os valores presentes
na declaração de enumeração.
Este recurso torna-se útil na padronização de valores a se-
2 2 VARIÁVEIS
rem usados como entrada de funções, por exemplo. Pode
ser considerada como uma funcionalidade mnemônica,
seu uso não altera o código final caso modifiquemos o
programa para que use variáveis inteiras ou strings de
mesmo valor do enum.
A seguinte sintaxe:
seq x = 3;
Não é permitida, mesmo que o valor presente no enum
seja avaliado como 3 pelo compilador em tempo de compilação.
Isso pode parecer confuso, mas lembre-se de
que os valores serão atribuidos pelo compilador, logo isso
evita que o mesmo programa seja compilado em ambientes
diferentes e tenha comportamento diferente.
2 Variáveis
As variáveis no C++ podem ser usadas da mesma forma
que na linguagem “C”, porém algumas poucas diferenças
podem ser destacadas, principalmente aquelas que trazem
à linguagem C++ características próprias da orientação a
objetos.
2.1 Tipos
Como na linguagem “C”, os tipos nativos do compilador
em uso são referenciados por:
char int float double
Que correspondem a números com tamanho relativos,
com os significados respectivos: caractere, inteiro, ponto
flutuante e ponto flutuante de dupla precisão. De qualquer
forma a extensão dos mesmos depende da máquina
que se pretende programar. Considerando que nem sempre
teremos que programar apenas computadores, poderemos
ter extensões bem distintas dependendo do hardware
a ser programado, por exemplo, computadores domésticos
tipicamente tem processadores de 32 ou 64 bits
hoje em dia, enquanto que dispositivos embarcados podem
ter processadores de 8, 16 ou 32 bits. Portanto, o
compilador para cada caso atribui faixas diferentes para
cada tipo em cada situação.
A linguagem C++ introduz o tipo bool, que representa o
valor booleano, falso ou verdadeiro, o que não existe na
linguagem “C”, porém seu tamanho na memória depende
da capacidade de otimização do compilador usado. Tipicamente
os compiladores para computadores usam uma
variável do tamanho de char para representar o valor, o
que poderia ser considerado um desperdício, mas devido
à abundância de memória não chega a ser inadequado.
Porém em sistemas pequenos há compiladores que armazenam
o valor booleano em apenas um bit. Obviamente,
se o processador possuir recursos de manipulação
de bits isso é muito útil e pode ser usado como um fator
de melhoria da qualidade do software desenvolvido. Em
outros ambientes, onde a manipulação de bits traga prejuízo
para o desempenho usa-se a estratégia padrão de
desperdiçar um pouco de espaço em favor de uma agilidade
maior nas operações. Portanto, embora as variações
de utilização do espaço sejam muitas, o compilador sempre
fará a mais apropriada para cada ambiente de utilização
da linguagem.
2.2 Modificadores
O C++ conta com os modificadores de amplitude
(short,long) presentes na linguagem “C” e modificadores
de acesso, alguns exclusivos do C++, que estão diretamente
ligados a características da POO (programação
orientada a objetos). Desta forma descreveremos apenas
os tipos relevantes exclusivamente para a programação
na linguagem escopo do livro presente sem nos aprofundarmos
na teoria por trás dos mesmos. A prática do uso
dos mesmos é melhor indicada como meio de aprofundamento
do tema.
Assim contamos com os modificadores da linguagem
“C":
static short long unsigned signed
2.2.1 const
A linguagem C++ introduz um novo modificador chamado
const, que tem comportamento variado dependendo
do local onde está sendo declarado. Sua função,
basicamente, é estabelecer um vínculo entre declaração
e obrigatoriedade da coerência no uso do símbolo declarado.
A princípio, quando declaramos uma constante com este
modificador fazemos com que seja obrigatório o uso do
símbolo de forma que o mesmo não possa ter seu valor
alterado. Assim, se fizermos:
const int x = 4;
O inteiro x não poderá deixar de ter valor igual a 4. Qualquer
tentativa de modificar o valor da constante ao longo
do programa será reportada como erro pelo compilador.
Porém podemos considerar esta funcionalidade como óbvia
e trivial, ainda temos o uso do modificador de uma
forma mais proveitosa, na passagem de parâmetros para
funções, por exemplo:
void inhibitX(const int *x) { ... ... BASEADDRESS =
z*((*x) - 23p*71); ... ... }
Neste caso, a função acima recebe um valor inteiro através
de um ponteiro, que não obrigatoriamente precisa
ser constante no escopo fora da função, porém dentro da
2.3 Nomeando tipos 3
mesma a variável será constante. Fazendo este simples
procedimento teremos como fazer com que um símbolo
seja variável fora da função e constante dentro da mesma,
de forma que dentro do escopo da mesma só façamos leituras
do seu valor. O artifício cria duas consequëncias
importantes: a primeira é a melhor legibilidade do código,
visto que ao usarmos uma função teremos certeza
de que os valores não serão alterados dentro da função;
a segunda é que poderemos evitar erros inadvertidos de
atribuição de valores à variável quando da construção da
função.
2.2.2 volatile
Uma variável "volátil", como a própria expressão sugere,
é uma variável que pode ser modificada sem o conhecimento
do programa principal, mesmo que esta ainda
esteja declarada dentro do escopo onde o programa está
sendo executado. Isso está relacionado, principalmente a
processos concorrentes e “threads”, estes podem alterar o
conteúdo da variável em eventos fora da previsibilidade
do tempo de compilação. Em outras palavras, o compilador
não pode prever com segurança se pode otimizar
trechos de programa onde esta variável se encontra.
A palavra reservada volatile é destinada as situações onde
uma variável pode ter seu valor alterado por fatores diversos,
e portanto, não pode ser otimizada. Usando-a o
programador informa ao compilador que não deve interferir
na forma com que o programa foi escrito para acesso
a esta variável. Desta forma impede que erros inseridos
por otimização estejam presentes na versão final do executável.
O uso desta palavra implica em mudança no comportamento
do compilador durante a interpretação do código.
As classes de objetos do tipo voláteis só poderão ser acessadas
por rotinas que declarem aceitar como entrada dados
voláteis, da mesma forma que apenas objetos voláteis
podem acessar variáveis voláteis. Esta “amarração” faz
com que o uso de tais variáveis se torne mais seguro.
Podemos declarar variáveis voláteis da seguinte forma:
volatile int x;
Enquanto que para funções que acessam tais variáveis teremos
consequências visíveis na montagem do código,
por exemplo, se tivermos o seguinte trecho de programa:
int x = 1265; void main_loop() { while( x == 1265) { //
fazer alguma coisa } }
Poderemos ter uma otimização gerada pelo compilador
como segue:
int x = 1265; void main_loop_optimized() { while( true
) { // fazer alguma coisa } }
Considerando que em um programa que foi desenhado
para ambiente multitarefa isso não pode ser considerado
verdadeiro, pois o programa pode estar esperando que
uma das tarefas modifique o estado da variável para prosseguir
seu curso, a otimização acima será um desastre,
uma vez que a função acima jamais será encerrada.
Para evitar isso fazemos:
volatile int x = 1265; void main_loop() { while( x ==
1265) { // fazer alguma coisa } }
E o compilador não poderá mais avaliar que o valor de
x pode ser otimizado para o valor corrente, pois informamos
na declaração que o valor da variável pode ser
alterado sem seu conhecimento. Desta forma o mesmo
não alterará o algorítmo e fará o teste da variável dentro
do while.
2.3 Nomeando tipos
A linguagem “C” possui recursos de nomeação de tipos
simples e compostos através das palavras chaves typedef
e struct. Adicionada a estas o C++ acrescenta a palavra
chave class. Vejamos como devemos definir um novo
tipo através desta palavra chave.
A palavra class atribui a um conjunto de tipos de dados o
estado de modelo de objeto. Este conceito é fundamental
para o modo avançado de programar usando o C++. Com
este identificador declaramos objetos, da mesma forma
que declaramos estruturas.
Uma classe pode ser definida em um cabeçalho “header”,
da seguinte forma:
class nome_da_classe { <tipo_1> variavel_1; <tipo_2>
variavel_2; . . . <tipo_n> variavel_n; ----- <tipo_n>
nome_funcao ( <tipo_1> variavel_1, <tipo_2> variavel_2,
<tipo_3> variavel_3 ...); };
O mais interessante de observar é a presença de uma função
dentro da declaração acima. Então poderíamos perguntar:
Por que colocar uma função dentro de um tipo?
A resposta é simples: Para manipular os dados dentro do
tipo! Não apenas por esta característica, mas por várias
outras que aboradaremos nos capítulos seguintes, o tipo
class é extremamente flexível e poderoso.
É importante ressaltar que em C++ a declaração do
identificador: enum, struct, class, etc... é dispensado
quando se declara uma variável ou objeto para o referido
tipo. Desta forma podemos ter também as seguintes declarações
como válidas, além do uso padrão da linguagem
“C":
struct data{ int a; int b; int c; }; class object{ int a; char
b; long w; float p; void getData(); }; ... ... ... ... void
func() { data x; object y; ... ... y.getData(); }
Como podemos ver na função acima se a variável x for de-
4 2 VARIÁVEIS
clarada para uma estrutura data o uso da palavra struct
não é obrigatório, assim como também não o é para outros
tipos de dados compostos.
Comentários
Enviar um comentário