Por que os tipos importam em C++
Na página de variáveis você declarou valores com um tipo explícito como int age = 30;. Esse tipo não é apenas um rótulo - ele diz ao compilador quantos bytes reservar, como interpretar esses bytes e quais operações são válidas. Erre o tipo e você pode perder precisão silenciosamente, causar overflow ou invocar comportamento indefinido.
C++ agrupa seus tipos embutidos em algumas famílias: inteiros, números de ponto flutuante, um tipo de caractere e um booleano. Vamos olhar cada um e, depois, as regras que costumam pegar as pessoas de surpresa.
Os tipos fundamentais
Aqui está um de cada tipo central em um único programa. Note os sufixos dos literais (L, f, u) e as aspas simples em char:
Um bool é impresso como 1 ou 0 por padrão, não como true/false. Um char usa aspas simples - 'A' é um caractere, enquanto "A" (aspas duplas) é um literal de string, um tipo completamente diferente. Esses dois erros são extremamente comuns no começo.
Os tamanhos não são fixos
Esta é a maior surpresa para quem vem de linguagens como Java. O padrão do C++ só garante os tamanhos mínimos e uma ordem relativa (short ≤ int ≤ long ≤ long long). O tamanho real depende do seu compilador e da sua plataforma. Verifique sempre com sizeof:
Em uma build típica de Linux de 64 bits você verá int = 4, long = 8. No Windows de 64 bits, porém, long tem apenas 4 bytes. Essa diferença de portabilidade é exatamente o motivo pelo qual você não deve escrever código que presume que long é de 64 bits.
Quando precisar de uma largura exata, recorra aos tipos inteiros de largura fixa de <cstdint>:
Use int32_t/int64_t para formatos de arquivo, protocolos de rede ou qualquer coisa que precise se comportar de forma idêntica em todas as máquinas. Note o cast (int)a - enviar um tipo de 8 bits para o stream o imprime como caractere, não como número, então faça o cast primeiro.
Signed versus unsigned
Cada tipo inteiro vem em duas variantes. Um tipo signed pode conter negativos; um tipo unsigned não, trocando a faixa negativa por um máximo positivo maior. O int simples é signed por padrão.
Subtrair de um 0 sem sinal dá a volta para um número positivo enorme em vez de ficar negativo. Isso pega as pessoas o tempo todo - especialmente com size_t (um tipo unsigned) retornado por .size():
vector<int> v = {1, 2, 3};
// PERIGO: v.size() é unsigned. Se v estiver vazio, v.size() - 1 dá a volta
// para um número gigantesco e o laço roda quase para sempre.
for (size_t i = 0; i <= v.size() - 1; i++) { /* ... */ }
Prefira i < v.size() (nunca <= size() - 1), ou contorne todo o problema com um laço for baseado em intervalo.
Overflow de inteiros é comportamento indefinido
Ao contrário do giro dos unsigned (que é bem definido), o overflow de inteiros com sinal é comportamento indefinido em C++. O compilador pode fazer qualquer coisa - retornar lixo, eliminar a verificação ao otimizar ou travar:
A correção é a mesma armadilha de overflow de qualquer linguagem: faça a aritmética em um tipo mais largo. Faça o cast de um operando para long long antes do +, para que a adição aconteça em 64 bits. Fazer o cast do resultado depois é tarde demais - o overflow já aconteceu.
Escolhendo o tipo certo
Para a maior parte do código os padrões servem: int para números inteiros, double para decimais. Recorra a outra coisa apenas quando tiver um motivo.
| Tipo | Tamanho típico | Use quando |
|---|---|---|
int | 32 bits | O padrão para números inteiros |
long long | 64 bits | Valores acima de ~2 bilhões: timestamps, contadores grandes |
double | 64 bits | O padrão para decimais - boa precisão |
float | 32 bits | Arrays com pouca memória onde a precisão pode ceder |
bool | 1 byte | Uma flag true/false |
int32_t / int64_t | exato | Formatos multiplataforma, protocolos, manipulação de bits |
Algumas pegadinhas para ter em mente. float tem apenas cerca de 7 dígitos decimais significativos, então 0.1f + 0.2f não é exatamente 0.3 - prefira double a menos que realmente precise economizar memória. E char pode ser signed ou unsigned dependendo da plataforma, então se você fizer aritmética com bytes brutos, especifique signed char ou unsigned char.
A seguir: A palavra-chave auto
Escrever o tipo toda vez fica cansativo e, às vezes, o tipo é longo ou difícil de nomear. C++ deixa o compilador deduzi-lo para você com a palavra-chave auto - auto x = 42; faz x ser um int, e auto it = v.begin(); poupa você de digitar um tipo de iterador verboso. A próxima página cobre quando auto deixa o código mais claro e quando esconde demais.
Perguntas frequentes
Quais são os tipos de dados básicos em C++?
Os tipos fundamentais são os inteiros (short, int, long, long long), os de ponto flutuante (float, double, long double), o tipo de caractere char e o tipo booleano bool. Cada tipo inteiro também pode ser signed ou unsigned. Todo o resto - std::string, arrays, suas próprias classes - é construído em cima desses.
Qual é a diferença entre int e long em C++?
Ambos armazenam números inteiros, mas garante-se que long seja pelo menos tão largo quanto int (frequentemente 64 bits em plataformas de 64 bits, mas apenas 32 bits no Windows). O padrão só fixa os tamanhos mínimos, então para uma largura garantida use os tipos de largura fixa de <cstdint> como int32_t e int64_t.
Qual é o tamanho de um int em C++?
O padrão só garante que int tenha pelo menos 16 bits, mas em praticamente todo desktop e servidor moderno ele tem 32 bits. Como os tamanhos dependem da plataforma, nunca presuma - imprima sizeof(int) para verificar, ou use tipos de <cstdint> como int32_t quando precisar de uma largura exata.