Cos'è una variabile
Una variabile è un pezzo di memoria con un nome che contiene un valore. In C++ ogni variabile ha un tipo fisso, scelto quando la dichiari, e quel tipo non cambia mai. È questo che rende il C++ tipizzato staticamente: il compilatore conosce il tipo di ogni variabile prima che il programma venga eseguito e si rifiuta di compilare codice che memorizza il tipo di valore sbagliato.
Una dichiarazione ha tre parti: il tipo, il nome e (quasi sempre) un valore iniziale.
Leggi int age = 30; come "crea un int chiamato age e mettici dentro 30". Il punto e virgola chiude l'istruzione, proprio come la pagina sui commenti ha mostrato che fa ogni istruzione. Nota che isActive viene stampato come 1: di default bool si mostra come 1/0, un aspetto che la pagina sui tipi di dato approfondisce.
Inizializzazione e assegnazione
Si somigliano ma sono operazioni diverse, e la differenza è una delle idee più importanti di questa pagina.
L'inizializzazione dà a una variabile il suo primo valore come parte della dichiarazione. L'assegnazione cambia il valore di una variabile che esiste già.
Puoi dichiarare senza inizializzare e assegnare in seguito, ma fai attenzione:
Funziona perché abbiamo assegnato score prima di leggerla. La versione pericolosa è leggerla per prima, come vediamo adesso.
L'insidia della variabile non inizializzata
Questa è la classica trappola del C++ che in molti altri linguaggi non esiste. Leggere una variabile locale dichiarata ma a cui non è mai stato dato un valore è undefined behavior: la variabile contiene i byte che si trovavano per caso in quella memoria.
int score; // non inizializzata
std::cout << score; // undefined behavior: stampa spazzatura, oppure "funziona", oppure crasha
Il compilatore lo compilerà senza problemi. Potrebbe stampare 0, potrebbe stampare 32766, e potrebbe comportarsi diversamente a ogni esecuzione o su ogni macchina, il che rende questi bug un incubo da rintracciare. Due difese:
- Inizializza sempre nella dichiarazione.
int score = 0;non costa nulla ed elimina del tutto il problema. - Attiva gli avvisi. Compilare con
-Wall -Wextrafa sì che il compilatore segnali molte letture non inizializzate prima che ti creino problemi.
Preferisci la prima opzione per ogni variabile locale: dalle un valore iniziale sensato nel momento in cui la crei.
Stili di inizializzazione: =, () e {}
Il C++ ti offre alcuni modi per scrivere un valore iniziale. Per lo più fanno la stessa cosa, ma l'inizializzazione con le graffe ha una protezione in più che vale la pena conoscere.
Il motivo per preferire {} è che rifiuta le conversioni narrowing, cioè le assegnazioni che perderebbero dati in silenzio. Con =, un valore decimale viene troncato senza dire nulla; con {}, il compilatore ti ferma:
int x = 3.9; // compila: x diventa 3 in silenzio (lo .9 viene scartato)
int y{3.9}; // errore di compilazione: il narrowing da double a int non è permesso
Scoprirlo in fase di compilazione è esattamente ciò che vuoi. Un'abitudine moderna diffusa è usare {} di default per quel controllo in più, tornando a = solo quando l'inizializzazione per copia si legge in modo più naturale.
Regole e convenzioni per i nomi
Il C++ impone alcune regole rigide, poi tutti ci aggiungono sopra delle convenzioni. Le regole: un nome può contenere lettere, cifre e _; non può iniziare con una cifra; non può essere una parola chiave riservata (come int o return); ed è sensibile alle maiuscole (age e Age sono due variabili diverse). Evita i nomi che iniziano con un underscore seguito da una maiuscola, o che contengono due underscore di fila: sono riservati all'implementazione.
Le convenzioni seguite dalla maggior parte del codice C++:
- Le variabili usano
snake_caseocamelCase: scegline uno e resta coerente,item_countoppureitemCount. - I nomi devono descrivere il valore:
count, nonc;user_email, nonx.
I nomi chiari non sono decorazione: sono il modo in cui il te stesso del futuro leggerà il codice. total_price = item_count * price_per_item si spiega da solo in un modo in cui t = c * p non lo farà mai.
Lo scope delle variabili
Una variabile esiste solo dentro il blocco, le { ... }, in cui è dichiarata, e viene distrutta alla graffa di chiusura. Questo è il suo scope (ambito di visibilità). Una variabile dichiarata dentro un ciclo o un blocco if è invisibile all'esterno:
Sia i sia square appartengono al ciclo e spariscono quando termina; total è dichiarata nel blocco esterno, quindi sopravvive. Un blocco interno può anche oscurare (shadowing) un nome di un blocco esterno: una nuova variabile con lo stesso nome nasconde temporaneamente quella esterna finché il blocco interno non si chiude. È una fonte frequente di confusione, quindi evita di riutilizzare i nomi tra scope annidati.
In pratica: dichiara ogni variabile nel blocco più piccolo che ne ha bisogno, e inizializzala lì. Uno scope ristretto significa meno nomi che competono per la tua attenzione e meno occasioni di leggere un valore lontano da dove è stato impostato.
Prossimo: tipi di dato
Ogni variabile di questa pagina è partita da un tipo: int, double, string, bool. La prossima pagina analizza nel dettaglio i tipi di dato del C++: i tipi fondamentali e le loro dimensioni, interi con e senza segno, float e double, char, e come scegliere il tipo giusto per ogni compito.
Domande frequenti
Come si dichiara una variabile in C++?
Scrivi il tipo, poi un nome e, facoltativamente, un valore: int age = 30;. Il tipo (int) resta fisso per tutta la vita della variabile; il nome (age) è il modo in cui ti riferisci a essa. Puoi dichiararla senza valore, int age;, ma per una variabile locale questo significa che contiene spazzatura finché non le assegni qualcosa, quindi inizializzala sempre nel punto in cui la dichiari.
Qual è la differenza tra inizializzazione e assegnazione in C++?
L'inizializzazione dà a una variabile il suo primo valore come parte della dichiarazione: int x = 5; oppure int x{5};. L'assegnazione cambia il valore di una variabile che esiste già: x = 7;. La distinzione conta perché leggere una variabile locale dichiarata ma mai inizializzata è undefined behavior.
Cosa succede se usi una variabile non inizializzata in C++?
Leggere il valore di una variabile locale non inizializzata è undefined behavior (comportamento indefinito): la variabile contiene i byte che si trovavano già in quella memoria, quindi il programma potrebbe stampare un numero a caso, funzionare per fortuna o crashare. Il compilatore non ti ferma (anche se molti danno un avviso con -Wall), quindi la soluzione è dare sempre un valore alle variabili locali quando le dichiari.