C Caracteres (char)

Uma opção de menu ('s' ou 'n'), a inicial de um nome, uma tecla apertada, um algarismo lido do teclado: tudo isso é um caractere. Em C, um caractere mora no tipo char. E aqui vem a grande revelação desta aula: para o computador, um char é só um número pequeno. Entender isso explica por que dá para somar letras, transformar '7' em 7 e por que o ç dá trabalho.

Você vai aprender
  • Como criar um char e mostrar com %c (a letra) ou %d (o número).
  • A tabela ASCII: o número de cada letra, algarismo e símbolo.
  • Contas com caracteres: 'A' + 1, 'z' - 'a', maiúscula e minúscula.
  • A diferença entre aspas simples ('A') e duplas ("A").
  • Como transformar o algarismo '7' no número 7.
  • Por que 'ç' não cabe num char.
  • Uma prévia de getchar e putchar, que leem e escrevem um caractere.

Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.


A ideia: o código secreto combinado

Quando crianças, muita gente brincou de código secreto: A = 1, B = 2, C = 3… Para mandar "OI", você escrevia "15 9", e o amigo, que conhecia a tabela, traduzia de volta.

O computador só guarda números. Para guardar letras, os computadores combinaram uma tabela parecida, a ASCII (American Standard Code for Information Interchange, "Código Padrão Americano para Troca de Informações", de 1963). Nela, 'A' é 65, 'B' é 66, 'a' é 97, o algarismo '0' é 48 e o espaço é 32. Um char guarda o número; quem decide mostrar como letra é o printf, quando você pede %c.


1. Criar um char

#include <stdio.h>

int main(void) {
    char letra = 'A';
    printf("Como letra:  %c\n", letra);
    printf("Como número: %d\n", letra);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra letra.c -o letra
$ ./letra
Como letra:  A
Como número: 65
char
De character, "caractere". Ocupa 1 byte: guarda números de -128 a 127 no gcc do laboratório e no editor.
'A'
Um caractere vai entre aspas simples, e só cabe um. O compilador troca 'A' pelo número 65.
%c
"Mostre este número como caractere": procura o 65 na tabela e desenha um A.
%d
"Mostre este número como número": 65. É a mesma caixa, com o mesmo conteúdo; só muda o jeito de mostrar.

Missão: o programa mostra A = A. Faça ele mostrar a letra minúscula a e o seu número na tabela: a = 97. Você precisa mudar o valor de letra e uma das letras do printf.

#include <stdio.h>

int main(void) {
    char letra = 'A';
    printf("%c = %c\n", letra, letra);
    return 0;
}
#include <stdio.h>

int main(void) {
    char letra = 'a';
    printf("%c = %d\n", letra, letra);
    return 0;
}

2. Aspas simples × aspas duplas

Em C, as aspas não são enfeite: cada tipo faz uma coisa diferente.

EscritaO que éTipoMostra com
'A'Um caractere: o número 65int (cabe num char)%c
"A"Um texto de uma letra: o A e um '\0' invisível que marca o fimarray de char%s

(Sim: em C, o literal 'A' é do tipo int, e sizeof('A') dá 4, como você viu em Tipos de Dados. Na prática, não faz diferença: ele cabe num char sem nenhum aviso.)

Trocar uma pela outra é erro:

#include <stdio.h>

int main(void) {
    char a = 'A';
    char b = "A";
    printf("%c %c\n", a, b);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra aspas.c -o aspas
aspas.c: In function ‘main’:
aspas.c:5:14: error: initialization of ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
    5 |     char b = "A";
      |              ^~~

"Inicialização de char a partir de char *". O "A" é um texto, e o C o entrega como o endereço onde as letras estão guardadas (um char *, aula Ponteiros e Strings). Endereço não cabe numa caixa de caractere. No editor, o Clang diz incompatible pointer to integer conversion initializing 'char' with an expression of type 'char[2]': o char[2] são as duas posições, 'A' e '\0'.

Duas letras em aspas simples

E o contrário, duas letras entre aspas simples? O gcc avisa, mas compila:

$ cat duas.c
#include <stdio.h>

int main(void) {
    char iniciais = 'AN';
    printf("%c\n", iniciais);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra duas.c -o duas
duas.c: In function ‘main’:
duas.c:4:21: warning: multi-character character constant [-Wmultichar]
    4 |     char iniciais = 'AN';
      |                     ^~~~
duas.c:4:21: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘16718’ to ‘78’ [-Woverflow]
$ ./duas
N                        « o A sumiu

Multi-character character constant = "constante de caractere com vários caracteres". O gcc juntou os dois números num só (65 × 256 + 78 = 16718), que não cabe num char; sobrou o 78, o N. Para guardar "AN", use um texto: char iniciais[] = "AN"; (aula Strings).


3. Um pedaço da tabela ASCII

Este programa mostra os caracteres visíveis da tabela, do 32 (o espaço) ao 126. (Ele usa um for e um if para montar as colunas; por enquanto, olhe só a saída.)

#include <stdio.h>

int main(void) {
    for (int codigo = 32; codigo < 127; codigo++) {
        printf("%3d %c   ", codigo, codigo);
        if ((codigo - 31) % 6 == 0) {
            printf("\n");
        }
    }
    printf("\n");
    return 0;
}
$ gcc -std=c23 -Wall -Wextra tabela.c -o tabela
$ ./tabela
 32      33 !    34 "    35 #    36 $    37 %
 38 &    39 '    40 (    41 )    42 *    43 +
 44 ,    45 -    46 .    47 /    48 0    49 1
 50 2    51 3    52 4    53 5    54 6    55 7
 56 8    57 9    58 :    59 ;    60 <    61 =
 62 >    63 ?    64 @    65 A    66 B    67 C
 68 D    69 E    70 F    71 G    72 H    73 I
 74 J    75 K    76 L    77 M    78 N    79 O
 80 P    81 Q    82 R    83 S    84 T    85 U
 86 V    87 W    88 X    89 Y    90 Z    91 [
 92 \    93 ]    94 ^    95 _    96 `    97 a
 98 b    99 c   100 d   101 e   102 f   103 g
104 h   105 i   106 j   107 k   108 l   109 m
110 n   111 o   112 p   113 q   114 r   115 s
116 t   117 u   118 v   119 w   120 x   121 y
122 z   123 {   124 |   125 }   126 ~   

Três coisas para guardar:

  • Os algarismos vêm em ordem e juntos: '0' = 48, '1' = 49 … '9' = 57.
  • As maiúsculas também: 'A' = 65 … 'Z' = 90.
  • E as minúsculas: 'a' = 97 … 'z' = 122. Cada minúscula fica exatamente 32 depois da sua maiúscula.

Ninguém decora a tabela inteira. O importante é saber que ela existe, que é ordenada e que você pode consultá-la (ou rodar este programa) quando precisar.

Os caracteres invisíveis

Os códigos de 0 a 31 (e o 127) não desenham nada: são comandos, como "pule uma linha". Você já conhece alguns, escritos com a barra invertida (aula Novas Linhas e Escapes):

#include <stdio.h>

int main(void) {
    printf("'\\n' = %d\n", '\n');
    printf("'\\t' = %d\n", '\t');
    printf("'\\0' = %d\n", '\0');
    printf("' '  = %d\n", ' ');
    printf("'\\'' = %d\n", '\'');
    return 0;
}

Dentro do texto do printf, \\ mostra uma barra invertida; por isso "'\\n'" aparece como '\n' na tela.

$ gcc -std=c23 -Wall -Wextra invisiveis.c -o invisiveis
$ ./invisiveis
'\n' = 10                « nova linha
'\t' = 9                 « tabulação
'\0' = 0                 « o caractere nulo, que marca o fim dos textos
' '  = 32                « o espaço ocupa lugar, mas não tem desenho
'\'' = 39                « aspas simples dentro de aspas simples: com barra

4. Contas com char

Se um char é um número, dá para fazer contas com ele:

#include <stdio.h>

int main(void) {
    char letra = 'A';
    char proxima = letra + 1;
    printf("%c %c\n", letra, proxima);
    printf("%c\n", 'a' + 2);
    printf("%c\n", 66);
    printf("%d\n", 'z' - 'a');
    return 0;
}
$ gcc -std=c23 -Wall -Wextra soma.c -o soma
$ ./soma
A B
c
B
25
letra + 1
65 + 1 = 66, que na tabela é o B. A letra seguinte.
'a' + 2
97 + 2 = 99, o c.
printf("%c", 66)
O %c aceita qualquer número da tabela, mesmo sem variável char: 66 é B.
'z' - 'a'
122 - 97 = 25: a distância entre as letras. O z é a 26ª letra, 25 posições depois do a.

Maiúscula e minúscula

Como cada minúscula está 32 posições depois da maiúscula, subtrair 32 transforma 'g' em 'G':

$ cat maiuscula.c
#include <stdio.h>

int main(void) {
    char minuscula = 'g';
    char maiuscula = minuscula - 32;
    printf("%c -> %c\n", minuscula, maiuscula);
    printf("%d %d\n", 'a' - 'A', 'g' - 'G');
    return 0;
}
$ gcc -std=c23 -Wall -Wextra maiuscula.c -o maiuscula
$ ./maiuscula
g -> G
32 32

Funciona, mas só para letras de a a z (tente com '!' e veja o que sai). Na vida real, use as funções prontas toupper e tolower ("para maiúscula", "para minúscula"), que conferem se o caractere é mesmo uma letra. Elas e as perguntas do tipo "é algarismo?", "é letra?" estão em Classificar Caracteres (ctype.h). Escrever 'a' - 'A' no lugar do 32 também deixa o código mais claro do que o número solto.

Missão: complete o printf para mostrar a letra guardada em letra e as três seguintes, separadas por espaço, usando letra + 1, letra + 2 e letra + 3. Com 'D', a saída deve ser D E F G.

#include <stdio.h>

int main(void) {
    char letra = 'D';
    printf("%c\n", letra);
    return 0;
}
#include <stdio.h>

int main(void) {
    char letra = 'D';
    printf("%c %c %c %c\n", letra, letra + 1, letra + 2, letra + 3);
    return 0;
}

Troque 'D' por 'X' e rode de novo: depois do Z vêm [ e \, porque a tabela continua. Fazer a conta "dar a volta" para o A é o segredo da cifra de César, um código secreto de verdade, que você vai conseguir escrever depois de Operadores Aritméticos.

O algarismo '7' não é o número 7

Quando o programa lê o que a pessoa digitou, os algarismos chegam como caracteres. E o caractere '7' vale 55 na tabela, não 7:

#include <stdio.h>

int main(void) {
    char c = '7';
    printf("%c vale %d na tabela\n", c, c);
    int numero = c - '0';
    printf("como número: %d\n", numero);
    printf("dobro: %d\n", numero * 2);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra digito.c -o digito
$ ./digito
7 vale 55 na tabela
como número: 7
dobro: 14

O truque é c - '0': como os algarismos estão em ordem a partir do 48, subtrair '0' (48) dá a posição do algarismo: 55 - 48 = 7. Esse truque é garantido pela norma do C para os algarismos de '0' a '9', em qualquer computador. Para textos inteiros, como "2024", existe strtol (aula Texto ↔ Número).


5. Por que 'ç' não cabe num char

A tabela ASCII tem só 128 posições, e nenhuma delas é ç, ã ou é. Os arquivos de hoje usam a codificação UTF-8, em que cada letra acentuada ocupa 2 bytes (aula Acentos e UTF-8). Dois bytes não cabem numa caixa de um:

#include <stdio.h>

int main(void) {
    char c = 'ç';
    printf("%c\n", c);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra acento.c -o acento
acento.c: In function ‘main’:
acento.c:4:14: warning: multi-character character constant [-Wmultichar]
    4 |     char c = 'ç';
      |              ^~~
acento.c:4:14: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘50087’ to ‘-89’ [-Woverflow]
$ ./acento
�                        « meio ç: um byte sozinho, que o terminal não sabe desenhar

O gcc viu dois bytes entre as aspas (é o mesmo aviso de 'AN'), guardou só o segundo e o terminal mostrou um losango com interrogação. No editor, o Clang é mais rígido e nem compila: error: character too large for enclosing character literal type ("caractere grande demais para o tipo do literal").

Dentro de textos entre aspas duplas, acentos funcionam normalmente: printf("Atenção!\n"); mostra Atenção!, porque os bytes do ç passam juntos até o terminal. O problema é só guardar uma letra acentuada sozinha num char.


6. Prévia: ler e escrever um caractere

A biblioteca tem duas funções pequenas para um caractere de cada vez: getchar ("pegue um caractere", do teclado) e putchar ("ponha um caractere", na tela).

#include <stdio.h>

int main(void) {
    int c = getchar();
    printf("Você digitou '%c', código %d\n", c, c);
    putchar(c);
    putchar('\n');
    return 0;
}
$ gcc -std=c23 -Wall -Wextra eco.c -o eco
$ echo Ana | ./eco
Você digitou 'A', código 65
A
int c = getchar();
Lê um caractere da entrada. Repare que a caixa é int, e não char: quando a entrada acaba, o getchar devolve um valor especial, EOF (end of file, "fim do arquivo", que vale -1), que precisa caber junto com todos os 256 bytes possíveis. As aulas while e stdin, stdout e stderr usam isso.
echo Ana | ./eco
O echo Ana | manda o texto "Ana" para o programa como se alguém tivesse digitado. O getchar pegou só o A; o "na" ficou esperando e foi ignorado.
putchar(c)
Escreve o caractere na tela, sem pular linha. Por isso o segundo putchar('\n').

Missão: o programa pergunta se a Ana quer sacola e lê a resposta com getchar (a caixa já vem com a resposta s digitada). Mas mostra Você respondeu: 115 (código 115). Troque uma letra do printf para mostrar o caractere na primeira posição: Você respondeu: s (código 115).

#include <stdio.h>

int main(void) {
    printf("Quer sacola? (s/n) ");
    int resposta = getchar();
    printf("Você respondeu: %d (código %d)\n", resposta, resposta);
    return 0;
}
#include <stdio.h>

int main(void) {
    printf("Quer sacola? (s/n) ");
    int resposta = getchar();
    printf("Você respondeu: %c (código %d)\n", resposta, resposta);
    return 0;
}

Formas diferentes: qual usar

O mesmo A pode ser escrito de três jeitos, e a mesma maiúscula pode ser obtida de três jeitos:

$ cat formas.c
#include <stdio.h>

int main(void) {
    char a = 'A';
    char b = 65;
    char c = '\x41';
    printf("%c %c %c\n", a, b, c);
    return 0;
}
$ gcc -std=c23 -Wall -Wextra formas.c -o formas
$ ./formas
A A A
FormaExemploQuando usar
Aspas simples'A'Sempre que possível. Quem lê entende na hora.
Número da tabela65Quase nunca: quem lê tem de consultar a tabela.
Escape em hexadecimal'\x41'Para caracteres sem desenho (códigos de controle); raramente para letras.
Somar ou subtrair 32c - 32Evite: o 32 solto não explica nada.
Distância entre as letrasc - 'a' + 'A'Para entender a tabela (e em exercícios).
Função prontatoupper(c)No dia a dia. Confere se é letra (aula ctype.h).

Quando dá errado

error: initialization of ‘char’ from ‘char *’ makes integer from pointer without a cast

O que significa: você usou aspas duplas ("A") para um char. Aspas duplas fazem um texto.

Como consertar: char letra = 'A';, com aspas simples. Se você quer mesmo um texto, use char nome[] = "Ana";.

warning: multi-character character constant [-Wmultichar]

O que significa: há mais de um caractere entre aspas simples: 'AN', ou uma letra acentuada como 'ç', que em UTF-8 são dois bytes. Só um deles fica na caixa. No editor, o caso do 'ç' é erro: character too large for enclosing character literal type.

Como consertar: um caractere por char; para mais de um, ou para acentos, use um texto entre aspas duplas.

A saída mostra 65 em vez de A (ou A em vez de 65)

O que significa: a letra do printf decide: %c mostra o caractere, %d mostra o número. O -Wall não avisa, porque as duas formas são válidas.

Como consertar: escolha a letra pelo que você quer ver.

A conta com algarismos dá um número enorme ('7' + '5' = 108)

O que significa: você somou os códigos da tabela (55 + 53), e não os algarismos.

Como consertar: converta cada um com - '0': ('7' - '0') + ('5' - '0') = 12.


Referência rápida

EscritaO que faz
char c = 'A';Guarda o caractere A (o número 65)
printf("%c", c)Mostra como caractere: A
printf("%d", c)Mostra o código: 65
c + 1O caractere seguinte na tabela
c - '0'Algarismo para número: '7' - '0' = 7
c - 'a' + 'A'Minúscula para maiúscula (só de a a z; prefira toupper)
'\n', '\t', '\0', '\''Nova linha (10), tabulação (9), nulo (0), aspas simples
int c = getchar();Lê um caractere (ou EOF, no fim da entrada)
putchar(c);Escreve um caractere
Códigos: '0' = 48, 'A' = 65, 'a' = 97, espaço = 32, '\n' = 10

Exercícios

1. Código da letra

Na tabela ASCII, 'A' é 65. Qual é o código de 'C'?

2. Algarismo para número

Complete para transformar o caractere c (um algarismo) no número correspondente:

int numero = c - ;

3. O que printf("%c", 'a' + 1); mostra?

4. Qual é a diferença entre 'A' e "A"?

5. Por que char c = 'ç'; dá problema?

6. Por que a variável que recebe getchar() deve ser int?

Missão final: somar algarismos

Missão: a Ana digitou os algarismos 7 e 5, que chegaram ao programa como caracteres. O programa soma os dois e mostra Soma: 108, porque somou os códigos da tabela (55 + 53). Use o truque do - '0' em cada um para mostrar Soma: 12.

#include <stdio.h>

int main(void) {
    char a = '7';
    char b = '5';
    int soma = a + b;
    printf("Soma: %d\n", soma);
    return 0;
}
#include <stdio.h>

int main(void) {
    char a = '7';
    char b = '5';
    int soma = (a - '0') + (b - '0');
    printf("Soma: %d\n", soma);
    return 0;
}

Resumo

  • Um char tem 1 byte e guarda um número pequeno; a tabela ASCII diz qual caractere cada número representa ('A' = 65, 'a' = 97, '0' = 48).
  • %c mostra o caractere; %d mostra o número. É a mesma caixa.
  • Aspas simples: um caractere ('A'). Aspas duplas: um texto ("A", com '\0' no fim). Trocar dá erro ou aviso.
  • Dá para fazer contas: 'A' + 1 é 'B'; minúscula = maiúscula + 32; c - '0' transforma um algarismo em número.
  • Letras acentuadas ocupam 2 bytes em UTF-8 e não cabem num char; em textos entre aspas duplas, funcionam.
  • getchar lê um caractere (guarde num int, por causa do EOF); putchar escreve um.