Vai al contenuto
Topos Uranos

Riepilogo

Questa lezione apre la prima unità del corso, dedicata ai primi passi nella programmazione con C++. Spiega a che cosa serve l’unità: portare chi non ha mai programmato da un problema enunciato in linguaggio corrente fino a un programma che compila, si esegue, legge dati e scrive risultati, sapendo che cosa ha fatto ciascuno strumento lungo il cammino; presenta le sue sei lezioni e il filo che le unisce, dall’algoritmo scritto in italiano fino alla classificazione degli errori; precisa il poco che si dà per scontato, cioè l’aritmetica scolastica, l’abitudine di seguire istruzioni scritte e la gestione di file e cartelle; formula il problema che l’unità risolve, quello di convertire un calcolo che si sa fare a mano in un procedimento che una macchina esegue senza sbagliare e senza stancarsi; e mostra dove conduce, nelle unità successive e nel corso Programmazione in C++ II.

Obiettivi di apprendimento

  1. Descrivere la sequenza dei contenuti dell’unità e spiegare che cosa apporta ciascuna lezione alla successiva.
  2. Riconoscere le conoscenze preliminari che il corso presuppone (operare con interi e decimali, calcolare un quoziente e un resto, seguire istruzioni scritte, salvare un file in una cartella determinata) e ripassarle se occorre.
  3. Spiegare, con un esempio, il cammino che l’unità insegna a percorrere: dall’enunciato alla specifica, dalla specifica all’algoritmo e dall’algoritmo al programma che si compila e si esegue.
  4. Riconoscere che cosa ci si aspetta di saper fare al termine dell’unità, e distinguerlo da ciò che si imparerà nelle unità successive.

Valutazione d’ingresso

Prima di percorrere l’unità conviene misurare il punto di partenza. Questa valutazione è un’autovalutazione: non ha voto, e il suo unico scopo è orientare lo studio. Poiché questa è la prima unità del corso, non ci sono domande su unità precedenti; al loro posto, le prime tre verificano ciò che il corso dà per scontato: il quoziente e il resto di una divisione intera, l’ordine dei passi di un procedimento e il nome, l’estensione e la cartella di un file. Le sette successive toccano, in ordine, le idee centrali delle sei lezioni dell’unità. Chi non ha mai programmato non deve aspettarsi di indovinarle, e non importa: ogni risposta, giusta o no, spiega il punto e nomina la lezione che lo tratta. Le stesse abilità si misurano di nuovo alla fine, nella lezione di sintesi.

Valutazione d’ingresso

Prima di iniziare l’unità, misura il tuo punto di partenza. È un’autovalutazione breve: non riceve voto e il risultato resta solo nel tuo browser.

  1. Domanda 1Dalle unità precedentiSi distribuiscono 47 fogli fra 6 persone, in parti intere uguali, e quelli che non bastano per un giro completo si mettono da parte. Quanti ne riceve ciascuna persona e quanti se ne mettono da parte?

  2. Domanda 2Dalle unità precedentiPer inviare un file allegato per posta elettronica ci sono quattro passi: A, scrivere l’indirizzo del destinatario; B, premere «Invia»; C, aprire un messaggio nuovo; D, allegare il file. Qual è l’ordine adeguato?

  3. Domanda 3Dalle unità precedentiUn file è salvato in C:\Users\ana\programas\suma.cpp. Qual è il suo nome, quale la sua estensione e in quale cartella si trova?

  4. Domanda 4Una ricetta dice: «Se l’impasto è secco, aggiungere un cucchiaio d’acqua e impastare; ripetere finché non lo è più». L’impasto ha bisogno esattamente di tre cucchiai per smettere di essere secco. Quante volte si chiede se l’impasto è secco?

  5. Domanda 5Quale di queste istruzioni può seguirla chiunque, o una macchina, esattamente nello stesso modo?

  6. Domanda 6Un programma compila senza errori né avvisi, si esegue bene con molti dati, e con uno in particolare si arresta con il messaggio «division by zero». In quale momento si è prodotto l’errore?

  7. Domanda 7Si è installato Visual Studio Code con la sua estensione per C++, e chiedendo g++ --version nel terminale, questo risponde che non riconosce g++. Che cosa è più probabile?

  8. Domanda 8Il compilatore scrive un messaggio che comincia con warning: e, nonostante esso, produce l’eseguibile. Che cosa significa?

  9. Domanda 9Dove comincia l’esecuzione di un programma C++?

  10. Domanda 10Un programma dichiara int age{};, esegue std::cin >> age; per leggere un’età, e l’utente scrive abc. Che cosa succede?

I contenuti dell’unità

L’unità ha sei lezioni, che percorrono in ordine il cammino da un problema a un programma funzionante. Ciascuna presuppone la precedente, e la prima non presuppone nulla del linguaggio.

  • Dal problema all’algoritmo. Il corso comincia senza codice, perché l’algoritmo precede il programma. La lezione insegna a specificare un problema mediante il suo input, il suo output e la relazione fra i due (la precondizione e la postcondizione); a descrivere un algoritmo in passi numerati, senza ambiguità; a seguirlo a mano con una tabella di traccia; a riconoscervi la sequenza, la decisione e la ripetizione; e a giustificare che una ripetizione termina, con una quantità che decresce a ogni giro.
  • Programmi, compilatori e C++. Un algoritmo scritto per una macchina è un programma, e un programma C++ non si esegue così come si scrive: il preprocessore, il compilatore e il linker lo convertono in un eseguibile. La lezione percorre queste tre fasi, distingue ciò che si scopre compilando da ciò che si manifesta solo eseguendo, confronta il modello compilato con quello interpretato e colloca nel tempo gli standard del linguaggio, fino a C++23, quello che il corso usa, e C++26, la cui implementazione nei compilatori è in corso.
  • Installare l’ambiente su Windows, macOS e Linux. Tre guide complete, una per sistema, per installare un compilatore capace di C++23 e l’editor Visual Studio Code con la sua estensione per C++, verificare che tutto funzioni con un programma di prova e risolvere i malfunzionamenti frequenti: il compilatore che il terminale non trova, la versione vecchia e lo standard che non è stato richiesto.
  • Compilare, eseguire e configurare. Il ciclo di modificare, salvare, compilare, eseguire e leggere, dal terminale e dall’editor, con il comando del corso. La lezione distingue gli errori, che impediscono l’eseguibile, dagli avvisi, che segnalano codice sospetto; attiva i sanitizer (quello degli errori di memoria, AddressSanitizer, e quello del comportamento indefinito, UndefinedBehaviorSanitizer), che sorvegliano il programma mentre è in esecuzione; e spiega perché un output mostra simboli strani al posto degli accenti e della ñ.
  • Anatomia di un programma. Il programma minimo, pezzo per pezzo: la direttiva di inclusione e gli header, la funzione main e il codice di uscita, il namespace std, le istruzioni, i blocchi e i commenti. La lezione insegna a correggere gli errori di struttura leggendo i messaggi del compilatore a partire dal primo, e fissa la guida di stile del corso.
  • Input, output ed errori. Il programma comunica con l’esterno: scrive con std::print e std::println, con specifiche di formato per la larghezza e i decimali, e legge con std::cin. La lezione mostra che cosa succede quando l’input non ha la forma attesa e come si verifica la lettura, e chiude l’unità con la classificazione degli errori secondo il loro momento: di compilazione, di collegamento, di esecuzione e di logica.

Il filo che le unisce è quello del programma stesso. La prima lezione lo concepisce, la seconda spiega in che cosa si converte, la terza installa gli strumenti che lo convertono, la quarta insegna a usarli e ad ascoltare ciò che dicono, la quinta lo seziona e la sesta lo mette a dialogare con chi lo usa. Se è così, alla fine dell’unità lo studente sa ancora molto poco di C++, ma sa come ci si lavora, ed è questo ciò che il resto del corso presuppone.

Ciò che si dà per scontato

Il corso non presuppone alcuna esperienza precedente di programmazione. Presuppone, invece, tre abilità che conviene ripassare ora, se la valutazione d’ingresso ha mostrato qualche dubbio.

La prima è l’aritmetica scolastica: operare con gli interi e con i decimali, rispettare l’ordine delle operazioni (la moltiplicazione e la divisione prima dell’addizione e della sottrazione, salvo parentesi) e, soprattutto, calcolare il quoziente e il resto di una divisione intera. Quest’ultima è più importante di quanto sembri. Dividere 47 fogli fra 6 persone dà 7 a ciascuna e ne avanzano 5, il che si scrive 47=6⋅7+547 = 6 \cdot 7 + 5, con il resto 55 minore del divisore 66. C++ ha un operatore per il quoziente e un altro per il resto, e gran parte dei calcoli con gli interi (convertire secondi in ore, separare le cifre di un numero, decidere se un numero è pari) si riduce a essi. Chi voglia studiarla con calma la trova, con la sua dimostrazione, nel corso Algebra e Geometria I: Numeri, Funzioni e Geometria del Piano; qui basta saperla calcolare.

La seconda è l’abitudine di seguire alla lettera istruzioni scritte e di riconoscere quando un’istruzione non basta per sapere che cosa fare. Non si tratta di una conoscenza, ma di un atteggiamento: leggere «aggiungere sale a piacere» e accorgersi che quell’istruzione dipende da chi la esegue. La prima lezione dell’unità trasforma quell’atteggiamento in metodo.

La terza è l’uso di base di un computer: creare una cartella, salvarvi un file con un nome scelto, riconoscere l’estensione del nome (.cpp, .txt, .exe) e sapere in quale cartella si trova ciascun file. Conviene configurare fin da ora il sistema perché mostri le estensioni, che alcuni sistemi, come Windows, nascondono per impostazione predefinita: il codice sorgente suma.cpp e l’eseguibile suma.exe sono file molto diversi, e confonderli è una fonte di errori assurdi. L’unità insegna anche a usare il terminale, sicché non occorre saperlo fare in anticipo; basta essere disposti a scrivere comandi invece di premere pulsanti. Installare programmi richiede, infine, di poterlo fare sul proprio computer; la lezione di installazione indica un’alternativa in linea per chi non possa.

Non si presuppone l’inglese. I messaggi del compilatore sono in inglese, e il corso insegna a leggerli campo per campo, in modo che ciò che conta in essi (il file, la riga, il tipo di difetto) si capisca senza padroneggiare la lingua. Né si presuppone alcuna matematica oltre quella scolastica: gli esempi usano la divisione, le medie, le tabelline, le date e le ore.

Il problema che l’unità risolve

Un calcolo che si sa fare a mano non è ancora un programma. La distanza fra i due è ciò che l’unità insegna a percorrere, e un problema semplice permette di misurarla.

EsempioUna durata in ore, minuti e secondi

Un cronometro registra la durata di ciascuna attività come un numero di secondi, per esempio 7384, e la si vuole scrivere nella forma abituale di ore, minuti e secondi, con i minuti e i secondi sempre su due cifre: 2:03:04. A mano, il calcolo è una catena di divisioni: 7384=3600⋅2+1847384 = 3600 \cdot 2 + 184, da cui risultano 2 ore e 184 secondi; 184=60⋅3+4184 = 60 \cdot 3 + 4, da cui risultano 3 minuti e 4 secondi. Per una durata si fa in un minuto. Per le mille durate di un registro, invece, il calcolo a mano è lungo e soggetto a errori, e il compito si presta a essere eseguito da una macchina.

Ora, una macchina non può ricevere la spiegazione precedente. Riceve un testo in un linguaggio di programmazione, e per scrivere quel testo bisogna aver risolto prima tre questioni che il calcolo a mano lasciava implicite. La prima è la specifica: che cosa entra (un numero intero di secondi, non negativo), che cosa esce (tre interi, le ore, i minuti e i secondi) e quale relazione li unisce: se tt è il totale, t=3600h+60m+st = 3600 h + 60 m + s, con 0≤m<600 \leq m < 60 e 0≤s<600 \leq s < 60. La seconda è l’algoritmo: i passi precisi che, per qualsiasi input ammissibile, producono quell’output.

  1. hh assume il valore del quoziente di tt per 36003600.
  2. mm assume il valore del quoziente per 6060 del resto di tt per 36003600.
  3. ss assume il valore del resto di tt per 6060.
  4. Scrivere hh, due punti, mm su due cifre, due punti e ss su due cifre.

La terza è la scrittura dell’algoritmo in C++, con la forma esatta che il linguaggio esige, e la sua traduzione in un eseguibile con gli strumenti adeguati. Il programma seguente lo fa. Contiene molte cose che non sono ancora state spiegate: le variabili (lezione 2.1), gli operatori / e %, che danno il quoziente e il resto della divisione intera (lezione 3.1), la lettura con std::cin e le specifiche di formato (lezione 1.6); la specifica {:02} di questo programma, che scrive un numero su due posizioni completando con zeri a sinistra, è una variante della larghezza che quella lezione insegna e che la lezione 3.1 userà. Per ora basta riconoscervi i quattro passi dell’algoritmo.

Programma in C++
#include <iostream>#include <print>​int main(){    int total{};  // le variabili saranno spiegate nella lezione 2.1    std::print("Durata in secondi: ");    std::cin >> total;    const int hours{total / 3600};    const int minutes{total % 3600 / 60};    const int seconds{total % 60};    std::println("{} s = {}:{:02}:{:02}", total, hours, minutes, seconds);}
Input del programma
7384
Output
Durata in secondi: 7384 s = 2:03:04

Le righe da 9 a 11 sono i passi da 1 a 3, e la riga 12 è il passo 4. Che il risultato coincida con quello calcolato a mano non è un caso, ma la verifica che ogni programma merita: testarlo prima con un dato di cui si conosce la risposta.

Il programma risolve il problema, ma l’unità insegna anche a diffidarne. Se l’utente scrive abc invece di un numero, la lettura fallisce, total resta a zero e il programma scrive, senza alcun segnale d’allarme, che la durata è 0:00:00: una conclusione falsa con l’apparenza di una vera. Se scrive una durata negativa, la specifica non dice che cosa debba succedere, e il risultato è privo di senso. Se si scrive total / 360 invece di total / 3600, il programma compila, si esegue e dà un risultato sbagliato, e nessuno strumento se ne accorge. Ciascuno di questi difetti appartiene a un tipo diverso di errore, che si scopre in un momento diverso e si cerca in un modo diverso; l’ultima lezione dell’unità li classifica. Ne segue che il problema che l’unità risolve non è solo quello di scrivere questo programma, ma quello di sapere perché funziona, con quali dati smette di funzionare e quale strumento avviserà, o no, di ciascun difetto.

Pertanto, al termine dell’unità lo studente potrà scrivere, compilare ed eseguire programmi di questo tipo: pochi dati in input, alcuni calcoli in sequenza e alcuni risultati scritti con formato. Non potrà ancora prendere decisioni né ripetere istruzioni in C++ (anche se sì in un algoritmo scritto in italiano), né scegliere con criterio il tipo di ciascun dato; è ciò che portano le unità successive.

Dove conduce

L’unità è la soglia del corso. Ciò che vi si usa senza spiegarlo si spiega dopo, in quest’ordine. L’unità 2, «Dati e tipi», spiega le variabili e come si conservano in memoria, i limiti degli interi (e perché un calcolo corretto può andare in overflow), l’arrotondamento dei numeri con decimali e le conversioni fra tipi. L’unità 3, «Espressioni e operatori», spiega l’aritmetica di C++, con il quoziente e il resto dei negativi, la logica delle condizioni e ciò che lo standard lascia indefinito in un’espressione. L’unità 4, «Controllo del flusso», porta in C++ le decisioni e le ripetizioni che la prima lezione scrive in italiano, e insegna a giustificare che un ciclo è corretto e termina. L’unità 5 scompone i programmi in funzioni; la 6 introduce i dati composti (array, vettori, tabelle e record), con gli algoritmi di ricerca e di ordinamento e il loro costo; la 7 tratta il testo, l’input robusto e i file; e l’8, i programmi di più file, i test sistematici e il debugger.

Dopo il corso, Programmazione in C++ II prosegue con ciò che questo lascia fuori di proposito: i puntatori e la memoria dinamica, le classi con metodi, i template propri, le funzioni anonime e le eccezioni. Nessuno di questi temi è necessario per risolvere i problemi di questo corso, e rimandarli permette di concentrarsi su ciò che vale in qualsiasi linguaggio: specificare, progettare l’algoritmo, scriverlo con chiarezza e verificare che è corretto. La lezione di sintesi di questa unità raccoglierà proprio questa distinzione, fra ciò che è proprio di C++ e ciò che si porterà in qualsiasi altro linguaggio.