4  Livello 2

Il presente capitolo analizza i meccanismi di interazione tra il programma e l’ambiente esterno attraverso i canali di input e output standard. Viene approfondito l’utilizzo della funzione scanf() per l’acquisizione di dati da tastiera, dettagliando il ruolo cruciale dell’operatore di indirizzo & nel contesto del passaggio dei parametri per valore e le dinamiche hardware legate alle violazioni di memoria del tipo Segmentation Fault.

4.1 Istruzioni di input e output

Un programma necessita di interagire con l’ambiente esterno per acquisire dati da elaborare e per mostrare i risultati delle elaborazioni. In C, questa interazione avviene principalmente attraverso i canali standard di comunicazione (standard I/O), gestiti dalle funzioni della libreria di sistema <stdio.h>.

  • Standard Output, printf(): la funzione utilizzata per inviare flussi di dati in uscita dal programma verso lo schermo del computer. Permette di visualizzare stringhe di testo e i valori memorizzati nelle variabili, formattandoli secondo le specifiche richieste dal programmatore.

  • Standard Input, scanf(): la funzione deputata alla lettura dei dati immessi dall’utente, tipicamente tramite la tastiera. Arresta l’esecuzione del programma, interpreta i caratteri digitati e provvede a memorizzarli direttamente all’interno delle celle di memoria riservate alle variabili.

Abbiamo avuto modo di vedere il funzionamento dell’istruzione printf() in precedenza. Adesso, per incrementare ulteriormente il livello di competenze, è essenziale analizzare l’altra funzione di base: la scanf(). Sebbene il suo utilizzo presenti alcune complessità che verranno esaminate in seguito, essa rappresenta il primo strumento per implementare l’interazione dell’utente con il programma. Nello specifico, consente di acquisire un input esterno leggendo le informazioni inserite tramite la tastiera:

scanf("%d", &base);

Questa istruzione acquisisce un valore numerico dallo standard input (tastiera) e lo memorizza nella variabile base. Il segnaposto %d impone alla funzione di interpretare la sequenza di caratteri digitati dall’utente come un numero intero decimale. Con &base si ricava l’indirizzo di memoria della variabile base.

Vediamo un primo esercizio.

ConsiglioEsercizio 1

Scrivere un programma in C che permetta di calcolare l’area di un rettangolo. La base e l’altezza devono essere letti da tastiera.

#include <stdio.h> 
int main(void) {
  int base, altezza;
  printf("\nInserisci la base: ");
  scanf("%d", &base);
  printf("\nInserisci l'altezza: ");
  scanf("%d", &altezza);
  printf("\nL'area vale: %d",base * altezza);
  return 0;
}

Questo programma acquisisce in input da tastiera due numeri interi (base e altezza) e calcola l’area di un rettangolo. La sintassi della scanf presenta analogie con quella della printf, in quanto richiede di specificare il formato del dato atteso (%d in questo caso, trattandosi di un valore intero).

È tuttavia fondamentale notare la presenza del simbolo & anteposto al nome della variabile. Questo operatore, come già anticipato, definisce l’indirizzo di memoria della variabile. Il motivo di questa sintassi risiede nel fatto che nel linguaggio C il passaggio degli argomenti alle funzioni avviene esclusivamente per valore. Di conseguenza, se si desidera che una funzione (come la scanf) modifichi il contenuto di una variabile e non lavori su una sua copia, è necessario passarle direttamente l’indirizzo di memoria della variabile stessa, indicandole così in quale cella scrivere il dato acquisito. Questo concetto verrà approfondito nei capitoli dedicati alle funzioni e ai puntatori.

Importante

Cosa succede se non uso il simbolo &? Se non utilizzo il simbolo & il programma si comporta in modo anomalo. Il compilatore di norma non blocca la generazione dell’eseguibile, ma l’esecuzione del programma produce un comportamento anomalo che, nella quasi totalità dei casi, si traduce nel crash dell’applicazione. Perché? In sostanza si passa il valore memorizzato nella variabile e non il suo indirizzo ma scanf usa quel valore come se fosse un indirizzo e tenta di scrivere nella cella di memoria corrispondente. Il sistema operativo interviene e termina il programma per violazione di memoria (Segmentation Fault). Ricorda che un programma utente standard ha il permesso di leggere e scrivere esclusivamente all’interno dell’area di memoria (lo spazio di indirizzamento) che il sistema operativo gli ha riservato all’avvio.

NotaValidazione dell’input

Un programma robusto dovrebbe validare l’input che riceve dall’esterno. Validare l’input significa verificarne il tipo, l’intervallo e la dimensione prima di utilizzarlo. In C questo controllo non avviene mai in automatico. Le conseguenze di una validazione assente o scorretta vanno dal comportamento imprevisto del programma fino a gravi vulnerabilità di sicurezza. Non è possibile trattare questi temi in modo completo in un corso di fondamenti, per ora è importante sapere che il problema esiste ed è reale.