Il linguaggio C Input e output I flussi di I/O L’uso di buffer Apertura e chiusura di file Lettura e scrittura di dati La selezione di un metodo di I/O I/O non bufferizzato L’accesso diretto a file Input e output 1 2 Input / Output Introduzione La gestione dell’I/O su file è uno degli aspetti più complessi dei linguaggi di programmazione, perché strettamente correlato con le particolari modalità di accesso a file ed alle periferiche dettate dal sistema operativo difficoltà nella progettazione di servizi di I/O portabili Storicamente, l’apposita libreria di run−time del C e la libreria di I/O di UNIX erano parzialmente sovrapposte: tuttavia, la libreria C tratta l’I/O bufferizzato bufferizzato, contrariamente alla libreria UNIX Le funzioni ANSI di I/O sono tutte bufferizzate (con possibilità di modifica della dimensione del buffer) Le funzioni ANSI di I/O operano inoltre una distinzione tra accesso a file in modalità binaria o testuale; in ambiente UNIX la distinzione è poco significativa poiché UNIX tratta file binari e file di testo alla stessa stregua come sequenze di byte (in altri sistemi operativi, tale distinzione è invece importante) Spesso un programma deve acquisire dati da una sorgente esterna o inviare informazioni a una destinazione esterna. L’informazione può essere di ogni tipo: caratteri, immagini, suoni, ecc. 3 4 Stream di Input / Output Stream Input / Output Per acquisire dati, un programma apre uno stream (o flusso) associato alla sorgente di informazione (file, socket, memoria, ecc.) e legge l’informazione sequenzialmente da esso. In modo analogo, un programma può inviare verso una destinazione esterna dei dati aprendo uno stream associato alla destinazione e scrivendovi in modo sequenziale i dati. Indipendentemente dall’origine e dalla destinazione dei dati e dal loro tipo, gli algoritmi per leggere e scrivere sequenzialmente dei dati sono gli stessi. 5 I flussi di I/O − 1 Input / Output Lettura Apertura dello stream Finché e’ presente informazione lettura dell’informazione Chiusura dello stream 6 Il linguaggio C non distingue tra periferiche e file su disco: in entrambi i casi le operazioni di I/O sono realizzate attraverso flussi o stream di I/O associati a file o periferiche Un flusso di I/O è una sequenza ordinata di byte: la lettura e la scrittura di un file o di una periferica implicano la lettura/scrittura di dati da/su un flusso Scrittura Apertura dello stream Finché e’ presente informazione scrittura dell’informazione Chiusura dello stream Sorgente C Flusso File I flussi di I/O I/O:: i programmi C accedono ai dati memorizzati in file attraverso array monodimensionali di caratteri, detti flussi di I/O 7 8 I flussi di I/O − 2 I flussi di I/O − 3 Per eseguire l’I/O, è necessario associare un flusso ad un file o a una periferica occorre dichiarare un puntatore alla struttura FILE La struttura FILE FILE, definita in stdio. stdio.h, è costituita da campi che contengono informazioni quali il nome del file, la modalità di accesso, il puntatore al prossimo carattere nel flusso I campi della struttura FILE FILE, istanziati all’atto dell’apertura di un flusso (o in fase di utilizzo), sono dipendenti dall’implementazione e differiscono per sistemi operativi diversi Le strutture FILE forniscono al sistema operativo le informazioni necessarie per la gestione dei file: l’unico meccanismo di accesso ad un flusso è il puntatore alla struttura FILE FILE, detto puntatore a file Il puntatore a file deve essere dichiarato nel programma, contiene l’identificatore del flusso restituito da una chiamata alla fopen(), e viene utilizzato per leggere, scrivere e chiudere il flusso Ciascun programma può aprire più flussi simultaneamente, nel rispetto dei limiti imposti dalla particolare implementazione FILE *fopen(char *filename, char *mode); 9 I flussi di I/O − 4 10 I flussi standard Uno dei campi della struttura FILE è un indicatore di posizione nel file file, che punta al successivo byte da leggere o scrivere: a fronte di operazioni di lettura/scrittura, il sistema operativo modifica conseguentemente l’indicatore di posizione L’indicatore di posizione del file non può essere manipolato direttamente (in maniera portabile), ma può essere letto e modificato tramite funzioni di libreria, permettendo accessi al flusso non sequenziali Attenzione il puntatore a file identifica un flusso aperto, che è Attenzione: connesso ad un file o a una periferica, l’indicatore di posizione nel file identifica uno specifico byte all’interno di un flusso 11 Esistono tre flussi standard, standard che vengono aperti stdin, stdout stdout, stderr automaticamente per ogni programma: stdin I flussi standard sono connessi al terminale, per default, ma molti sistemi operativi ne permettono la redirezione (ad es., è possibile inviare messaggi di errore ad un file per effettuare diagnostica) Le funzioni printf() e scanf() utilizzano i flussi standard di I/O; possono essere utilizzate anche per fare I/O su/da file, ridirigendo stdin e stdout a file per mezzo della funzione freopen() Tuttavia, esistono le funzioni di libreria apposite, fprintf() e fscanf(), che permettono di specificare il flusso su cui operare 12 L’uso del buffer − 1 I formati testo e binario Confrontate con la memoria centrale, le unità di memoria di massa sono estremamente lente: nei programmi, il tempo richiesto per accedere alle periferiche eccede largamente il tempo impiegato dalla CPU per i calcoli effettivi È di fondamentale importanza ridurre il numero di operazioni fisiche di lettura/scrittura, mediante tecniche di bufferizzazione Un buffer è un’area dei memoria in cui i dati sono memorizzati temporaneamente, prima di essere inviati a destinazione Mediante l’uso di buffer, il sistema operativo può limitare il numero di accessi alla memoria di massa Tutti i sistemi operativi utilizzano buffer per leggere/scrivere su unità di I/O: l’accesso avviene con “granularità di blocco”, con blocchi di dimensione 512/4096 byte I dati possono essere acceduti in modalità testo o binaria binaria: Un flusso testuale è composto da una sequenza di linee, concluse da newline (sistemi operativi diversi possono memorizzare linee con formati diversi, utilizzando un carattere differente di terminazione linea) I flussi standard sono testuali In formato binario il compilatore non effettua alcuna interpretazione dei byte: i bit sono letti e scritti come un flusso continuo I flussi binari sono utilizzati quando è fondamentale preservare l’esatto contenuto del file 13 L’uso del buffer − 2 14 L’uso del buffer − 3 Le librerie standard di I/O del C comprendono un gestore di buffer che mantiene il buffer in memoria il più a lungo possibile: se si accede alla stessa porzione di un flusso più volte, si ha alta probabilità che il flusso sia stato mantenuto nella memoria centrale (si possono verificare problemi di accesso concorrente, gestibili via sistema operativo, se il file è condiviso da più processi) Sia nel caso di bufferizzazione a righe che a blocchi, è possibile richiedere esplicitamente al sistema operativo di forzare l’invio del buffer a destinazione in un momento qualsiasi, per mezzo della funzione fflush() Il C consente di personalizzare il meccanismo di bufferizzazione (modificando le dimensioni del buffer) fino ad eliminarla, ponendo la dimensione del buffer a zero Le librerie di run−time del C contengono un ulteriore livello di bufferizzazione che può assumere due forme distinte: bufferizzazione a linee e bufferizzazione a blocchi Nella bufferizzazione a linee, il sistema immagazzina i caratteri fino a quando si incontra un newline (o il buffer è pieno), inviando l’intera linea al sistema operativo (ciò che accade per l’inserimento da tastiera) Nella bufferizzazione a blocchi, il sistema immagazzina i caratteri fino a riempire un blocco, trasferendolo quindi al sistema operativo Tutti i flussi di I/O a file utilizzano una bufferizzazione a blocchi, i flussi riferiti a terminale sono dipendenti dal sistema operativo, ma sono o non bufferizzati o bufferizzati a linee 15 16 Apertura e chiusura di file − 1 Il file header <stdio.h> Prima di poter accedere al contenuto di un file, è necessario aprirlo tramite la funzione fopen(), che prevede due parametri: il nome del file e la modalità di accesso Esistono due insiemi di modalità di accesso, per i flussi testuali e per i flussi binari Per utilizzare le funzioni di I/O è necessario includere il file header stdio stdio..h che contiene: Le dichiarazioni dei prototipi di tutte le funzioni di I/O La dichiarazione della struttura FILE Le macro costanti, come stdin stdin, stdout stdout, stderr stderr, EOF EOF corrisponde al valore restituito dalle funzioni di I/O in corrispondenza dell’identificatore di fine file Nota La definizione di NULL Nota: NULL, per l’ANSI C, è invece stddef..h contenuta nel file stddef #ifndef NULL #define NULL (void *) 0 #endif Possibilità di lettura del flusso Possibilità di scrittura sul flusso Possibilità di scrittura solo alla fine r+ + w+ + * * * * * “a a” Apre un file testuale esistente in modalità append ; la scrittura può avvenire solo alla fine del file; se il file non esiste verrà creato automaticamente, in caso contrario il contenuto del file preesistente verrà mantenuto “rr+” Apre un file testuale esistente in lettura e scrittura, posizionandosi all’inizio del file; se il file non esiste, la funzione ritornerà il codice di errore NULL “w w+ ” Crea un nuovo file testuale e lo apre in lettura e scrittura “a a+” Apre un file testuale esistente o ne apre uno nuovo in modalità append; la lettura può avvenire in una posizione qualsiasi del file, la scrittura solo alla fine 18 FILE *open_test() { /* restituisce un puntatore ad una struttura FILE */ FILE *fp; a+ + * * Il file preesistente viene reinizializzato Crea un nuovo file testuale e lo apre in scrittura, posizionandosi all’inizio del file; se il file esiste, i dati precedenti vengono eliminati Esempio: Funzione che apre un file testuale, denominato test, Esempio test con accesso in lettura Note La funzione fprintf() è #include <stdio.h> analoga alla printf(), #include <stddef.h> Proprietà di file e flussi rispetto alle modalità di apertura della fopen() * “w w” Apertura e chiusura di file − 3 Le modalità binarie differiscono per l’aggiunta di una b (es., “rb rb”) La funzione fopen() restituisce un puntatore a file, utilizzabile per accedere successivamente al file aperto Il file deve esistere prima dell’apertura Apre un file testuale esistente in lettura, posizionandosi all’inizio del file; se il file non esiste, la funzione ritornerà il codice di errore NULL 17 Apertura e chiusura di file − 2 r w a “rr” * * * * fp = fopen(“test”, “r”); if (fp == NULL) fprintf(stderr, “Errore nell’apertura del file test\n”); return fp; * * } * 19 eccettuato un parametro aggiuntivo, che identifica il flusso di uscita La funzione open_test() è poco flessibile, perché permette l’apertura del solo file test e soltanto per accessi in lettura 20 Apertura e chiusura di file − 4 Apertura e chiusura di file − 5 Per chiudere un file viene utilizzata la funzione fclose(), con argomento il puntatore alla struttura FILE da chiudere La chiusura del file provoca il rilascio della struttura FILE, FILE per la successiva allocazione ad altri file La chiusura del file forza anche la scrittura del contenuto del buffer associato al flusso Dato che tutti i sistemi operativi stabiliscono un numero massimo di flussi aperti contemporaneamente, è buona norma chiudere i file quando se ne è conclusa l’elaborazione Tutti i flussi aperti vengono comunque chiusi dal sistema operativo quando il programma termina correttamente (nel caso di terminazione anomala, il comportamento non è standardizzato) #include <stdio.h> #include <stddef.h> FILE *open_file(file_name, access_mode) char *file_name, *access_mode; { FILE *fp; if ((fp = fopen(file_name, access_mode)) == NULL) fprintf(stderr, “Errore nell’apertura del file %s \ con modalità di accesso %s\n”, file_name, access_mode); return fp; } La funzione open_file() è equivalente alla fopen(), a meno della notifica di errore se il file non può essere aperto 21 22 Lettura e scrittura per caratteri − 1 Lettura e scrittura per caratteri − 2 Esempio: Funzione che copia il contenuto di un file in un altro Esempio Esistono quattro modalità per la lettura/scrittura di caratteri da un flusso getc() : macro che legge un carattere da un flusso fgetc() : analoga a getc(), ma realizzata come una funzione putc() : macro che scrive un carattere su un flusso fputc() : analoga a putc(), ma realizzata come una funzione Le macro getc() e putc(), in quanto tali, sono normalmente molto più veloci delle analoghe funzioni Tuttavia, se un parametro attuale contiene operatori che implicano effetti collaterali, fgetc() ed fputc() sono preferibili Per le altre macro di libreria, lo standard ANSI prevede che gli argomenti possano comparire una sola volta nel corpo della macro, per evitare effetti collaterali #include <stdio.h> #include <stddef.h> #define FAIL 0 #define SUCCESS 1 int copy_file(infile, outfile) char *infile, *outfile; { FILE *fp1, *fp2; char c; if ((fp1 = fopen(infile, “r”)) == NULL) return FAIL; if ((fp2 = fopen(outfile, “w”)) == NULL) { fclose(fp1); return FAIL; } while ((c = getc(fp1))) putc(c, fp2); fclose(fp1); fclose(fp2); return SUCCESS; 23 } Note Entrambi i file vengono acceduti in modalità binaria La macro getc getc() () legge il prossimo carattere dal flusso specificato e sposta l’indicatore di posizione del file avanti di un elemento ad ogni chiamata In modalità binaria, non è possibile interpretare il valore di ritorno della getc() getc () per decretare la fine del file: feof feof() () invece non presenta ambiguità 24 Lettura e scrittura per linee − 1 Lettura e scrittura per linee − 2 Esistono due funzioni di I/O orientate alle linee, fgets fgets() () ed fputs() fputs () Il prototipo per la funzione fgets fgets() () è char *fgets( *fgets(char *s *s, , int n, FILE *stream); *stream); Gli argomenti hanno il seguente significato: char *fputs *fputs( (char *s, *s, FILE *stream *stream); ); La funzione fputs fputs() () scrive l’array identificato dal primo argomento nel flusso identificato dal secondo argomento char *gets *gets( (char *s); *s); s : puntatore al primo elemento dell’array in cui vengono memorizzati i caratteri letti n : numero massimo dei caratteri da leggere (compreso ‘\0’) stream : puntatore al flusso da cui leggere La funzione fgets fgets() () legge caratteri fino ad un newline, newline la fine del file o il numero massimo specificato di caratteri, inserendo automaticamente un carattere NULL dopo l’ultimo carattere scritto nell’array; restituisce NULL se incontra la fine del file, altrimenti restituisce il primo argomento La differenza fondamentale fra gets gets() () (che legge da stdin) stdin ed fgets() fgets () è che la seconda inserisce nell’array (prima del \0 finale) anche il carattere di newline che delimita la linea Inoltre fgets fgets() () permette la specifica del numero massimo dei gets() () procede sempre fino ad caratteri da leggere, mentre gets un terminatore (newline newline o EOF) EOF 25 Lettura e scrittura per linee − 3 #include <stdio.h> #include <stddef.h> #define FAIL 0 #define SUCCESS 1 #define LINESIZE 100 int copy_file(infile, outfile) char *infile, *outfile; { FILE *fp1, *fp2; char line[LINESIZE]; if ((fp1 = fopen(infile, “r”)) == NULL) return FAIL; if ((fp2 = fopen(outfile, “w”)) == NULL) { fclose(fp1); return FAIL; } while (fgets(line, LINESIZE, fp1) != NULL) fputs(line, fp2); fclose(fp1); fclose(fp2); return SUCCESS; } 26 Lettura e scrittura per blocchi − 1 Note Il file è aperto in modalità testuale per accedere ai dati a livello di linea, altrimenti fgets() potrebbe operare in modo scorretto, cercando caratteri di newline non presenti nel file (il terminatore di linea può essere diverso per sistemi operativi diversi) fgets(), invece, si “adegua” alle caratteristiche del sistema operativo specifico La funzione copy_file() per linee è più lenta della versione per caratteri, poiché fgets() ed fputs() sono realizzate per mezzo di fgetc() ed fputc() (meno efficienti delle analoghe macro) 27 Un blocco può essere immaginato come un array: quando si legge o si scrive un blocco è necessario specificare il numero di elementi del blocco e la dimensione di ognuno di essi Le due funzioni orientate alla gestione dei blocchi sono fread() e fwrite() Il prototipo per la funzione fread() è int fread(void *ptr, int size, int nmemb, FILE *stream); Gli argomenti hanno il seguente significato: ptr : puntatore ad un array in cui vengono memorizzati i dati size : dimensione di ogni elemento dell’array nmemb : numero di elementi da leggere stream : puntatore a file 28 Lettura e scrittura per blocchi − 3 Lettura e scrittura per blocchi − 2 #include <stdio.h> #include <stddef.h> #define FAIL 0 #define SUCCESS 1 #define BLOCKSIZE 512 typedef char DATA; La funzione fread() restituisce il numero di elementi effettivamente letti, che dovrebbe coincidere con il terzo argomento, a meno di errori o di condizioni di fine file La funzione fwrite() ha gli stessi argomenti, ma scrive nel flusso gli elementi contenuti nell’array La funzione copy_file() può essere realizzata anche con granularità di blocco: if (ferror(fp1)) { printf(“Errore in lettura del file %s\n”, infile); fclose(fp1); fclose(fp2); return FAIL; } { FILE *fp1, *fp2; DATA block[BLOCKSIZE]; int num_read; 29 La selezione di un metodo di I/O − 1 Le macro putc() e getc() sono le più veloci, ma la maggior parte dei sistemi operativi è in grado di realizzare operazioni di I/O su blocchi ancora più efficienti (ad es., read() e write() in UNIX) #include <stdio.h> Funzione che conta il numero di linee di un file fwrite(block, sizeof(DATA), num_read, fp2); int copy_file(infile, outfile) char *infile, *outfile; La condizione di fine file è controllata confrontando il numero degli elementi letti, restituito da fread(), con il valore specificato nella lista degli argomenti: se sono diversi si ha una condizione di fine file o di errore La funzione ferror() viene utilizzata per stabilire quale condizione si è verificata Talvolta occorre privilegiare la semplicità all’efficienza: fgets() e fputs(), ad esempio, sono lente, ma particolarmente adatte nei casi in cui sia necessario analizzare linee while ((num_read = fread(block, sizeof(DATA), BLOCKSIZE, fp1)) == BLOCKSIZE) fwrite(block, sizeof(DATA), num_read, fp2); #include <stddef.h> #define MAX_LINE_SIZE 120 if ((fp1 = fopen(infile, “rb”)) == NULL) { printf(“Errore nell’apertura del file %s \ in input\n”, infile); return FAIL; } if ((fp2 = fopen(outfile, “wb”)) == NULL) { printf(“Errore nell’apertura del file %s \ in output\n”, outfile); fclose(fp1); return FAIL; } fclose(fp1); fclose(fp2); return SUCCESS; } 30 La selezione di un metodo di I/O − 2 Ultimo, ma non meno importante, fattore da considerare nella scelta di un metodo di I/O è la portabilità, fondamentale non tanto nella scelta del tipo di I/O (granularità a caratteri, linee o blocchi), ma nella scelta della modalità testo o binaria Se il file contiene dati testuali (codice sorgente o documenti), la modalità testo e l’accesso per linee sono da privilegiare int lines_in_file(fp) FILE *fp; { char buf[MAX_LINE_SIZE]; int line_num = 0; rewind(fp); /* sposta l’indicatore di posizione all’inizio del file */ while (fgets(buf, MAX_LINE_SIZE, fp) != NULL) line_num++; return line_num; Se i dati sono numerici e non sono strutturati per linee, è preferibile la modalità binaria, con accesso per caratteri o per blocchi (codice eseguibile) } 31 32 I/O non bufferizzato − 1 I/O non bufferizzato − 2 Le librerie di run−time del C consentono di modificare la dimensione del buffer: tale possibilità deve essere utilizzata con attenzione, dato che la dimensione del buffer dovrebbe essere “ottima” per il particolare sistema operativo Talvolta, è tuttavia necessario eliminare completamente la bufferizzazione, tipicamente quando si vogliono elaborare immediatamente i dati di input Per eliminare la bufferizzazione ci si può avvalere delle funzioni setbuf() e setvbuf() La funzione setbuf() richiede due parametri: un puntatore a file, ed un puntatore ad un array di caratteri da utilizzare come nuovo buffer; se tale puntatore è nullo, la bufferizzazione viene eliminata; setbuf() non restituisce valori La funzione setvbuf() richiede due parametri aggiuntivi, che permettono di specificare la tipologia di bufferizzazione (per linee, per blocchi, o assente) e la dimensione dell’array da utilizzare come buffer La tipologia di bufferizzazione va specificata mediante uno dei tre simboli definiti in stdio. stdio.h: _IOFBF : bufferizzazione per blocchi _IOLBF : bufferizzazione per linee _IONBF : bufferizzazione assente La funzione restituisce un valore diverso da zero se completata correttamente, zero se non è in grado di soddisfare la richiesta setbuf(stdin, NULL); setvbuf(stdin, NULL, _IONBF, 0); 33 L’accesso diretto a file − 1 34 L’accesso diretto a file − 2 In C, le funzioni per l’accesso diretto a file sono fseek() e Esempio: L’istruzione Esempio stat = fseek(fp, 10, SEEK_SET); sposta l’indicatore di posizione del file al carattere 10 del flusso (l’undicesimo), che sarà il prossimo elemento letto o scritto La fseek() restituisce zero se la richiesta è corretta, un valore diverso da zero altrimenti Esempio: L’istruzione Esempio stat = fseek(fp, 1, SEEK_END); non è lecita se fp è aperto in sola lettura, perché sposta l’indicatore oltre la fine del file Per flussi binari, lo spostamento può essere un qualsiasi numero intero che non sposti l’indicatore al di fuori del file; per flussi testuali, deve essere zero o un valore restituito dalla ftell() ftell() La funzione fseek() sposta l’indicatore di posizione del file a un carattere specificato nel flusso Il prototipo della fseek() è int fseek(FILE *stream, long int offset, int whence) dove: stream : puntatore a file offset : numero di caratteri di spostamento whence : posizione di partenza da cui calcolare lo spostamento L’argomento whence può assumere uno dei tre seguenti valori, definiti in stdio. stdio.h SEEK_SET : inizio del file SEEK_CUR : posizione corrente dell’indicatore SEEK_END : fine del file 35 36 L’accesso diretto a file − 3 Esempio: dimensione del file /* Determinazione del numero di caratteri di un file con fseek e ftell */ #include <stdio.h> La funzione ftell() richiede, come unico argomento, un puntatore a file e restituisce la posizione corrente dell’indicatore di posizione nel file La posizione restituita da ftell() si intende relativa all’inizio del file… main(int argc, char **argv) { FILE *fp; long n; if (argc<2) printf(“File non specificato\n”); else { fp=fopen(argv[1], “r”); /* apertura del file */ if (fp != NULL) { fseek(fp, 0, SEEK_END); /* puntatore alla fine del file */ n=ftell(fp); /* lettura posizione del puntatore */ fclose(fp); printf(“Dimensione del file %ld\n”, n); } else printf(“Errore: il file %s non esiste”, argv[1]); } …per flussi binari rappresenta il numero di caratteri dall’inizio del file alla posizione corrente …per flussi testuali rappresenta un valore dipendente dall’implementazione, significativo solo se utilizzato come parametro per la fseek() cur_pos = ftell(fp); if (search(string) == FAIL) fseek(fp, cur_pos, SEEK_set); Se la ricerca di una certa stringa nel file fallisce, l’indicatore di posizione nel file viene riportato al valore originale } 37 Lettura e scrittura di dati 38 La gestione degli errori Su un file aperto è possibile utilizzare il puntatore a file per svolgere operazioni di lettura e scrittura Le operazioni di accesso al file possono essere effettuate su oggetti di granularità diversa, in particolare a livello di… Ogni funzione di I/O restituisce un valore speciale in caso di errore: alcune restituiscono zero, altre un valore diverso da zero o EOF Per ogni flusso aperto, esistono due campi booleani nella struttura FILE che registrano condizioni di errore o di fine file Si può accedere ai campi di fine file e di errore utilizzando le funzioni feof() e ferror() La funzione clearerr() pone entrambi i campi a zero …carattere …linea …blocco Qualsiasi sia la granularità, è impossibile leggere da un flusso e quindi scrivere sullo stesso flusso senza che fra le due operazioni venga effettuata una chiamata a fseek(), rewind() o fflush() Le funzioni fseek(), rewind() e fflush() sono le uniche funzioni di I/O che forzano la scrittura del buffer sul flusso 39 #include <stdio.h> #define ERR_FLAG 1 #define EOF_FLAG 2 char stream_stat(fp) FILE *fp; { /* se nessun campo booleano è alterato, stat vale 0; * se il solo campo di errore è alterato, stat vale 1, * se il solo campo di fine file è alterato, stat vale 2 * se sono alterati entrambi, stat vale 3 */ char stat = 0; if (ferror(fp)) stat |= ERR_FLAG; if (feof(fp)) stat |= EOF_FLAG; clearerr(fp); return stat; } 40 L’allocazione dinamica della memoria − 1 Alle variabili con durata fissa viene riservata memoria per l’intera durata del programma, mentre alle variabili con durata automatica la memoria viene allocata ogni volta che si esegue il blocco relativo: in entrambi i casi si suppone di conoscere la quantità di memoria da allocare nel momento i cui si scrive il codice sorgente Tuttavia, talvolta l’occupazione di memoria dipende strettamente dai dati in ingresso In C, esistono quattro funzioni della libreria di run−time che allocazione dinamica della memoria permettono l’allocazione malloc() − alloca un numero specificato di byte in memoria e restituisce un puntatore all’inizio del blocco allocato calloc() − come malloc() malloc(), ma inizializza a zero i byte allocati; consente di allocare la memoria per più di un oggetto alla volta realloc() − cambia la dimensione di un blocco precedentemente allocato free() − libera la memoria che era stata allocata con malloc() malloc(), calloc() o realloc() 41