C++ Tipos e Limites
Quantos bytes tem um int? Qual o maior número que cabe num short? Que tipo tem o literal 42ul? Esta página responde de uma vez, em tabelas. Todos os valores foram medidos na máquina da Ana (Linux x86-64, g++ 15.2) com sizeof e std::numeric_limits. Para aprender com calma, veja Tipos de Dados, Inteiros, float e double, char e bool.
- Bytes é o resultado de
sizeof(tipo). Um byte tem 8 bits. - O padrão do C++ só garante tamanhos mínimos (um
inttem pelo menos 16 bits, umlongpelo menos 32…). Os tamanhos exatos dependem do sistema: as tabelas valem para o Linux x86-64 (e quase todas para o macOS). As diferenças estão em Outros sistemas. - No Windows,
longtem 4 bytes, não 8. Se o número pode passar de 2 bilhões, uselong longoustd::int64_t. - O editor do guia gera WebAssembly de 32 bits: lá,
long, ponteiros esize_ttêm 4 bytes.
Atalhos: Inteiros · Caracteres e bool · Ponto flutuante · <cstdint> · Literais e sufixos · numeric_limits · Outros sistemas · Armadilhas
Para conferir no seu computador
#include <cstdint>
#include <iostream>
#include <limits>
int main() {
std::cout << "int: " << sizeof(int) << " bytes, de "
<< std::numeric_limits<int>::min() << " a "
<< std::numeric_limits<int>::max() << '\n';
std::cout << "long: " << sizeof(long) << " bytes\n";
std::cout << "int64_t até " << std::numeric_limits<std::int64_t>::max() << '\n';
std::cout << "double: " << std::numeric_limits<double>::digits10
<< " dígitos garantidos\n";
}
$ g++ -std=c++23 -Wall -Wextra tamanhos.cpp -o tamanhos $ ./tamanhos int: 4 bytes, de -2147483648 a 2147483647 long: 8 bytes int64_t até 9223372036854775807 double: 15 dígitos garantidos
Experimente: rode e compare com as tabelas. Aqui no editor (WebAssembly de 32 bits) o long deve aparecer com 4 bytes. Troque int por outros tipos para medir.
#include <cstdint>
#include <iostream>
#include <limits>
int main() {
std::cout << "int: " << sizeof(int) << " bytes, de "
<< std::numeric_limits<int>::min() << " a "
<< std::numeric_limits<int>::max() << '\n';
std::cout << "long: " << sizeof(long) << " bytes\n";
std::cout << "int64_t até " << std::numeric_limits<std::int64_t>::max() << '\n';
std::cout << "double: " << std::numeric_limits<double>::digits10
<< " dígitos garantidos\n";
}
Inteiros
Com sinal (signed) guardam negativos; sem sinal (unsigned) só zero e positivos, mas vão duas vezes mais longe. short = short int, long = long int, unsigned = unsigned int. Aula: Inteiros.
| Tipo | Bytes | Mínimo | Máximo | Literal |
|---|---|---|---|---|
short | 2 | −32768 | 32767 | (não tem) |
unsigned short | 2 | 0 | 65535 | (não tem) |
int | 4 | −2147483648 | 2147483647 | 42 |
unsigned | 4 | 0 | 4294967295 | 42u |
long | 8 (Windows: 4) | −9223372036854775808 | 9223372036854775807 | 42l |
unsigned long | 8 (Windows: 4) | 0 | 18446744073709551615 | 42ul |
long long | 8 | −9223372036854775808 | 9223372036854775807 | 42ll |
unsigned long long | 8 | 0 | 18446744073709551615 | 42ull |
std::size_t | 8 | 0 | 18446744073709551615 | 42uz C++23 |
std::ptrdiff_t | 8 | −9223372036854775808 | 9223372036854775807 | 42z C++23 |
Para ter uma ideia: o int vai até uns 2,1 bilhões; o long long, até uns 9,2 quintilhões (9,2 × 1018). std::size_t é o tipo que sizeof e .size() devolvem; std::ptrdiff_t, o da diferença entre dois ponteiros. No Linux x86-64, são unsigned long e long.
Caracteres e bool
Aulas: char, bool e Acentos e UTF-8.
| Tipo | Bytes | Faixa | Literal | Para quê |
|---|---|---|---|---|
bool | 1 | false (0), true (1) | true | Verdadeiro ou falso. |
char | 1 | −128 a 127 (com sinal no x86-64) | 'A' | Um byte de texto. Um é em UTF-8 ocupa dois char. |
signed char | 1 | −128 a 127 | (não tem) | Um número pequeno com sinal. |
unsigned char | 1 | 0 a 255 | (não tem) | Um byte "cru" (arquivos, rede, UTF-8). |
char8_t C++20 | 1 | 0 a 255 | u8'A' | Um byte de UTF-8, marcado como tal. |
char16_t | 2 | 0 a 65535 | u'A' | Uma unidade de UTF-16. |
char32_t | 4 | 0 a 4294967295 | U'é' | Um caractere Unicode inteiro (UTF-32). |
wchar_t | 4 (Windows: 2) | −2147483648 a 2147483647 | L'A' | "Caractere largo". Varia demais entre sistemas: evite. |
char, signed char e unsigned char são três tipos diferentes. Se o char puro tem sinal ou não depende da plataforma: tem no x86-64 e no macOS, não tem no Linux em ARM (Raspberry Pi, por exemplo). Por isso, para examinar bytes, use unsigned char.
Ponto flutuante
Aula: float e double. "Dígitos" é digits10: quantos dígitos decimais sempre sobrevivem à ida e volta.
| Tipo | Bytes | Dígitos | Menor positivo normal (min) | Máximo | Epsilon | Literal |
|---|---|---|---|---|---|---|
float | 4 | 6 | 1.1754944e-38 | 3.4028235e+38 | 1.1920929e-07 | 3.14f |
double | 8 | 15 | 2.2250738585072014e-308 | 1.7976931348623157e+308 | 2.220446049250313e-16 | 3.14 |
long double | 16 (Windows MSVC e macOS ARM: 8) | 18 | 3.3621031431120935063e-4932 | 1.189731495357231765e+4932 | 1.084202172485504434e-19 | 3.14L |
- O mais negativo é
lowest(), igual a −max()(por exemplo, −1.7976931348623157e+308). Atenção:min()de ponto flutuante é o menor positivo, não o mais negativo. - Epsilon é a distância entre 1 e o próximo número representável: a "resolução" do tipo perto de 1.
- Use
doublecomo padrão.floatsó quando a memória importa muito (gráficos, milhões de valores). - No x86-64 Linux, o
long doubleocupa 16 bytes, mas usa 80 bits de precisão (o resto é enchimento).
Nenhum deles guarda 0,1 exatamente. Com 20 casas (std::format("{:.20f}", …)): 0.1f → 0.10000000149011611938 e 0.1 → 0.10000000000000000555. E 0.1 + 0.2 == 0.3 é false: a soma dá 0.30000000000000004.
Inteiros de tamanho fixo: <cstdint>
Quando o tamanho exato importa (arquivos binários, rede, ou só para não depender do sistema), use os tipos de <cstdint>. Eles são apelidos para os tipos acima: no Linux x86-64, std::int64_t é long; no Windows, long long. O nome diz o tamanho em todo lugar.
| Tipo | Bytes | Mínimo | Máximo | Observação |
|---|---|---|---|---|
std::int8_t | 1 | −128 | 127 | É um signed char: o cout o mostra como letra (veja Armadilhas). |
std::uint8_t | 1 | 0 | 255 | É um unsigned char: mesmo cuidado. |
std::int16_t | 2 | −32768 | 32767 | |
std::uint16_t | 2 | 0 | 65535 | |
std::int32_t | 4 | −2147483648 | 2147483647 | |
std::uint32_t | 4 | 0 | 4294967295 | |
std::int64_t | 8 | −9223372036854775808 | 9223372036854775807 | Em qualquer sistema. Bom para dinheiro em centavos. |
std::uint64_t | 8 | 0 | 18446744073709551615 | |
std::intptr_t | 8 | igual a int64_t aqui | Inteiro do tamanho de um ponteiro (4 bytes no editor do guia). | |
std::intmax_t | 8 | igual a int64_t aqui | O maior inteiro com sinal do sistema. | |
std::int_least16_t | 2 | −32768 | 32767 | "Pelo menos 16 bits", o menor que der. |
std::int_fast16_t | 8 | igual a int64_t aqui | "Pelo menos 16 bits", o mais rápido. No Linux x86-64 o g++ escolheu 8 bytes; int_fast8_t ficou com 1. | |
Os nomes também existem sem o std:: (int32_t), herança do C; com <cstdint>, prefira escrever o std::.
Literais e sufixos
Um literal é um valor escrito direto no código. O tipo dele vem da forma e do sufixo (maiúsculo ou minúsculo dá no mesmo: 42UL = 42ul; prefira L maiúsculo, que não se confunde com 1). Todos os tipos desta tabela foram confirmados com static_assert no g++ 15.
| Literal | Tipo | Valor / observação |
|---|---|---|
42 | int | Decimal. |
3000000000 | long | Não cabe em int: o compilador sobe sozinho para o primeiro tipo que cabe. |
42u · 42l · 42ll | unsigned · long · long long | u = sem sinal, l = long, ll = long long. |
42ul · 42ull | unsigned long · unsigned long long | Combinações. |
42uz · 42z C++23 | std::size_t · std::ptrdiff_t | Útil em for (auto i = 0uz; i < v.size(); ++i). |
0x2A | int | Hexadecimal: 42. |
052 | int | Octal (zero na frente!): 42. Cuidado com 010, que vale 8. |
0b101010 C++14 | int | Binário: 42. |
1'000'000 C++14 | int | O apóstrofo separa dígitos, só para leitura: 1000000. |
3.14 · .5 · 1e3 | double | 1e3 = 1 × 10³ = 1000. |
3.14f | float | Sem o f, é double. |
3.14L | long double | |
0x1p4 C++17 | double | Hexadecimal de ponto flutuante: 1 × 2⁴ = 16. |
'A' | char | Aspas simples: um caractere. |
u8'A' · u'A' · U'A' · L'A' | char8_t · char16_t · char32_t · wchar_t | |
"oi" | const char[3] | Aspas duplas: um array de caracteres com um '\0' no fim (sizeof("oi") = 3). |
"oi"s | std::string | Precisa de using namespace std::literals;. Veja Strings. |
"oi"sv | std::string_view | Idem. Veja string_view. |
R"(C:\pasta)" | const char[…] | Raw string: as barras não são escapes. Veja Caracteres Especiais. |
true · false | bool | |
nullptr | std::nullptr_t | O "ponteiro nenhum". Veja nullptr. |
std::numeric_limits
De <limits>. Pergunte ao compilador em vez de decorar: std::numeric_limits<T>::membro. Os INT_MAX, LLONG_MIN… de <climits> são a versão antiga, do C.
| Membro | O que é | Exemplo | Resultado |
|---|---|---|---|
max() | O maior valor. | numeric_limits<int>::max() | 2147483647 |
min() | Inteiros: o menor. Ponto flutuante: o menor positivo normal. | numeric_limits<int>::min()numeric_limits<double>::min() | -21474836482.2250738585072014e-308 |
lowest() | O mais negativo, em qualquer tipo. | numeric_limits<double>::lowest() | -1.7976931348623157e+308 |
digits | Bits de valor (sem o de sinal). | numeric_limits<int>::digitsnumeric_limits<unsigned>::digits | 3132 |
digits10 | Dígitos decimais garantidos. | numeric_limits<float>::digits10 | 6 |
is_signed | Tem sinal? | numeric_limits<char>::is_signed | true (x86-64) |
epsilon() | Distância entre 1 e o próximo valor. | numeric_limits<double>::epsilon() | 2.220446049250313e-16 |
infinity() | Infinito. | numeric_limits<double>::infinity() | inf |
quiet_NaN() | "Não é um número" (Not a Number). | numeric_limits<double>::quiet_NaN() | nan |
denorm_min() | O menor positivo de todos (subnormal). | numeric_limits<double>::denorm_min() | 5e-324 |
Missão: usando std::numeric_limits (nada de escrever os números à mão), mostre o máximo de std::uint16_t e o de std::int16_t, um por linha. A saída deve ser:
65535 32767
#include <cstdint>
#include <iostream>
#include <limits>
int main() {
// seu código aqui
}
#include <cstdint>
#include <iostream>
#include <limits>
int main() {
std::cout << std::numeric_limits<std::uint16_t>::max() << '\n';
std::cout << std::numeric_limits<std::int16_t>::max() << '\n';
}
Outros sistemas
As diferenças que importam, conferidas com o clang++ 21 do laboratório apontado para cada sistema (--target=…, com static_assert):
| Tipo | Linux x86-64 | Windows (MSVC) | Windows (MinGW) | macOS ARM | Linux ARM | Editor do guia (wasm32) |
|---|---|---|---|---|---|---|
long | 8 | 4 | 4 | 8 | 8 | 4 |
ponteiro, size_t | 8 | 8 | 8 | 8 | 8 | 4 |
long double | 16 | 8 | 16 | 8 | 16 | 16 |
wchar_t | 4 | 2 | 2 | 4 | 4 | 4 |
char tem sinal? | sim | sim | sim | sim | não | sim |
short (2), int (4), long long (8), float (4) e double (8) são iguais em todos eles. Moral: para inteiros grandes, long long ou std::int64_t, nunca long.
Armadilhas
1. std::uint8_t sai como letra. Ele é um unsigned char, e o cout mostra char como caractere:
#include <cstdint>
#include <iostream>
int main() {
std::uint8_t idade = 65;
std::cout << "Idade: " << idade << '\n';
std::cout << "Idade: " << +idade << '\n';
std::cout << "Idade: " << static_cast<int>(idade) << '\n';
}
$ g++ -std=c++23 -Wall -Wextra uint8.cpp -o uint8 $ ./uint8 Idade: A Idade: 65 Idade: 65
65 é o código do A. O + na frente promove para int; o static_cast<int> diz o mesmo de forma explícita. O std::format("{}", idade) mostra 65.
2. Estouro: o valor não cabe.
#include <climits>
#include <iostream>
int main() {
int a = 3000000000;
int b = INT_MAX + 1;
unsigned c = -1;
short d = 70000;
std::cout << a << ' ' << b << ' ' << c << ' ' << d << '\n';
}
$ g++ -std=c++23 -Wall -Wextra estouro.cpp -o estouro
estouro.cpp: In function ‘int main()’:
estouro.cpp:6:21: warning: integer overflow in expression of type ‘int’ results in ‘-2147483648’ [-Woverflow]
6 | int b = INT_MAX + 1;
| ^
estouro.cpp:8:15: warning: overflow in conversion from ‘int’ to ‘short int’ changes value from ‘70000’ to ‘4464’ [-Woverflow]
8 | short d = 70000;
| ^~~~~
$ ./estouro
-1294967296 -2147483648 4294967295 4464
int a = 3000000000;virou −1294967296 sem aviso com-Wall -Wextra. Só a opção-Wconversionpega:warning: conversion from ‘long int’ to ‘int’ changes value from ‘3000000000’ to ‘-1294967296’ [-Wconversion].INT_MAX + 1cominté comportamento indefinido. Aqui, como é uma conta feita na compilação, o g++ avisou (-Woverflow); numa conta com variáveis, não avisaria.unsigned c = -1;vale 4294967295, sem aviso: emunsignedas contas "dão a volta" (aritmética módulo 2³²), e isso é definido. Do mesmo jeito,0u - 1dá 4294967295.short d = 70000;virou 4464 (70000 − 65536), com aviso.
3. Com chaves, o compilador recusa. A inicialização com {} não aceita conversões que perdem valor (narrowing, "estreitamento"):
int main() {
int a{3000000000};
return a;
}
$ g++ -std=c++23 -Wall -Wextra chaves.cpp -o chaves
chaves.cpp: In function ‘int main()’:
chaves.cpp:2:11: error: narrowing conversion of ‘3000000000’ from ‘long int’ to ‘int’ [-Wnarrowing]
2 | int a{3000000000};
| ^~~~~~~~~~
Um bom motivo para preferir int a{…};. Veja Variáveis e Conversão de Tipos.
4. Divisão de inteiros. 7 / 2 dá 3, e -7 / 2 dá -3 (corta para o lado do zero); -7 % 2 dá -1. Com um dos lados double, 7 / 2.0 dá 3.5. Veja Operadores Aritméticos.
5. Comparar com e sem sinal. -1 < 0u é false: o −1 é convertido para unsigned e vira 4294967295. Por isso o -Wall avisa em i < v.size() com int i (-Wsign-compare).