In questa puntata esploriamo i costrutti decisionali del C++, quelli che ci permettono di eseguire parti di codice solo se si verificano determinate condizioni: if, else, else if e switch case. Per compilare useremo anche un primo Makefile, visto che nella puntata precedente abbiamo imparato a scriverli.
Il costrutto if
Creiamo un file main.cpp. Includiamo iostream, dichiariamo la funzione main e, prima di tutto, una variabile. In C++ si dichiara scrivendo prima il tipo e poi il nome, con un’assegnazione di valore che è opzionale. Per ora ci basta sapere che int è il tipo intero. Una carrellata di tutti i tipi la faremo in uno dei prossimi video.
Il costrutto if valuta una condizione e, se è vera, esegue un ramo di codice:
#include <iostream>
int main() {
int a = 7;
if (a > 5) {
std::cout << "a e' maggiore di 5" << std::endl;
}
return 0;
}
Compilare con un makefile
Per compilare usiamo un Makefile, così facciamo fare il lavoro a make. Dichiariamo un po’ di variabili: il nome dell’eseguibile, la lista dei sorgenti, i flag per il compilatore (con -g per il debug) e il nome del compilatore. Poi la regola di default all, che ha come prerequisito l’eseguibile, e la regola che lo genera a partire dai sorgenti. Dentro la regola, $(CC) $(CFLAGS) -o $(PROG) $(SOURCES) significa “compila i sorgenti usando il file di uscita indicato”. Le righe dei comandi devono iniziare con un TAB, non con degli spazi (nel blocco qui sotto la riga del comando comincia appunto con un TAB). Eccolo, già completo della regola clean di cui parliamo fra poco:
# nome dell'eseguibile da generare
PROG = main
# lista dei sorgenti separati da spazi
SOURCES = main.cpp
# flag del compilatore (ad es. -g per il debug)
CFLAGS = -g
# nome del compilatore
CC = g++
# regola di default
all: $(PROG)
# regola per la compilazione dell'eseguibile
$(PROG): $(SOURCES)
$(CC) $(CFLAGS) -o $(PROG) $(SOURCES)
clean:
rm -f $(PROG)
Lanciamo make e poi il programma:
make
./main
L’output è:
a e' maggiore di 5
Attenzione: il Makefile e il sorgente devono stare nella stessa cartella, altrimenti make non trova niente (a me è successo, ed era esattamente questo il problema).
Il ramo else
Il costrutto if prevede anche un ramo che viene eseguito quando la condizione non è vera. Cambiamo il valore di a in 3 e aggiungiamo else:
#include <iostream>
int main() {
int a = 3;
if (a > 5) {
std::cout << "a e' maggiore di 5" << std::endl;
} else {
std::cout << "a e' minore o uguale a 5" << std::endl;
}
return 0;
}
Ricompiliamo. Attenzione: make ricompila solo se il sorgente è più recente dell’eseguibile, quindi ricordatevi di salvare il file. L’output è:
a e' minore o uguale a 5
Else if
La terza opzione è inserire un numero arbitrario di else if fra l’if e l’else. In questo caso l’else diventa l’opzione di default e tutte le altre condizioni vengono valutate come singoli if, nell’ordine. Con a uguale a 5 verrà eseguito il ramo centrale:
#include <iostream>
int main() {
int a = 5;
if (a == 5) {
std::cout << "a = 5" << std::endl;
} else if (a > 5) {
std::cout << "a e' maggiore di 5" << std::endl;
} else {
std::cout << "a e' minore di 5" << std::endl;
}
return 0;
}
L’output è:
a = 5
I rami else if possiamo metterne quanti ne vogliamo, a seconda di quanto è complicata la condizione da valutare.
Il target clean
Avrete notato che ogni volta devo cancellare l’eseguibile prima di ricompilare. Evitiamolo con un target clean nel Makefile, che è quello che avete già visto in fondo al file di prima:
clean:
rm -f $(PROG)
(anche qui la riga del comando comincia con un TAB). Da ora in poi, per essere sicuri di ripartire da zero, basta fare:
make clean && make
Il costrutto switch case
Quando gli else if si moltiplicano come funghi dopo un temporale, conviene usare un altro costrutto decisionale molto importante: lo switch case, che con opzioni multiple è molto più efficiente di una catena di if. Lo switch vuole la variabile da valutare, poi si scrive un case per ogni valore possibile. Ogni blocco case termina con un break, e possiamo aggiungere un caso default che viene eseguito quando nessuno degli altri è vero:
#include <iostream>
int main() {
int a = 5;
switch (a) {
case 1:
std::cout << "a = 1" << std::endl;
break;
case 5:
std::cout << "a = 5" << std::endl;
break;
case 10:
std::cout << "a = 10" << std::endl;
break;
default:
std::cout << "a non e' 1, 5 o 10" << std::endl;
}
return 0;
}
Provando con a uguale a 5 otteniamo:
a = 5
Se invece mettiamo a uguale a 11 (facendo make clean && make) scatta il ramo default:
a non e' 1, 5 o 10
Più case senza break
Una cosa interessante dello switch è che togliendo il break si possono sommare più casi: l’esecuzione prosegue nel case successivo finché non incontra un break. Qui, per qualunque valore da 1 a 4, viene eseguito il primo blocco che incontra:
#include <iostream>
int main() {
int a = 3;
switch (a) {
case 1:
case 2:
case 3:
case 4:
std::cout << "a e' minore di 5" << std::endl;
break;
case 5:
std::cout << "a = 5" << std::endl;
break;
default:
std::cout << "a e' maggiore di 5" << std::endl;
}
return 0;
}
Con a uguale a 3 l’output è:
a e' minore di 5
Per concludere
Ci sarebbe ancora qualcosina da dire, ma lo vedremo man mano che servirà. Per ora avete tutto quello che vi serve per sperimentare con i costrutti decisionali. Fate pratica anche con i Makefile, perché la prossima volta li complicheremo ancora un pochettino.
Link e risorse
- il video su YouTube
- esempi e comandi della puntata su GitHub
- if, cppreference — if e else
- switch, cppreference — switch case
- g++, opzioni di invocazione — la doc del compilatore
- make, man page — il comando make
Sostieni il canale
📚 Bibliografia:
C++ per principianti – https://amzn.to/3ZxyzAB
Acquistando dai link affiliati supporti il progetto The Linux Nerd senza costi aggiuntivi!
