Um caracter (= character) nada mais é que um número natural entre 0 e 255. (Portanto, cada caracter é representado por um byte na memória do computador). Reciprocamente, cada número natural do conjunto 0, 1, 2, . . . , 254, 255 é um caracter. Caracteres são um dos tipos-de-dados básicos da linguagem C.
Antes de prosseguir, convém lembrar algumas convenções sobre a representação de números. Na linguagem C, números naturais (zero, um, dois, três etc.) podem ser escritos em notação decimal (base 10) ou em notação octal (base 8). Por exemplo, o número sessenta e cinco é representado por 65 em notação decimal (6×10 + 5) e por 101 em notação octal (1×64 + 0×8 + 1). Para não confundir as duas notações, a linguagem C adota a seguinte convenção: na representação octal de um número, o primeiro dígito é sempre 0. Assim, a representação octal de sessenta e cinco é 0101 e a representação octal de quarenta e oito é 060. As representações decimais desses dois números são 65 e 48 respectivamente.
Quando um caracter é exibido em um dispositivo de impressão ou na tela do monitor de vídeo, ele é representado por um símbolo gráfico. Por exemplo, o caracter 65 é exibido na impressora e na tela do monitor como
A
e o caracter 66 é exibido como
B
Alguns caracteres têm uma representação gráfica estranha. O caracter 32, por exemplo, é representado por um espaço em branco; a representação do caracter 0 é vazia (não ocupa espaço algum); e o caracter 10 não tem um símbolo gráfico mas produz uma mudança de linha.
Os símbolos gráficos (e os efeitos não-gráficos) que correspondem aos caracteres 0 a 127 foram estabelecidos pelo American Standard Code for Information Interchange. A correspondência é conhecida como "tabela ASCII". Os símbolos que correspondem aos caracteres 128 a 255 não estão bem padronizados: cada sistema escolhe a tabela que mais lhe agrada. Uma das tabelas mais difundidas é ISO 8859-1 (também conhecida como ISO Latin1). Eis uma amostra dessa tabela:
símbolo gráfico | notação decimal | notação octal | constante C |
caracter nulo | 0 | 0 | '\0' |
mudança de linha | 10 | 012 | '\n' |
quebra de página | 12 | 014 | '\f' |
espaço em branco | 32 | 040 | ' ' |
* | 42 | 052 | '*' |
+ | 43 | 053 | '+' |
- | 45 | 055 | '-' |
/ | 47 | 057 | '/' |
0 | 48 | 060 | '0' |
1 | 49 | 061 | '1' |
2 | 50 | 062 | '2' |
; | 59 | 073 | ';' |
A | 65 | 0101 | 'A' |
N | 78 | 0116 | 'N' |
O | 79 | 0117 | 'O' |
a | 97 | 0141 | 'a' |
o | 111 | 0157 | 'o' |
ã | 227 | 0343 | '\343' |
ç | 231 | 0347 | '\347' |
ÿ | 255 | 0377 | '\377' |
A última coluna dá a representação dos caracteres como constantes do tipo char na linguagem C.
Os caracteres ' ', '\n', '\r', '\t', '\f' são conhecidos como brancos (= white spaces). Muitas funções C (por exemplo, scanf) tratam todos os brancos exatamente da mesma maneira.
[A propósito, veja o programa od (de "octal dump") presente em qualquer sistema Unix ou GNU/Linux: ele imprime o valor de cada caracter de um arquivo dado. Por exemplo, od -t u1 -A d xxx imprime o valor de cada caracter do arquivo xxx em notação decimal.]
Os caracteres que discutimos acima são conhecidos na linguagem C como caracteres sem sinal (= unsigned characters). Para declarar uma variável x desse tipo, diga
unsigned char x;
Há um outro tipo de caracteres em C: os caracteres com sinal (= signed characters). Para declarar uma variável y desse tipo basta dizer
char y;
Um caracter com sinal é simplesmente um número inteiro entre -128 e +127. Caracteres desse tipo têm representações gráficas análogas às dos caracteres sem sinal. A tabela abaixo estabelece a correspondência: os valores que estão em uma mesma coluna da tabela têm a mesma representação gráfica.
signed | 0 | 1 | . . . | 127 | -128 | -127 | -126 | . . . | -2 | -1 |
unsigned | 0 | 1 | . . . | 127 | 128 | 129 | 130 | . . . | 254 | 255 |
(Esta é uma boa ocasião para ler a página Representação de Inteiros.)
O tipo-de-dados char pode ser automaticamente transformado no tipo-de-dados int e vice-versa. Um objeto do tipo char é essencialmente a mesma coisa que um objeto do tipo int cujo valor tenha sido limitado ao intervalo [-127..128].
Exemplo: Digamos que k é uma variável do tipo char ou do tipo int. Então os comandos de atribuição
k = 'A'; k = 65; k = 0101;
têm exatamente o mesmo efeito: cada um deles atribui à variável k o valor 65. Esse valor é representado graficamente por A. O comando printf ("CAR=%c DEC=%d", k, k) , executado depois de qualquer uma das atribuições, produzirá
CAR=A DEC=65
Outro exemplo: os comandos de atribuição
k = '0'; k = 48; k = 060;
têm exatamente o mesmo efeito: cada um deles atribui à variável k o valor 48. Depois de qualquer uma das atribuições, o comando printf ("CAR=%c DEC=%d", k, k) produzirá
CAR=0 DEC=48
Mais um exemplo: os dois comandos de atribuição
k = '\0'; k = 0;
têm exatamente o mesmo efeito: cada um deles atribui à variável k o valor 0 (zero). Esse valor é o código do caracter nulo, que não tem representação gráfica. Depois de qualquer dessas atribuições, o comando printf ("CAR=%c DEC=%d", k, k) produzirá
CAR= DEC=0
Último exemplo: os comandos
k = '\n'; k = 10; k = 012;
são equivalentes: todos atribuem à variável k o valor 10. Esse caracter não tem representação gráfica mas indica o fim de uma linha. Depois de qualquer dessas atribuições, o comando printf ("CAR=%c DEC=%d", k, k) produz
CAR= DEC=10
65 67 72 79 85 33 10 51 50 32 43 32 52 51 32 61 32 55 53
Interprete esses números como caracteres. Qual o resultado?
Operações aritméticas sobre caracteres são executadas de maneira um tanto estranha. Por exemplo, no trecho de código
unsigned char x, y, z; x = 0; y = 255; z = x - y;
a expressão x - y é calculada como se fosse (int) x - (int) y e portanto a quarta linha equivale a
z = -255;
É claro que o valor final de z é 1.
Analogamente, a expressão x - y abaixo
char x, y, z; x = 2; y = -127; z = x - y;
é calculada como se fosse (int) x - (int) y e portanto a quarta linha equivale a z = 129;. Mas é claro que o valor final de z é -127.