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.

Como ler as tabelas
  • Bytes é o resultado de sizeof(tipo). Um byte tem 8 bits.
  • O padrão do C++ só garante tamanhos mínimos (um int tem pelo menos 16 bits, um long pelo 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, long tem 4 bytes, não 8. Se o número pode passar de 2 bilhões, use long long ou std::int64_t.
  • O editor do guia gera WebAssembly de 32 bits: lá, long, ponteiros e size_t tê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.

TipoBytesMínimoMáximoLiteral
short2−3276832767(não tem)
unsigned short2065535(não tem)
int4−2147483648214748364742
unsigned40429496729542u
long8 (Windows: 4)−9223372036854775808922337203685477580742l
unsigned long8 (Windows: 4)01844674407370955161542ul
long long8−9223372036854775808922337203685477580742ll
unsigned long long801844674407370955161542ull
std::size_t801844674407370955161542uz C++23
std::ptrdiff_t8−9223372036854775808922337203685477580742z 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.

TipoBytesFaixaLiteralPara quê
bool1false (0), true (1)trueVerdadeiro ou falso.
char1−128 a 127 (com sinal no x86-64)'A'Um byte de texto. Um é em UTF-8 ocupa dois char.
signed char1−128 a 127(não tem)Um número pequeno com sinal.
unsigned char10 a 255(não tem)Um byte "cru" (arquivos, rede, UTF-8).
char8_t C++2010 a 255u8'A'Um byte de UTF-8, marcado como tal.
char16_t20 a 65535u'A'Uma unidade de UTF-16.
char32_t40 a 4294967295U'é'Um caractere Unicode inteiro (UTF-32).
wchar_t4 (Windows: 2)−2147483648 a 2147483647L'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.

TipoBytesDígitosMenor positivo normal (min)MáximoEpsilonLiteral
float461.1754944e-383.4028235e+381.1920929e-073.14f
double8152.2250738585072014e-3081.7976931348623157e+3082.220446049250313e-163.14
long double16 (Windows MSVC e macOS ARM: 8)183.3621031431120935063e-49321.189731495357231765e+49321.084202172485504434e-193.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 double como padrão. float só quando a memória importa muito (gráficos, milhões de valores).
  • No x86-64 Linux, o long double ocupa 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.

TipoBytesMínimoMáximoObservação
std::int8_t1−128127É um signed char: o cout o mostra como letra (veja Armadilhas).
std::uint8_t10255É um unsigned char: mesmo cuidado.
std::int16_t2−3276832767
std::uint16_t2065535
std::int32_t4−21474836482147483647
std::uint32_t404294967295
std::int64_t8−92233720368547758089223372036854775807Em qualquer sistema. Bom para dinheiro em centavos.
std::uint64_t8018446744073709551615
std::intptr_t8igual a int64_t aquiInteiro do tamanho de um ponteiro (4 bytes no editor do guia).
std::intmax_t8igual a int64_t aquiO maior inteiro com sinal do sistema.
std::int_least16_t2−3276832767"Pelo menos 16 bits", o menor que der.
std::int_fast16_t8igual 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.

LiteralTipoValor / observação
42intDecimal.
3000000000longNão cabe em int: o compilador sobe sozinho para o primeiro tipo que cabe.
42u · 42l · 42llunsigned · long · long longu = sem sinal, l = long, ll = long long.
42ul · 42ullunsigned long · unsigned long longCombinações.
42uz · 42z C++23std::size_t · std::ptrdiff_tÚtil em for (auto i = 0uz; i < v.size(); ++i).
0x2AintHexadecimal: 42.
052intOctal (zero na frente!): 42. Cuidado com 010, que vale 8.
0b101010 C++14intBinário: 42.
1'000'000 C++14intO apóstrofo separa dígitos, só para leitura: 1000000.
3.14 · .5 · 1e3double1e3 = 1 × 10³ = 1000.
3.14ffloatSem o f, é double.
3.14Llong double
0x1p4 C++17doubleHexadecimal de ponto flutuante: 1 × 2⁴ = 16.
'A'charAspas 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"sstd::stringPrecisa de using namespace std::literals;. Veja Strings.
"oi"svstd::string_viewIdem. Veja string_view.
R"(C:\pasta)"const char[…]Raw string: as barras não são escapes. Veja Caracteres Especiais.
true · falsebool
nullptrstd::nullptr_tO "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.

MembroO que éExemploResultado
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()
-2147483648
2.2250738585072014e-308
lowest()O mais negativo, em qualquer tipo.numeric_limits<double>::lowest()-1.7976931348623157e+308
digitsBits de valor (sem o de sinal).numeric_limits<int>::digits
numeric_limits<unsigned>::digits
31
32
digits10Dígitos decimais garantidos.numeric_limits<float>::digits106
is_signedTem sinal?numeric_limits<char>::is_signedtrue (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):

TipoLinux x86-64Windows (MSVC)Windows (MinGW)macOS ARMLinux ARMEditor do guia (wasm32)
long844884
ponteiro, size_t888884
long double1681681616
wchar_t422444
char tem sinal?simsimsimsimnãosim

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 -Wconversion pega: warning: conversion from ‘long int’ to ‘int’ changes value from ‘3000000000’ to ‘-1294967296’ [-Wconversion].
  • INT_MAX + 1 com int é 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: em unsigned as contas "dão a volta" (aritmética módulo 2³²), e isso é definido. Do mesmo jeito, 0u - 1 dá 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).