Riepilogo
Finora ogni programma del corso è stato un solo blocco, main, in cui tutto si scrive dove si usa. Questa lezione insegna a dare un nome a una parte di un programma e a usarla senza guardarne l’interno: la funzione. Si studiano la dichiarazione, che annuncia al compilatore il nome, il tipo di ritorno e i parametri; la definizione, che aggiunge il corpo; e la chiamata, che trasferisce il controllo a quel corpo e riceve il valore restituito. Si distingue ciò che il compilatore controlla in compilazione (che ogni chiamata concordi con una dichiarazione) da ciò che accade in esecuzione (il salto alla definizione e il ritorno). Si spiega perché arrivare alla fine di una funzione che deve restituire un valore senza un return è comportamento indefinito. Si segue l’esecuzione con lo stack delle chiamate, che conserva un frame per ogni chiamata in corso. E si specifica ogni funzione con un contratto, la sua precondizione e la sua postcondizione, che dice che cosa la funzione esige da chi la chiama e che cosa garantisce in cambio.
Obiettivi di apprendimento
- Dichiarare, definire e chiamare funzioni con parametri e valore di ritorno, e indicare in un programma quale parte è la dichiarazione e quale la definizione.
- Seguire con un diagramma dello stack delle chiamate l’esecuzione di un programma con funzioni che si chiamano tra loro.
- Specificare una funzione con la sua precondizione e la sua postcondizione, e scrivere una chiamata che violi la precondizione.
- Spiegare perché una funzione che non restituisce un valore su qualche percorso è comportamento indefinito, e riconoscere l’avviso corrispondente del compilatore.
Perché le funzioni
I programmi dell’unità precedente risolvevano ogni problema dentro main, e ciò è bastato finché i problemi erano piccoli. Ora, appena un programma cresce, compaiono tre difficoltà. La prima è la ripetizione: se lo stesso calcolo serve in tre punti, lo si scrive tre volte, e un errore in esso si corregge tre volte (oppure ci si dimentica di correggerlo in uno dei tre). La seconda è la lettura: un blocco di cinquanta righe non dice che cosa fa, ma come lo fa, e il lettore deve ricostruire l’intenzione a partire dal meccanismo. La terza è il test: se tutto è mescolato, non c’è modo di controllare una parte senza eseguire il resto.
Una funzione è un frammento di programma dotato di nome, che riceve dati, detti parametri, e consegna un risultato, detto valore di ritorno. La matematica conosce l’idea da secoli: la distanza fra due punti del piano è una funzione delle loro quattro coordinate, , e chi calcola il perimetro di un triangolo non ripete la formula tre volte, ma le dà un nome e la applica tre volte. In C++ si scrive la stessa cosa.
Il programma legge le coordinate dei tre vertici di un triangolo e scrive il suo perimetro, che è la somma delle lunghezze dei suoi tre lati.
#include <cmath>#include <iostream>#include <print>// Distanza fra i punti (x1, y1) e (x2, y2).double distance(double x1, double y1, double x2, double y2){ const double dx{x2 - x1}; const double dy{y2 - y1}; return std::sqrt(dx * dx + dy * dy);}int main(){ double ax{}; double ay{}; double bx{}; double by{}; double cx{}; double cy{}; std::cin >> ax >> ay >> bx >> by >> cx >> cy; const double perimeter{distance(ax, ay, bx, by) + distance(bx, by, cx, cy) + distance(cx, cy, ax, ay)}; std::println("Perimetro: {}", perimeter);}0 0 3 0 0 4Perimetro: 12I lati del triangolo di vertici , e misurano 3, 5 e 4, e la loro somma è 12.
La funzione distance si scrive una volta e si usa tre; main si legge come la definizione del perimetro, «la somma di tre distanze»; e distance può essere testata da sola, con una coppia di punti di cui si conosce la distanza. Si guadagnano così le tre cose che mancavano. L’insegnamento generale, che vale in qualsiasi linguaggio, è che una funzione dà un nome a un’idea: chi la usa pensa a che cosa fa, non a come lo fa. Questa separazione fra l’uso e la realizzazione si chiama astrazione procedurale, ed è il tema di tutta l’unità.
Caricamento del contenuto…