Dal diagramma di flusso al programma. Dopo aver progettato algoritmi e pseudocodice, impariamo a rappresentare dati e operazioni nel linguaggio C: dalla scrittura del sorgente alla compilazione, dalle variabili ai tipi numerici, fino a espressioni, conversioni e verifica dei risultati. Gli esempi adottano C17 e sono pensati per la prima classe del Liceo Scientifico, opzione Scienze Applicate.

Ambiente di programmazione C con diagramma di flusso, variabili, codice sorgente, compilazione e risultato di esecuzione.
Dal diagramma di flusso al linguaggio C: variabili, operatori, compilazione ed esecuzione di un programma.

1. Dall’algoritmo al programma

Un problema computazionale definisce dati di partenza, risultati desiderati e vincoli. In particolare, un algoritmo è un procedimento finito, espresso mediante passi eseguibili, per risolvere il problema. Il programma ne rappresenta l’implementazione in un linguaggio di programmazione: deve rispettarne le regole sintattiche e il significato delle istruzioni.

Nell’unità precedente abbiamo studiato sequenze, selezioni, iterazioni e diagrammi. Ora, invece, ci concentriamo su come esprimere dati e calcoli in C. Le strutture condizionali, la lettura da tastiera e i cicli verranno sviluppati nelle prossime due unità.

Problema → specifica dei dati → algoritmo → codice sorgente C → programma eseguibile → test del risultato
Metodo scientifico. Un programma che si compila non è necessariamente corretto: occorre confrontare l’output con risultati attesi e verifiche indipendenti.

2. Linguaggi di programmazione e compilazione

Un linguaggio di programmazione consente di descrivere procedure con una sintassi precisa. Il codice C, infatti, è un testo leggibile da chi programma: non è direttamente una sequenza di istruzioni macchina eseguibili dalla CPU. Il compilatore traduce il sorgente (attraverso diverse fasi) in una rappresentazione adatta a essere collegata e trasformata in programma eseguibile.

Nel flusso usuale con GCC, il preprocessore gestisce direttive come #include; il compilatore traduce il codice preprocessato, l’assemblatore produce file oggetto e il linker collega gli oggetti con le librerie necessarie. Tuttavia, la sequenza precisa di strumenti e file può variare secondo piattaforma e ambiente.

Sorgente .c → preprocessing → compilazione → assemblaggio → file oggetto → linking → eseguibile

L’esecuzione è un’operazione distinta dalla compilazione: un eseguibile compilato può essere lanciato e provato più volte senza ricompilarlo se il codice non cambia.

3. Perché studiare il C e come distinguerlo dal C++

Il linguaggio C è usato in sistemi operativi, firmware, dispositivi embedded e software in cui è importante controllare risorse e rappresentazione dei dati. Tuttavia, è un linguaggio distinto da C++: hanno una storia comune e alcune sintassi simili, ma gli standard, le regole e le librerie non sono intercambiabili.

In questa lezione usiamo ISO C17 e la libreria standard C. Non compaiono cout, cin, iostream o riferimenti del C++. Inoltre, la presenza dell’estensione .c aiuta a usare la modalità C degli strumenti.

4. Ambiente di sviluppo: editor, IDE, GCC e debugger

Strumento A che cosa serve
Editor Scrive e modifica i file di testo contenenti il sorgente .c.
IDE Integra editor, comandi di costruzione, gestione dei file e talvolta debugger.
Compilatore Controlla molte regole del linguaggio e traduce il sorgente.
Linker Combina file oggetto e componenti necessari per ottenere l’eseguibile.
Debugger Consente di fermare l’esecuzione, osservare variabili e seguire istruzioni.

Un possibile ambiente didattico è un editor come Visual Studio Code con un compilatore GCC installato separatamente. Infatti, l’editor non è il compilatore: la colorazione del codice non prova la correttezza del programma.

Compilare da terminale

gcc -std=c17 -Wall -Wextra -Wpedantic -o primo primo.c

Il comando richiede lo standard C17, abilita diagnostiche utili e genera un programma chiamato primo (su Windows, in genere primo.exe). Sul terminale Linux/macOS si avvia solitamente con ./primo; in Windows, da PowerShell, con .\primo.exe. I nomi, i percorsi e la procedura di installazione, quindi, dipendono dal sistema.

Avvertenza. Gli avvisi del compilatore non coprono tutti gli errori logici e non garantiscono l’assenza di comportamenti non definiti.
Schema del processo di compilazione C: preprocessing, compilazione, assemblaggio, linking ed esecuzione del programma.
FIG01 — Le fasi di costruzione di un programma C: dal sorgente main.c al file eseguibile, attraverso preprocessore, compilatore, assemblatore e linker.

5. Anatomia del primo programma C

#include <stdio.h>

int main(void)
{
    printf("Ciao, MEGISTONE!\n");
    return 0;
}

Output previsto:

Ciao, MEGISTONE!

La riga #include <stdio.h> rende disponibili le dichiarazioni delle funzioni di ingresso/uscita standard, tra cui printf. La funzione main è il punto di ingresso del programma in un ambiente ospitato ordinario; int main(void) dichiara che non riceve parametri e restituisce un valore intero. Infine, le parentesi graffe delimitano il blocco di istruzioni.

La chiamata printf scrive un messaggio sullo standard output. La sequenza \n va a capo; il punto e virgola termina l’istruzione. return 0; segnala convenzionalmente il completamento con successo. In C17 il termine della funzione main senza un return esplicito equivale a restituire zero, ma nella didattica iniziale lo scriviamo per chiarezza.

Anatomia di un programma C17 con direttiva include, funzione main, istruzione printf, return 0 e risultato nel terminale.
FIG02 — Struttura del primo programma C: direttiva #include, funzione main, blocco di istruzioni, stampa con printf e valore di ritorno.

6. Sintassi, identificatori, parole riservate e commenti

Le lettere maiuscole e minuscole sono distinte: area e Area designano identificatori diversi. I nomi possono usare lettere, cifre e sottolineatura, ma non iniziare con una cifra; alcune forme con trattini bassi sono riservate all’implementazione. Non sono ammesse parole chiave come int come nomi di variabile.

Forma Valutazione Motivo
base Corretta Nome semplice e descrittivo.
altezza2 Corretta La cifra non è iniziale.
2altezza Errata Inizia con una cifra.
altezza totale Errata Contiene uno spazio.
int Errata È una parola riservata.
_Nome Da evitare Alcuni prefissi sono riservati: scegliere nomi ordinari.

I commenti // commento su una riga e /* commento su più righe */ documentano le scelte, ma non devono sostituire nomi comprensibili. Per esempio, preferire velocita_media a nomi generici come x1 quando il significato fisico conta.

7. Variabili: dichiarazione, inizializzazione e assegnazione

Una variabile è un oggetto identificato dal programma e caratterizzato da un tipo. La dichiarazione introduce il nome e il tipo (negli esempi locali mostrati definisce anche l’oggetto); l’inizializzazione fornisce il valore iniziale; l’assegnazione aggiorna il valore di una variabile già esistente. Un valore conservato in memoria non deve essere rappresentato come una scatola identica su ogni macchina: dimensione, allineamento e disposizione dipendono dal tipo e dall’implementazione.

int conteggio = 3;       // dichiarazione con inizializzazione
conteggio = 5;           // assegnazione
conteggio = conteggio + 2; // ora vale 7

In particolare, l’ultima riga non è un’uguaglianza matematica: l’espressione a destra viene calcolata usando il valore corrente, poi il risultato viene assegnato all’oggetto a sinistra. Inoltre, una variabile locale automatica dichiarata senza inizializzazione può avere un valore indeterminato: non va letta prima di assegnarle un valore valido.

Infografica sul linguaggio C con variabili, tipi char, int, float e double, rappresentazione binaria dei valori e modello didattico della memoria RAM.
FIG03 — Variabili, tipi e rappresentazione dei dati in C17: dichiarazione, inizializzazione, assegnazione e codifica binaria. Le dimensioni e le rappresentazioni illustrate si riferiscono a un’implementazione esemplificativa con ASCII, interi a 32 bit e formati IEEE 754 binary32 e binary64.

8. Tipi fondamentali e intervalli rappresentabili

Il tipo indica quali valori e quali operazioni sono appropriati. int rappresenta numeri interi; char è un tipo intero spesso impiegato per unità di carattere; float e double rappresentano numeri in virgola mobile, normalmente approssimati. I modificatori short, long, signed e unsigned definiscono ulteriori famiglie di tipi interi.

Tipo Uso tipico Attenzione
char Unità di carattere o piccolo intero Può essere con o senza segno, secondo l’implementazione.
int Conteggi e valori interi Non è garantito che abbia 32 bit.
unsigned int Interi non negativi L’aritmetica senza segno segue regole modulari.
float Misure approssimate Precisione finita.
double Misure con precisione normalmente maggiore Non rappresenta esattamente tutti i decimali.

Per conoscere i limiti del proprio ambiente si possono consultare <limits.h> e <float.h>. In C CHAR_BIT è almeno 8; int garantisce almeno l’intervallo −32767…32767, non necessariamente quello a 32 bit. Di conseguenza, i valori e le dimensioni effettive vanno misurati sulla piattaforma, senza universalizzarli.

#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void)
{
    printf("Bit per byte = %d\n", CHAR_BIT);
    printf("INT_MIN = %d; INT_MAX = %d\n", INT_MIN, INT_MAX);
    printf("Cifre decimali significative di double: %d\n", DBL_DIG);
    return 0;
}

9. Costanti, letterali e qualificatore const

Un letterale è un valore scritto direttamente nel codice: 7 è un letterale intero, 3.5 è normalmente un letterale double, 3.5f è un float, 'A' è una costante carattere di tipo int in C. Una variabile qualificata const non può essere modificata mediante il suo nome dopo l’inizializzazione: è utile per rappresentare un dato fisso.

const double pi_grezzo = 3.14159;
const int giorni_settimana = 7;

Tuttavia, la parola const non trasforma ogni numero in una costante di compilazione adatta a qualsiasi contesto: il suo effetto va distinto dalle espressioni costanti definite dallo standard.

10. Espressioni, assegnamento e operatori composti

Un’espressione combina valori, variabili e operatori producendo un risultato, talvolta con effetti collaterali. L’operatore = assegna un valore; +=, -=, *= e /= rappresentano forme composte di assegnamento.

int totale = 10;
totale += 4;  // 14
totale *= 2;  // 28

Inoltre, l’operando sinistro di un’assegnazione deve essere modificabile; non è possibile assegnare un nuovo valore a un letterale o a una variabile dichiarata const.

11. Operatori aritmetici, resto, incremento e decremento

Operatore Significato Esempio
+ Somma 4 + 3 → 7
- Sottrazione 4 - 3 → 1
* Moltiplicazione 4 * 3 → 12
/ Divisione 7 / 2 → 3 con operandi interi
% Resto tra interi 7 % 2 → 1
++ Incremento di uno n++;
-- Decremento di uno n--;

L’operatore % si applica a tipi interi; non serve per calcolare la percentuale. Il risultato di una divisione per zero non è valido in C; anche il resto con divisore zero è non definito. Per questo motivo, nell’introduzione a incremento e decremento useremo istruzioni separate, evitando di modificare più volte lo stesso oggetto in una singola espressione non sequenziata.

int n = 4;
n++;     // n vale 5
--n;     // n torna a 4

12. Divisione intera, promozioni e conversioni

Se entrambi gli operandi sono interi, la divisione produce un quoziente intero troncato verso zero. Se almeno uno è double e le usuali conversioni aritmetiche lo richiedono, l’operazione avviene in virgola mobile. Di conseguenza, assegnare il risultato della divisione intera a un double non recupera la parte frazionaria già perduta.

#include <stdio.h>

int main(void)
{
    int a = 7;
    int b = 2;
    double q1 = a / b;
    double q2 = (double)a / b;
    printf("q1 = %.1f\n", q1);
    printf("q2 = %.1f\n", q2);
    printf("resto = %d\n", a % b);
    return 0;
}

Output previsto:

q1 = 3.0
q2 = 3.5
resto = 1
7 / 2 = 3 (operandi interi); 7.0 / 2 = 3.5 (operazione reale); (double)7 / 2 = 3.5 (cast esplicito).

Una conversione può essere implicita, come nel passaggio da int a double, oppure esplicita mediante il cast (tipo)espressione. In ogni caso, ogni conversione va valutata rispetto all’intervallo e alla precisione del tipo di destinazione.

13. Operatori relazionali e logici

Gli operatori <, <=, >, >= confrontano valori; == e != controllano uguaglianza e disuguaglianza. Il risultato di un confronto C è un intero: 1 se vero, 0 se falso. Non confondere = (assegnamento) e == (confronto).

Gli operatori logici && (AND), || (OR) e ! (NOT) trattano zero come falso e qualunque valore diverso da zero come vero; producono 0 oppure 1. && e || valutano il secondo operando soltanto se necessario, con il cosiddetto cortocircuito.

#include <stdio.h>

int main(void)
{
    int eta = 16;
    int risultato = (eta >= 14) && (eta < 18);
    printf("Nella fascia: %d\n", risultato);
    return 0;
}

Output previsto:

Nella fascia: 1

Nell’Articolo 07 vedremo come le strutture if e switch utilizzano i risultati di questi confronti.

14. Precedenza, associatività e ordine di valutazione

La precedenza indica come sono raggruppati gli operatori in assenza di parentesi. L’associatività stabilisce il raggruppamento quando compaiono operatori della stessa precedenza. Tuttavia, nessuna delle due proprietà stabilisce in generale l’ordine temporale in cui vengono calcolati gli operandi.

2 + 3 * 4 = 14, mentre (2 + 3) * 4 = 20.
Gruppo essenziale Esempi Indicazione
Moltiplicativi * / % Si raggruppano prima di + -.
Additivi + - Seguono i moltiplicativi.
Confronto < <= > >= Precedono == !=.
Uguaglianza == != Non sono assegnamenti.
AND logico && Si raggruppa prima di OR.
OR logico || Si raggruppa dopo AND.
Assegnamento = += -= Ha precedenza più bassa delle operazioni qui elencate.
Attenzione: in f() + g() la precedenza non impone quale funzione venga chiamata prima. Scrivere istruzioni distinte quando l’ordine e gli effetti collaterali sono importanti.
Infografica degli operatori C17: aritmetici, relazionali, logici, assegnamento, precedenza, associatività e incremento.
FIG04 — Operatori ed espressioni nel linguaggio C: categorie, precedenza, associatività, valutazione delle espressioni e differenza tra incremento prefisso e postfisso.

15. Precisione numerica e comportamento del programma

I numeri in virgola mobile hanno una precisione finita: ad esempio il valore decimale 0,1 non è rappresentabile esattamente nel consueto formato binario in virgola mobile. Di conseguenza, alcuni risultati stampati possono essere approssimazioni; per confrontare misure è spesso necessario ragionare sulle tolleranze.

Lo standard distingue comportamenti definiti, aspetti dipendenti dall’implementazione (per esempio alcune proprietà dei tipi) e comportamenti non definiti, per i quali non è garantito alcun risultato. Tra gli esempi da evitare vi sono la divisione intera per zero e l’overflow di un intero con segno. Pertanto, non trattare un risultato osservato su un computer come una regola universale.

#include <stdio.h>

int main(void)
{
    double x = 0.1;
    printf("%.17f\n", x);
    return 0;
}

La stampa con molte cifre serve a osservare la rappresentazione adottata dall’ambiente; l’output preciso dipende dalle caratteristiche della piattaforma. È un esempio diagnostico, non un valore da memorizzare per tutte le macchine.


16. Errori frequenti e debugging elementare

Categoria Esempio Come intervenire
Errore sintattico Manca ; Leggere riga e contesto del messaggio diagnostico.
Errore di tipo / formato printf("%d", 2.5); Usare un formato coerente con l’argomento: %f per double.
Errore logico Calcolare il perimetro con base * altezza Verificare la formula prima del programma.
Variabile non inizializzata Usare un valore locale mai assegnato Inizializzare prima della lettura.
Divisione non valida 10 / 0 Verificare il divisore prima dell’operazione.
Errore di precisione Pretendere uguaglianza esatta fra decimali approssimati Adottare un criterio numerico appropriato.

Un metodo di debugging di base segue quattro passi: riprodurre l’errore, identificare il punto o l’ipotesi errata, correggere una causa alla volta e rieseguire i test. Inoltre, il debugger consente di osservare il contenuto di variabili e l’ordine delle istruzioni, ma la verifica matematica rimane indispensabile.

17. Dall’algoritmo ai programmi scientifici verificati

Esempio A — Area e perimetro di un rettangolo

Problema: calcolare area e perimetro di un rettangolo con base 8,0 cm e altezza 3,5 cm, entrambe positive. Input: due valori assegnati nel programma. Algoritmo: moltiplicare per l’area e sommare i lati per il perimetro. Verifica indipendente: 8 × 3,5 = 28 cm²; 2 × (8 + 3,5) = 23 cm.

A = b × h; P = 2 × (b + h).
#include <stdio.h>

int main(void)
{
    const double base = 8.0;
    const double altezza = 3.5;
    double area = base * altezza;
    double perimetro = 2.0 * (base + altezza);
    printf("Area = %.2f cm quadrati\n", area);
    printf("Perimetro = %.2f cm\n", perimetro);
    return 0;
}

Output previsto:

Area = 28.00 cm quadrati
Perimetro = 23.00 cm

Esempio B — Conversione di temperatura

Problema: convertire 25 °C in gradi Fahrenheit. Dati: temperatura in Celsius; nessun input interattivo. Algoritmo: moltiplicare per 9/5 e aggiungere 32. Verifica: 25 × 1,8 + 32 = 77 °F.

T°F = (9/5) × T°C + 32; nel codice utilizzare 9.0 / 5.0.
#include <stdio.h>

int main(void)
{
    const double celsius = 25.0;
    double fahrenheit = (9.0 / 5.0) * celsius + 32.0;
    printf("Temperatura = %.1f F\n", fahrenheit);
    return 0;
}

Output previsto:

Temperatura = 77.0 F

Esempio C — Velocità media

Problema: calcolare la velocità media scalare di un moto con distanza percorsa di 150 m in 12 s. Ipotesi: intervallo temporale strettamente positivo. Algoritmo: dividere distanza per durata. Verifica: 150/12 = 12,5 m/s. In particolare, la formula riguarda la distanza totale, non lo spostamento vettoriale.

vmedia = s / Δt, con Δt > 0.
#include <stdio.h>

int main(void)
{
    const double distanza = 150.0;
    const double tempo = 12.0;
    double velocita_media = distanza / tempo;
    printf("Velocita media = %.2f m/s\n", velocita_media);
    return 0;
}

Output previsto:

Velocita media = 12.50 m/s

Esempio D — Energia cinetica

Problema: calcolare l’energia cinetica traslazionale classica di un corpo con massa 2 kg e velocità 3 m/s. Ipotesi: meccanica classica, massa positiva, velocità espressa in m/s. Verifica: 0,5 × 2 × 3² = 9 J.

Ec = ½ m v².
#include <stdio.h>

int main(void)
{
    const double massa = 2.0;
    const double velocita = 3.0;
    double energia = 0.5 * massa * velocita * velocita;
    printf("Energia cinetica = %.2f J\n", energia);
    return 0;
}

Output previsto:

Energia cinetica = 9.00 J

Esempio E — Legge di Ohm

Problema: determinare la corrente in un resistore ohmico ideale con tensione applicata 12 V e resistenza 4 Ω. Ipotesi: resistenza positiva e comportamento ohmico nel regime considerato. Verifica: 12/4 = 3 A.

I = V / R, con R > 0.
#include <stdio.h>

int main(void)
{
    const double tensione = 12.0;
    const double resistenza = 4.0;
    double corrente = tensione / resistenza;
    printf("Corrente = %.2f A\n", corrente);
    return 0;
}

Output previsto:

Corrente = 3.00 A

Esempio F — Conversione delle unità

Problema: trasformare una distanza di 3,6 km in metri. Algoritmo: moltiplicare per 1000 m/km. Verifica: 3,6 × 1000 = 3600 m.

#include <stdio.h>

int main(void)
{
    const double km = 3.6;
    double metri = km * 1000.0;
    printf("Distanza = %.0f m\n", metri);
    return 0;
}

Output previsto:

Distanza = 3600 m

Buone pratiche

Tra le buone pratiche rientrano: scrivere nomi leggibili; esplicitare le unità di misura nei commenti e negli output; inizializzare le variabili; usare parentesi quando semplificano la lettura; non aggiungere cast senza motivo; compilare con avvisi attivi; controllare sempre il significato fisico e i casi limite. In questa unità, inoltre, i dati sono predefiniti, perché la validazione dell’input dell’utente verrà introdotta nelle successive lezioni.

18. Allenati con MEGI — 20 esercizi graduati

Prova a risolvere ogni esercizio prima di aprire la soluzione ragionata. In particolare, i primi otto verificano le basi, i successivi otto richiedono applicazione, gli ultimi quattro integrano programmazione e controllo degli errori.

Sorgente, compilazione e struttura del programma

  1. Che differenza esiste tra un file .c e un eseguibile?

    Mostra la soluzione ragionata

    Il file .c contiene il testo sorgente scritto da chi programma. L’eseguibile è un prodotto delle fasi di traduzione e collegamento, adatto alla piattaforma di destinazione. Un sorgente non è automaticamente eseguibile.

  2. Metti in ordine: linking, sorgente, file oggetto, compilazione, eseguibile.

    Mostra la soluzione ragionata

    Un percorso didattico semplificato è sorgente → compilazione (con preprocessing e assemblaggio) → file oggetto → linking → eseguibile. Sono passaggi distinti, anche quando un comando GCC li coordina.

  3. Nel programma con int main(void), che cosa significano int, void e return 0;?

    Mostra la soluzione ragionata

    int è il tipo di ritorno; void nella lista parametri indica che la funzione non riceve argomenti; return 0 segnala il completamento con successo all’ambiente ospitante.

  4. Quali nomi sono validi: totale, 3dati, dato_2, float?

    Mostra la soluzione ragionata

    totale e dato_2 sono identificatori validi. 3dati non può iniziare con una cifra; float è una parola chiave del C.

Variabili, tipi e istruzioni fondamentali

  1. Distingui dichiarazione, inizializzazione e assegnazione in int n = 4; n = 9;.

    Mostra la soluzione ragionata

    int n = 4 dichiara n di tipo int e lo inizializza a 4; la successiva istruzione n = 9 è un assegnamento che aggiorna il valore a 9.

  2. Scegli un tipo appropriato per un numero di studenti, una misura di massa e un carattere.

    Mostra la soluzione ragionata

    Un conteggio può usare int; una misura con parte frazionaria può usare double; una singola unità di carattere può usare char. La scelta reale dipende anche dall’intervallo richiesto e dalla codifica del testo.

  3. Quanto vale x dopo int x = 5; x = x + 3; x *= 2;?

    Mostra la soluzione ragionata

    Il valore passa da 5 a 8 e poi a 16. Ogni istruzione utilizza il valore prodotto dalla precedente.

  4. Quale messaggio produce printf("Valore: %d\n", 4);?

    Mostra la soluzione ragionata

    Produce Valore: 4 e poi un a capo. Il formato %d è coerente con il numero intero 4.

Operatori aritmetici, divisioni e conversioni

  1. Calcola 2 + 3 * 4 e (2 + 3) * 4.

    Mostra la soluzione ragionata

    Il primo dà 14 perché la moltiplicazione è raggruppata prima dell’addizione; il secondo dà 20 grazie alle parentesi.

  2. Con int a=17, b=5, quanto valgono a/b e a%b?

    Mostra la soluzione ragionata

    Il quoziente intero è 3 e il resto è 2, perché 17 = 5 × 3 + 2.

  3. Perché double q = 7 / 2; assegna 3,0 e non 3,5?

    Mostra la soluzione ragionata

    La divisione si svolge fra due int e produce prima 3. Solo successivamente 3 viene convertito in double, diventando 3.0.

  4. Correggi l’espressione per ottenere 3,5 da due interi a=7 e b=2.

    Mostra la soluzione ragionata

    Usare per esempio (double)a / b: il cast trasforma il primo operando in double e le conversioni usuali portano a una divisione in virgola mobile.

Confronti, logica e diagnostica

  1. Valuta 5 > 2, 5 == 2, 5 != 2.

    Mostra la soluzione ragionata

    I risultati interi C sono rispettivamente 1, 0 e 1. Il confronto di uguaglianza usa == e non =.

  2. Con a=1 e b=0, valuta a && b, a || b e !a.

    Mostra la soluzione ragionata

    AND = 0, OR = 1, NOT a = 0. Gli operatori logici producono sempre 0 oppure 1.

  3. Spiega la differenza fra precedenza e ordine di valutazione degli operandi.

    Mostra la soluzione ragionata

    La precedenza raggruppa sintatticamente gli operatori. L’ordine di valutazione riguarda quando gli operandi sono effettivamente calcolati e, in generale, non è fissato dalla precedenza.

  4. Trova due errori in int main(void) { int x = 3 printf("%f", x); return 0; }.

    Mostra la soluzione ragionata

    Manca il punto e virgola dopo int x = 3. Inoltre %f richiede un argomento double, mentre x è int: per stampare x va usato %d. Nel programma completo serve inoltre la direttiva #include <stdio.h> per ottenere la dichiarazione di printf.

Debugging e applicazioni scientifiche

  1. Perché int x; printf("%d", x); non è un esempio corretto?

    Mostra la soluzione ragionata

    La variabile locale automatica x non è stata inizializzata: la sua lettura non fornisce un valore affidabile e può comportare comportamento non definito. Inizializzare x prima della stampa, per esempio int x = 0;.

  2. Scrivi le istruzioni C per area e perimetro di un rettangolo di lati 6,0 cm e 2,5 cm.

    Mostra la soluzione ragionata

    Per esempio: const double b=6.0, h=2.5; double area=b*h; double perimetro=2.0*(b+h);. I risultati verificati sono 15,0 cm² e 17,0 cm. Il codice può inserirsi nel corpo di main.

  3. Scrivi l’espressione C che trasforma 20 °C in °F e calcola il risultato.

    Mostra la soluzione ragionata

    Con double c=20.0; double f=(9.0/5.0)*c+32.0; si ottiene 68,0 °F. Usare 9.0/5.0 rende esplicita la divisione in virgola mobile.

  4. Un programma usa double tensione=9.0, resistenza=0.0; double corrente=tensione/resistenza;. Qual è il problema e quale test serve?

    Mostra la soluzione ragionata

    La formula I=V/R richiede R>0 per l’esempio. Il dato R=0 viola la condizione prevista per questo modello e rende impossibile calcolare una corrente finita mediante I=V/R. In virgola mobile il comportamento concreto dipende dall’implementazione e dalle caratteristiche aritmetiche adottate: non bisogna supporre un risultato numerico ordinario. Il programma completo dovrà verificare il valore prima di dividere; il controllo condizionale sarà affrontato nell’Articolo 07. Non sostituire arbitrariamente R con un altro numero senza segnalarlo.

Infine, i prossimi passi del percorso sono input/output, condizioni e scelte (Articolo 07) e iterazioni, funzioni iniziali e problemi (Articolo 08). Il laboratorio STEM dedicato sarà collegato qui tramite il pulsante arancione 3D soltanto dopo pubblicazione, test delle modalità A–E e verifica dell’URL reale.

Riferimenti tecnici e didattici


Articoli simili