Introduzione all'I/O sui dispositivi standard

 


In questa lezione introdurremo le caratteristiche principali dell'I/O in C++, limitandoci per il momento all'I/O in free-format sui dispositivi standard di input e di output.

Precisiamo che useremo una libreria (dichiarata nell'header-file: <iostream.h>) che è ormai "superata" dalla Libreria Standard (alcuni compilatori danno un messaggio di warning, avvisando che si sta usando una "deprecated" (?!) library). Tuttavia questa libreria è ancora integrata nello standard e ci sembra un buon approccio per introdurre l'argomento.

 


 

Dispositivi standard di I/O

 

In C++ (come in C) sono definiti i seguenti dispositivi standard di I/O (elenchiamo i tre principali):

stdin e stdout sono reindirizzabili a files nella linea di comando quando si lancia il programma eseguibile.

 


 

Oggetti globali di I/O

 

In C++ i dispositivi standard di I/O stdout, stderr e stdin sono "collegati" rispettivamente agli oggetti globali cout, cerr e cin.

Oggetto (definizione temporanea): variabile appartenente a un tipo astratto, non nativo del linguaggio.

Globale: visibile sempre e dappertutto.

Un oggetto globale é creato appena si lancia il programma, prima che venga eseguita la prima istruzione del main.

Per definire gli oggetti globali di I/O bisogna includere l'header-file: <iostream.h>.

 


 

Operatori di flusso di I/O

 

In C++ sono definiti gli operatori di flusso di I/O

<< (inserimento)

e

>> (estrazione)

i cui left-operand sono rispettivamente cout (oppure cerr, che non menzioneremo più, in quanto le sue proprietà sono identiche a quelle di cout) e cin.

Il compilatore distingue gli operatori di flusso da quelli di shift dei bit (identificati dagli stessi simboli) in base al contesto, cioè in base al tipo degli operandi.

 


 

Output tramite l'operatore di inserimento

 

In C++ un'operazione di output si identifica con un'operazione di inserimento nell'oggetto cout:

cout   <<   dato;

dove dato è una qualsiasi variabile o espressione di tipo nativo (oppure una stringa). L'istruzione significa: il "dato" viene "inserito" nell'oggetto cout (e da questo automaticamente trasferito su stdout).

A differenza dalla funzione printf non è necessario usare specificatori di formato, in quanto il tipo delle variabili è riconosciuto automaticamente (in realtà, come vedremo più avanti, esistono anche qui degli specificatori, detti "manipolatori di formato", ma servono soltanto quando la scrittura deve essere non in free-format).

Esempi:                       cout << "Scrive una stringa\n";
cout << Variabile_intera;
cout << Variabile_float;
ecc.....

In ogni operazione viene trasferito un solo dato per volta; per cui, se si devono scrivere più dati (specie se di tipo diverso), vanno fatte altrettante operazioni di inserimento, con istruzioni separate. Alternativamente, in una stessa istruzione si possono "impilare" più operazioni di inserimento una di seguito all'altra.

Esempio:                       cout << dato1 << dato2 << dato3;

equivale a:    

cout << dato1;   cout << dato2;   cout << dato3;

questo è possibile grazie al fatto che l'operatore << restituisce lo stesso oggetto del left-operand (cioè cout) e che l'associatività dell'operazione procede da sinistra a destra.

Una variabile di tipo char è scritta come carattere; per scriverla come numero occorre fare il casting.
Per esempio, l'istruzione:             cout << 'A' << " ha codice ascii: " << (int)'A' << "\n";
visualizza la frase: A ha codice ascii 65

 


 

Input tramite l'operatore di estrazione

 

In C++ un'operazione di input si identifica con un'operazione di estrazione dall'oggetto cin:

cin   >>  dato;

dove dato è un l-value di qualsiasi tipo nativo (oppure una variabile stringa). L'istruzione significa: il valore immesso da stdin (automaticamente trasferito in cin) viene "estratto" dall'oggetto cin e memorizzato nella variabile "dato".

Come le operazioni di inserimento, anche quelle di estrazione possono essere "impilate" una di seguito all'altra in un'unica istruzione.
Esempio: cin >> dato1 >> dato2 >> dato3; (i dati dato1, dato2, dato3 devono essere forniti nello stesso ordine).

Il programma interpreta la lettura di un dato come terminata se incontra un blank, un carattere di tabulazione o un ritorno a capo. Ne consegue che, se l'input è una stringa, non deve contenere blanks (né tabs) e non può essere spezzata in due righe. D'altra parte l'esistenza dei terminatori (blank, tab o CR) consente di immettere più dati nella stessa riga.

Casi particolari:

 


 

Memorizzazione dei dati introdotti da tastiera

 

Se stdin é associato, come di default, alla tastiera, la memorizzazione dei dati segue delle regole generali, che sono le stesse sia in C++ (lettura tramite l'oggetto cin) che in C (lettura tramite le funzioni di libreria):

Come si può notare, la presenza del buffer di input (molto utile peraltro per migliorare l'efficienza del programma) crea una specie di "asincronismo" fra operatore e programma, che può essere facilmente causa di errore: bisogna fare attenzione a fornire ogni volta esattamente il numero di dati richiesti.

  [p10]

 


 

Comportamento in caso di errore in lettura

 

Le operazioni di estrazione non restituiscono mai espliciti messaggi di errore, tuttavia,

Per accorgersi di un errore (e per porvi rimedio) bisogna utilizzare alcune proprietà dell'oggetto cin (di cui parleremo più avanti).

 


 

Torna all'Indice