A mais simples estrutura para representar um dado é um escalar (Fig. 2.1a), que representa um elemento de informação que pode ser acessado através de um identificador.
A representação elementar de um dado se dá através de um escalar. Em assembly, tipicamente todos os valores escalares são representados na forma de bytes ou words. Diferentes linguagens de alto nível determinam tipos diferenciados de escalares que são compreendidos pela linguagem.
Internamente, todos os dados são representados no computador como seqüências de bits2.2. Esta é a forma mais conveniente para manipular os dados através de circuitos digitais, que podem diferenciar apenas entre dois estados (on ou off, verdade ou falso, 0 ou 1). Uma seqüência de bits pode representar uma faixa com valores distintos.
O formato de representação interna (ou seja, como uma seqüência de bits é traduzida para um valor) pode variar de computador para computador, embora haja um esforço crescente para uniformizar a representação de tipos básicos. Assim, um caráter usualmente ocupa um byte com conteúdo definido por algum padrão de codificação (EBCDIC, ASCII, UniCode, ISO); um número inteiro tem uma representação binária inteira (sinal e magnitude, complemento de um ou complemento de dois); e um valor real é usualmente representado tipicamente no formato sinal, mantissa e expoente.
A linguagem C suporta escalares através da declaração de variáveis em um dos seus tipos de dados básicos2.3, que são:
char | caráter |
int | inteiro |
float | real |
double | real de precisão dupla |
Observe que, ao contrário de outras linguagens de alto nível, em C não há um tipo booleano para construir variáveis que assumem o valor verdadeiro ou falso. Em C, em uma expressão booleana qualquer valor inteiro diferente de 0 é interpretado como verdadeiro, enquanto que o valor 0 é interpretado como falso.
Uma variável do tipo char ocupa um byte com o valor binário da representação ASCII de um caráter. O formato ASCII básico (Tabela 2.1) permite representar 128 caracteres distintos (valores entre 0 e 127), entre os quais estão diversos caracteres de controle (tais com ESC, associado à tecla de escape, e CR, associado ao carriage return) e de pontuação. Outros formatos além de ASCII inclui ISO8859, que padroniza a representação associada à faixa de valores entre 128 e 255 organizando-a em diversos subconjuntos, dos quais o ISO8859-1 (Latin-1) é o mais utilizado. UniCode integra várias famílias de caracteres em uma representação unificada (de um ou dois bytes), sendo que a base dessa proposta engloba as codificações ASCII e ISO8859-1.
Caracteres ASCII são denotados em C entre aspas simples, tais como
'A'
. Cada caráter ASCII corresponde também a uma representação
binária usada internamente -- por exemplo, o caráter ASCII A equivale
a uma seqüência de bits que corresponde ao valor hexadecimal
ou decimal 65.
Além dos caracteres alfanuméricos e de pontuação, que podem ser
representados em uma função diretamente pelo símbolo correspondente entre
aspas, C também define representações para caracteres especiais de controle
do código ASCII através de seqüências de escape iniciados pelo símbolo
\
(contrabarra). As principais seqüências são apresentadas na
Tabela 2.2.
Seqüências de caracteres ocorrem tão frequentemente em computação que
recebem um nome específico, (string). Em C, strings são
representadas por arranjos de caracteres, com o fim da string
sendo marcado pelo caráter NUL ('\0'
).
O tipo int representa um valor inteiro que pode ser positivo ou negativo. O número de bytes ocupado por este tipo (e conseqüentemente a faixa de valores que podem ser representados) refletem o tamanho ``natural'' do inteiro na máquina onde o programa será executado. Usualmente, quatro bytes (32 bits) são reservados para o tipo int nos computadores atuais, permitindo representar valores na faixa entre a , ou a .
Para declarar uma variável escalar de nome i do tipo inteiro em um programa C, a seguinte expressão seria utilizada:
int i;
Essa declaração reserva na memória um espaço para a variável i, suficiente para armazenar a representação binária em complemento de dois do valor associado à variável, que inicialmente é indefinido. Caso se desejasse ter um valor inicial definido para a variável, a declaração seria acompanhada da inicialização, como em
int i = 0;
Variáveis inteiras podem ser qualificadas na sua declaração como short ou long e unsigned. Um tipo unsigned int indica que a variável apenas armazenará valores positivos; o padrão é que variáveis inteiras utilizem a representação em complemento de dois, com valores positivos e negativos (Apêndice A).
Os modificadores short e long modificam o espaço reservado para o armazenamento da variável. Um tipo short int indica que (caso seja possível) o compilador deverá usar um número menor de bytes para representar o valor numérico -- usualmente, dois bytes são alocados para este tipo. Uma variável do tipo long int indica que a representação mais longa de um inteiro deve ser utilizada, sendo que usualmente quatro bytes são reservados para variáveis deste tipo.
Estas dimensões de variáveis denotam apenas uma situação usual definida por boa parte dos compiladores, sendo que não há nenhuma garantia quanto a isto. A única coisa que se pode afirmar com relação à dimensão de inteiros em C é que uma variável do tipo short int não terá um número maior de bits em sua representação do que uma variável do tipo long int2.4.
Em C, números inteiros podem ter representação decimal, octal ou
hexadecimal. Números com representação decimal são denotados por qualquer
seqüência de algarismos entre 0 e 9 que inicie com um
algarismo diferente de 0 -- 10, 127, 512, etc. Números em octal
são seqüências de algarismos entre 0 e 7 iniciadas por
0 -- 012 (decimal 10), 077 (decimal 63), etc. Números em
hexadecimal são seqüências de algarismos entre 0 e F
iniciadas com o prefixo 0x
-- 0xF (decimal 15),
0x1A (decimal 26). As representações octal e hexadecimal são
atrativas como formas compactas de representação de valores binários --
cada algarismo da representação octal pode ser diretamente expandido para
uma seqüência de três bits, e da representação hexadecimal para seqüências
de quatro bits. Assim, tanto 127 quanto 0177 quanto
0x7F correspondem a uma mesma seqüência de bits, .
Os tipos float e double representam valores em ponto flutuante, limitados apenas pela precisão da máquina que executa o programa. O tipo float oferece seis dígitos de precisão enquanto que double suporta o dobro da precisão de um float.
Valores com representação em ponto flutuante (reais) são representados em C através do uso do ponto decimal, como em 1.5 para representar o valor um e meio. A notação exponencial também pode ser usada, como em 1.2345e-6 ou em 0.12E3.