C++ Caracteres (char)

Uma letra, um algarismo, um sinal de pontuação: cada um é um caractere, e em C++ um caractere sozinho cabe no tipo char (de character, "caractere"). O segredo desta aula é que, para o computador, um char é um número: a letra A é guardada como 65. Entender isso explica por que dá para "somar" letras, por que std::toupper às vezes mostra um número e por que o ç não cabe num char.

Você vai aprender
  • Como criar um char e a diferença entre aspas simples ('A') e duplas ("A").
  • Que todo caractere tem um código: static_cast<int>('A') mostra 65.
  • Um pedaço da tabela ASCII e os padrões que ela esconde.
  • Contas com caracteres: 'a' + 1, '7' - '0'.
  • As funções de <cctype>: std::toupper, std::tolower, std::isdigit e companhia.
  • Por que 'ç' dá o aviso multi-character character constant.

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


A ideia: o código secreto combinado

Imagine duas crianças que combinam um código: 1 = A, 2 = B, 3 = C… Para mandar "CAB", uma escreve 3 1 2 no bilhete, e a outra traduz de volta. O bilhete só tem números; as letras existem no combinado.

O computador faz igual. A memória só guarda números. Para guardar letras, todo mundo segue uma tabela combinada, chamada ASCII (sigla em inglês de "Código Padrão Americano para Troca de Informações", criado nos anos 1960). Nela, A é 65, B é 66, o espaço é 32. Um char guarda o número; quem mostra a letra é o std::cout, que consulta a tabela.


1. Criar um char

#include <iostream>

int main() {
    char letra = 'A';
    std::cout << "letra: " << letra << "\n";
    std::cout << "código: " << static_cast<int>(letra) << "\n";
    char outra = 66;
    std::cout << "outra: " << outra << "\n";
}
char letra = 'A';
Um caractere vai entre aspas simples. Por dentro, a variável guarda 65.
std::cout << letra
O cout vê que o tipo é char e mostra a letra.
static_cast<int>(letra)
"Converta para int". O valor é o mesmo 65, mas agora o cout vê um número e mostra o número. static_cast ("conversão estática", feita na compilação) é o jeito moderno de converter tipos; ele tem aula própria em Conversão de Tipos.
char outra = 66;
O caminho inverso: guardamos o número 66, e o cout mostra a letra que tem esse código.
$ g++ -std=c++23 -Wall -Wextra letra.cpp -o letra
$ ./letra
letra: A
código: 65
outra: B

Um char ocupa 1 byte e guarda números de -128 a 127 no Linux da Ana (aula Tipos de Dados). Existem também unsigned char (0 a 255) e signed char; para guardar letras, o char simples é o que você usa.

Missão: o programa mostra o código da letra A. Acrescente duas linhas iguais para descobrir o código da letra Z e do caractere 0 (o algarismo zero, entre aspas simples). A saída deve ter Z = 90 e 0 = 48.

#include <iostream>

int main() {
    std::cout << 'A' << " = " << static_cast<int>('A') << "\n";
}
#include <iostream>

int main() {
    std::cout << 'A' << " = " << static_cast<int>('A') << "\n";
    std::cout << 'Z' << " = " << static_cast<int>('Z') << "\n";
    std::cout << '0' << " = " << static_cast<int>('0') << "\n";
}

Repare: o caractere '0' vale 48, e não 0. O algarismo que aparece na tela e o número zero são coisas diferentes. Isso vai ser útil na seção 4.


2. Aspas simples × aspas duplas

Esta é a confusão número 1 com char:

EscritaO que éTipo
'A'Um caracterechar
"A"Um texto que, por acaso, tem uma letra sótexto (vira std::string quando guardado num)
"Ana"Um texto com três letrastexto
'Ana'Um engano (veja abaixo)—

Trocar uma pela outra dá erro. Texto entre aspas duplas num char:

$ cat aspas.cpp
#include <iostream>

int main() {
    char inicial = "A";
    std::cout << inicial << "\n";
}
$ g++ -std=c++23 -Wall -Wextra aspas.cpp -o aspas
aspas.cpp: In function ‘int main()’:
aspas.cpp:4:20: error: invalid conversion from ‘const char*’ to ‘char’ [-fpermissive]
    4 |     char inicial = "A";
      |                    ^~~
      |                    |
      |                    const char*

Invalid conversion from ‘const char*’ to ‘char’: o "A" é um texto (tipo const char*, explicado em auto), e não cabe numa caixa de um caractere só.

E um caractere num std::string:

$ cat aspas2.cpp
#include <iostream>
#include <string>

int main() {
    std::string nome = 'A';
    std::cout << nome << "\n";
}
$ g++ -std=c++23 -Wall -Wextra aspas2.cpp -o aspas2
aspas2.cpp: In function ‘int main()’:
aspas2.cpp:5:24: error: conversion from ‘char’ to non-scalar type ‘std::string’ {aka ‘std::__cxx11::basic_string<char>’} requested

Conversion from ‘char’ to non-scalar type ‘std::string’ requested = "pediu-se uma conversão de char para o tipo std::string" (que o C++ não faz sozinho com =). O trecho entre chaves ({aka ‘std::__cxx11::basic_string<char>’}, "também conhecido como") é o nome completo do std::string dentro da biblioteca; pode ignorar. Conserto: std::string nome = "A";.

E várias letras entre aspas simples? O g++ aceita, com dois avisos, e guarda só a última:

$ cat aspas3.cpp
#include <iostream>

int main() {
    char inicial = 'Ana';
    std::cout << inicial << "\n";
}
$ g++ -std=c++23 -Wall -Wextra aspas3.cpp -o aspas3
aspas3.cpp:4:20: warning: multi-character character constant [-Wmultichar]
    4 |     char inicial = 'Ana';
      |                    ^~~~~
aspas3.cpp: In function ‘int main()’:
aspas3.cpp:4:20: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘4288097’ to ‘97’ [-Woverflow]
$ ./aspas3
a                        « sobrou só o último "a" (código 97)

Multi-character character constant = "constante de caractere com vários caracteres". O compilador juntou os códigos das três letras num número só (4288097) e, ao guardar num char, sobrou apenas o pedaço do último a, que vale 97. Guarde este aviso: ele volta na seção 6, com o ç.


3. Um pedaço da tabela ASCII

Este programa mostra o código de alguns caracteres. Ele usa uma lista e um laço que repete o cout para cada item (Arrays e for de intervalo, aulas futuras); aqui, só a saída importa:

#include <iostream>

int main() {
    const char exemplos[] = {' ', '!', '0', '1', '9', 'A', 'B', 'Z', 'a', 'b', 'z', '~'};
    for (char c : exemplos) {
        std::cout << "'" << c << "' = " << static_cast<int>(c) << "\n";
    }
}
$ g++ -std=c++23 -Wall -Wextra tabela.cpp -o tabela
$ ./tabela
' ' = 32
'!' = 33
'0' = 48
'1' = 49
'9' = 57
'A' = 65
'B' = 66
'Z' = 90
'a' = 97
'b' = 98
'z' = 122
'~' = 126
FaixaCaracteresCódigos
ControleInvisíveis: '\n' (nova linha) = 10, '\t' (tabulação) = 9…0 a 31
Espaço e pontuação' ' = 32, ! " # $ %…32 a 47
Algarismos'0' a '9'48 a 57
Maiúsculas'A' a 'Z'65 a 90
Minúsculas'a' a 'z'97 a 122
Último'~' = 126 (127 é um controle, "apagar")até 127

Três padrões que valem ouro:

  • As letras estão em ordem: 'B' é 'A' + 1, 'C' é 'A' + 2…
  • Minúscula = maiúscula + 32: 'a' (97) − 'A' (65) = 32.
  • Os algarismos também estão em ordem: '7' − '0' = 55 − 48 = 7.

A tabela ASCII só tem as letras do inglês: nada de á, ç, ã. Eles vêm de uma tabela maior, o Unicode (seção 6).


4. Contas com char

Como char é número, dá para fazer contas. Mas cuidado com o tipo do resultado:

#include <iostream>

int main() {
    char letra = 'a';
    std::cout << letra + 1 << "\n";
    char proxima = letra + 1;
    std::cout << proxima << "\n";
    std::cout << static_cast<char>('A' + 2) << "\n";
    std::cout << static_cast<char>('a' - 32) << "\n";
    char digito = '7';
    int valor = digito - '0';
    std::cout << "valor: " << valor << "\n";
    letra++;
    std::cout << letra << "\n";
}
$ g++ -std=c++23 -Wall -Wextra conta.cpp -o conta
$ ./conta
98                       « 'a' + 1 virou número!
b
C
A
valor: 7
b
LinhaSaídaPor quê
letra + 198Numa conta, o C++ promove o char para int (97 + 1 = 98, um int). O cout recebe um int e mostra número.
char proxima = letra + 1;bO 98 foi guardado numa caixa char; o cout mostra a letra.
static_cast<char>('A' + 2)CMesmo efeito, sem variável: converter o resultado de volta para char.
'a' - 32AMinúscula menos 32 = maiúscula.
digito - '0'7O truque clássico: transforma o caractere '7' no número 7.
letra++bO ++ soma 1 e a variável continua char (aula ++ e --).

Conta com letra funciona, mas o código fica difícil de ler (o que é esse 32?). Para trocar maiúsculas e minúsculas, prefira as funções da próxima seção.


5. As funções de <cctype>

O cabeçalho <cctype> ("c" de herdado do C, "ctype" de character type, tipo de caractere) tem funções prontas para perguntar e transformar caracteres:

#include <cctype>
#include <iostream>

int main() {
    char c = 'a';
    std::cout << std::toupper(c) << "\n";
    std::cout << static_cast<char>(std::toupper(c)) << "\n";
    std::cout << static_cast<char>(std::tolower('Q')) << "\n";
    std::cout << "isdigit('7'): " << std::isdigit('7') << "\n";
    std::cout << "isdigit('x'): " << std::isdigit('x') << "\n";
    std::cout << "isalpha('x'): " << std::isalpha('x') << "\n";
    std::cout << "isupper('x'): " << std::isupper('x') << "\n";
    std::cout << "isspace(' '): " << std::isspace(' ') << "\n";
    bool ehDigito = std::isdigit('7');
    std::cout << "como bool: " << ehDigito << "\n";
}
$ g++ -std=c++23 -Wall -Wextra cctype.cpp -o cctype
$ ./cctype
65                       « toupper devolve int: aparece o código, e não a letra
A
q
isdigit('7'): 1
isdigit('x'): 0
isalpha('x'): 1024       « "verdadeiro" pode vir como qualquer número diferente de 0
isupper('x'): 0
isspace(' '): 8192
como bool: 1

Duas surpresas, ambas porque essas funções vêm da linguagem C, que não tinha bool nem gostava de char como resultado:

  • std::toupper devolve um int, o código da letra. Para ver a letra, converta: static_cast<char>(std::toupper(c)).
  • As funções is… devolvem int: 0 quer dizer falso, e qualquer outro número quer dizer verdadeiro (1024, 8192…). Guardadas num bool, viram 1 (aula Booleanos). Num if, funcionam direto.
FunçãoPergunta ou fazExemplo
std::isdigit(c)É algarismo (0–9)?'7' → sim
std::isalpha(c)É letra (A–Z, a–z)?'x' → sim; '7' → não
std::isalnum(c)É letra ou algarismo?'x', '7' → sim; '!' → não
std::isupper(c) / std::islower(c)É maiúscula? / minúscula?'Q' → maiúscula
std::isspace(c)É espaço, tabulação ou nova linha?' ', '\n' → sim
std::ispunct(c)É pontuação?'!', '?' → sim
std::toupper(c)Devolve a maiúscula (como int)'a' → 65 ('A')
std::tolower(c)Devolve a minúscula (como int)'Q' → 113 ('q')

Essas funções só conhecem a tabela ASCII: std::toupper não transforma ç em Ç. Para textos inteiros (std::string), você vai aplicá-las letra por letra em Acessar Caracteres.

Missão: o programa deveria mostrar a resposta em maiúscula, Resposta: S, mas mostra Resposta: 83. Conserte a linha do cout.

#include <cctype>
#include <iostream>

int main() {
    char resposta = 's';
    std::cout << "Resposta: " << std::toupper(resposta) << "\n";
}
#include <cctype>
#include <iostream>

int main() {
    char resposta = 's';
    std::cout << "Resposta: " << static_cast<char>(std::toupper(resposta)) << "\n";
}

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

#include <iostream>

int main() {
    char letra = 'ç';
    std::cout << letra << "\n";
    std::cout << static_cast<int>(letra) << "\n";
}
$ g++ -std=c++23 -Wall -Wextra cedilha.cpp -o cedilha
cedilha.cpp:4:18: warning: multi-character character constant [-Wmultichar]
    4 |     char letra = 'ç';
      |                  ^~~
cedilha.cpp: In function ‘int main()’:
cedilha.cpp:4:18: warning: overflow in conversion from ‘int’ to ‘char’ changes value from ‘50087’ to ‘-89’ [-Woverflow]
$ ./cedilha
�                        « um pedaço de letra que o terminal não sabe desenhar
-89

O mesmo aviso do 'Ana': multi-character character constant. Mas ç é uma letra só! O que aconteceu?

  1. O arquivo .cpp está salvo em UTF-8, o jeito moderno de guardar texto, que tem todas as letras do mundo. No UTF-8, as letras do ASCII ocupam 1 byte, mas o ç ocupa 2 bytes (195 e 167).
  2. Para o compilador, 'ç' é como 'Ana': dois "caracteres" de 1 byte entre aspas simples. Ele os junta num número só: 195 × 256 + 167 = 50087.
  3. Um char tem 1 byte. Ao guardar 50087, sobra só o último byte, 167, que num char com sinal aparece como -89 (o overflow in conversion).
  4. Na hora de mostrar, o cout manda meio ç para o terminal, que desenha � ("não sei o que é isto").
No editor do guia é erro

O Clang é mais rígido e recusa o programa: error: character too large for enclosing character literal type ("caractere grande demais para o tipo de literal de caractere").

Como guardar letras acentuadas, então? Num std::string, entre aspas duplas: std::string letra = "ç";. O texto ocupa 2 bytes, e o terminal recebe o ç inteiro. Os detalhes do UTF-8, e o que acontece quando você conta as letras de "ação", estão na aula Acentos e UTF-8.


Caracteres especiais

Alguns caracteres não dá para digitar entre aspas simples, então têm um código com barra invertida, a sequência de escape:

EscritaCaractereCódigo
'\n'Nova linha10
'\t'Tabulação9
'\''A própria aspa simples39
'\\'A própria barra invertida92
'\0'O caractere nulo (marca o fim de textos no estilo C)0

Parecem dois caracteres, mas são um só. A lista completa está em Caracteres Especiais e Raw Strings.


Quando dá errado

error: invalid conversion from ‘const char*’ to ‘char’

O que significa: você pôs aspas duplas ("A") num char.

Como consertar: aspas simples para um caractere ('A'); ou mude a variável para std::string se é texto.

warning: multi-character character constant [-Wmultichar]

O que significa: entre aspas simples há mais de um byte: várias letras ('Ana') ou uma letra acentuada ('ç', 'é').

Como consertar: textos e letras acentuadas vão em std::string, com aspas duplas.

Apareceu um número (65, 98…) onde deveria aparecer uma letra

O que significa: a letra passou por uma conta ('a' + 1) ou por std::toupper, e virou int.

Como consertar: guarde num char ou converta: static_cast<char>(...).

A conta com um algarismo deu um número estranho (como 55 em vez de 7)

O que significa: você usou o caractere '7' (código 55) como se fosse o número 7.

Como consertar: subtraia '0': int valor = digito - '0';.


Referência rápida

EscritaO que faz
char c = 'A';Um caractere (aspas simples)
static_cast<int>(c)O código do caractere (65)
static_cast<char>(66)O caractere de um código (B)
c + 1Conta: resultado int
digito - '0'Caractere de algarismo → número
std::toupper(c), std::tolower(c)Maiúscula/minúscula (devolvem int; <cctype>)
std::isdigit(c), std::isalpha(c), std::isspace(c)Perguntas: 0 = não, outro número = sim
'\n', '\t', '\'', '\\'Caracteres especiais

Exercícios

1. Código da letra

Qual é o código ASCII de 'B'?

2. Algarismo para número

Complete para transformar o caractere '5' no número 5:

int n = '5' - ;

3. O que std::cout << 'a' + 1; mostra?

4. Qual destas linhas está certa?

5. Por que o g++ avisa multi-character character constant em char c = 'ç';?

Missão final: o dobro do algarismo

Missão: a Ana digitou o algarismo '7' num formulário, e o programa deveria mostrar o dobro, Dobro: 14. Mas mostra Dobro: 110. Por quê? (Dica: qual é o código de '7'?) Conserte a linha do valor.

#include <iostream>

int main() {
    char digito = '7';
    int valor = digito;
    std::cout << "Dobro: " << valor * 2 << "\n";
}
#include <iostream>

int main() {
    char digito = '7';
    int valor = digito - '0';
    std::cout << "Dobro: " << valor * 2 << "\n";
}

Resumo

  • char guarda um caractere, entre aspas simples: 'A'. Texto vai entre aspas duplas, em std::string.
  • Por dentro, um char é um número (tabela ASCII): 'A' = 65, 'a' = 97, '0' = 48, espaço = 32. static_cast<int>(c) mostra o código.
  • Numa conta, o char vira int: 'a' + 1 mostra 98. Guarde num char ou use static_cast<char>. digito - '0' dá o valor do algarismo.
  • <cctype>: std::toupper/std::tolower (devolvem int!) e as perguntas std::isdigit, std::isalpha, std::isspace… (0 = não; outro número = sim).
  • 'ç' ocupa 2 bytes em UTF-8 e não cabe num char: aviso multi-character character constant no g++, erro no Clang. Use std::string (aula Acentos e UTF-8).