Menu

Tipi di dato in C++: int, double, char, bool e altri

Una panoramica pratica dei tipi di dato fondamentali del C++, cioè interi, virgola mobile, char e bool, più dimensioni, signed e unsigned, letterali e suffissi, overflow e come scegliere il tipo giusto.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Perché i tipi contano in C++

Quando dichiari un valore con un tipo esplicito come int age = 30;, quel tipo non è solo un'etichetta: dice al compilatore quanti byte riservare, come interpretarli e quali operazioni sono consentite. Se sbagli tipo puoi perdere precisione in silenzio, andare in overflow o provocare un comportamento indefinito.

Il C++ raggruppa i suoi tipi predefiniti in alcune famiglie: interi, numeri a virgola mobile, un tipo carattere e un booleano. Vediamoli uno per uno, poi passiamo alle regole che mettono in difficoltà.

I tipi fondamentali

Ecco un esempio di ogni tipo principale in un unico programma. Nota i suffissi dei letterali (L, f, u) e gli apici singoli per char:

Di default un bool si stampa come 1 o 0, non come true/false. Un char usa gli apici singoli: 'A' è un carattere, mentre "A" (virgolette doppie) è un letterale stringa, un tipo completamente diverso. All'inizio questi due errori sono comunissimi.

Le dimensioni non sono fisse

È la sorpresa più grande per chi arriva da linguaggi come Java. Lo standard C++ garantisce solo dimensioni minime e un ordine relativo (short ≤ int ≤ long ≤ long long). La dimensione effettiva dipende dal compilatore e dalla piattaforma. Verificala sempre con sizeof:

In una tipica compilazione Linux a 64 bit vedrai int = 4, long = 8. Su Windows a 64 bit, però, long è di soli 4 byte. Questa differenza di portabilità è proprio il motivo per cui non dovresti scrivere codice che presume che long sia a 64 bit.

Quando ti serve una larghezza esatta, usa i tipi interi a larghezza fissa di <cstdint>:

Usa int32_t/int64_t per formati di file, protocolli di rete o qualsiasi cosa debba comportarsi in modo identico su macchine diverse. Nota il cast (int)a: inviare a uno stream un tipo a 8 bit lo stampa come carattere, non come numero, quindi fai prima il cast.

Signed e unsigned

Ogni tipo intero esiste in due varianti. Un tipo signed può contenere numeri negativi; un tipo unsigned no, e in cambio dell'intervallo negativo ottiene un massimo positivo più alto. Un semplice int è signed di default.

Sottrarre da uno 0 unsigned fa ripartire il valore da un numero positivo enorme invece di andare in negativo. Questo colpisce di continuo, soprattutto con size_t (un tipo unsigned) restituito da .size():

vector<int> v = {1, 2, 3};
// PERICOLO: v.size() è unsigned. Se v è vuoto, v.size() - 1 ricomincia
// da un numero gigantesco e il ciclo gira quasi all'infinito.
for (size_t i = 0; i <= v.size() - 1; i++) { /* ... */ }

Preferisci i < v.size() (mai <= size() - 1), oppure evita del tutto il problema con un ciclo for basato su range.

L'overflow degli interi è comportamento indefinito

A differenza del ritorno a capo degli unsigned (che è ben definito), l'overflow degli interi signed è comportamento indefinito in C++. Il compilatore può fare qualsiasi cosa: restituire spazzatura, eliminare il controllo durante l'ottimizzazione o far crashare il programma.

La soluzione è la stessa della trappola dell'overflow in qualsiasi linguaggio: fai i calcoli in un tipo più largo. Converti un operando in long long prima del +, così la somma avviene a 64 bit. Convertire il risultato dopo è troppo tardi: l'overflow è già avvenuto.

Scegliere il tipo giusto

Per la maggior parte del codice i default vanno benissimo: int per i numeri interi, double per i decimali. Passa a qualcos'altro solo quando hai un motivo.

TipoDimensione tipicaQuando usarlo
int32 bitIl default per i numeri interi
long long64 bitValori oltre i 2 miliardi circa: timestamp, contatori grandi
double64 bitIl default per i decimali, buona precisione
float32 bitArray in cui la memoria è scarsa e si può sacrificare precisione
bool1 byteUn flag vero/falso
int32_t / int64_tesattaFormati multipiattaforma, protocolli, manipolazione di bit

Un paio di insidie da tenere a mente. float ha solo circa 7 cifre decimali significative, quindi 0.1f + 0.2f non è esattamente 0.3: preferisci double a meno che tu non debba davvero risparmiare memoria. E char può essere signed o unsigned a seconda della piattaforma, quindi se fai calcoli su byte grezzi scrivi esplicitamente signed char o unsigned char.

Prossimo: La parola chiave auto

Scrivere il tipo ogni volta diventa noioso, e a volte il tipo è lungo o difficile da nominare. Il C++ permette al compilatore di dedurlo per te con la parola chiave auto: auto x = 42; rende x un int, e auto it = v.begin(); ti evita di scrivere un tipo di iteratore prolisso. La prossima pagina spiega quando auto rende il codice più chiaro e quando nasconde troppo.

Domande frequenti

Quali sono i tipi di dato di base in C++?

I tipi fondamentali sono gli interi (short, int, long, long long), i tipi a virgola mobile (float, double, long double), il tipo carattere char e il tipo booleano bool. Ogni tipo intero può anche essere signed o unsigned. Tutto il resto, come std::string, gli array e le tue classi, è costruito sopra questi tipi.

Qual è la differenza tra int e long in C++?

Entrambi memorizzano numeri interi, ma long è garantito largo almeno quanto int (spesso 64 bit sulle piattaforme a 64 bit, ma solo 32 bit su Windows). Lo standard fissa solo le dimensioni minime, quindi per una larghezza garantita usa i tipi a larghezza fissa di <cstdint> come int32_t e int64_t.

Quanto è grande un int in C++?

Lo standard garantisce solo che int sia almeno di 16 bit, ma praticamente su ogni desktop e server moderno è di 32 bit. Dato che le dimensioni dipendono dalla piattaforma, non dare mai nulla per scontato: stampa sizeof(int) per verificarlo, oppure usa i tipi di <cstdint> come int32_t quando ti serve una larghezza esatta.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA