Programmazione 1 - Vincenzo Marra - Università degli Studi di Milano
Transcript
Programmazione 1 - Vincenzo Marra - Università degli Studi di Milano
I/O standard: i file Strutture Programmazione 1 Lezione 10 Vincenzo Marra [email protected] Dipartimento di Matematica Federigo Enriques Università degli Studi di Milano 20 maggio 2015 I/O standard: i file Strutture Input/Output standard: i file Il compilatore C assume l’esistenza nel sistema di tre flussi di dati standard: 1 stdin – associato d’ufficio ai dati in ingresso provenienti dalla tastiera. 2 stdout – associato d’ufficio ai dati in uscita visualizzati sul monitor del calcolatore. 3 stderr – associato d’ufficio ai dati in uscita visualizzati sul monitor del calcolatore. (Non ne parleremo in dettaglio.) I/O standard: i file Strutture Input/Output standard: i file Il compilatore C assume l’esistenza nel sistema di tre flussi di dati standard: 1 stdin – associato d’ufficio ai dati in ingresso provenienti dalla tastiera. 2 stdout – associato d’ufficio ai dati in uscita visualizzati sul monitor del calcolatore. 3 stderr – associato d’ufficio ai dati in uscita visualizzati sul monitor del calcolatore. (Non ne parleremo in dettaglio.) Un file che risieda nella memoria di massa del calcolatore può essere usato dai programmi in C come flusso di dati, sia in lettura che in scrittura. Le funzioni della libreria standard necessarie allo scopo sono definite nel file di intestazione stdio.h. I/O standard: i file Strutture Apertura dei file Prima di poter leggere da o scrivere su un file, è necessario aprirlo tramite la funzione: FILE *fopen(char *nome, char *modo) I/O standard: i file Strutture Apertura dei file Prima di poter leggere da o scrivere su un file, è necessario aprirlo tramite la funzione: FILE *fopen(char *nome, char *modo) 1 La stringa nome è il nome del file, comprensivo del percorso (assoluto o relativo al punto del file system in cui risiede il programma). I/O standard: i file Strutture Apertura dei file Prima di poter leggere da o scrivere su un file, è necessario aprirlo tramite la funzione: FILE *fopen(char *nome, char *modo) 1 2 La stringa nome è il nome del file, comprensivo del percorso (assoluto o relativo al punto del file system in cui risiede il programma). La stringa modo stabilisce la modalità di apertura del file — per esempio, in lettura — e sarà discussa fra poco. I/O standard: i file Strutture Apertura dei file Prima di poter leggere da o scrivere su un file, è necessario aprirlo tramite la funzione: FILE *fopen(char *nome, char *modo) 1 2 3 La stringa nome è il nome del file, comprensivo del percorso (assoluto o relativo al punto del file system in cui risiede il programma). La stringa modo stabilisce la modalità di apertura del file — per esempio, in lettura — e sarà discussa fra poco. FILE è un tipo (definito da stdio.h, non primitivo come int o char) i cui valori descrivono file del file system. I/O standard: i file Strutture Apertura dei file Prima di poter leggere da o scrivere su un file, è necessario aprirlo tramite la funzione: FILE *fopen(char *nome, char *modo) 1 2 3 4 La stringa nome è il nome del file, comprensivo del percorso (assoluto o relativo al punto del file system in cui risiede il programma). La stringa modo stabilisce la modalità di apertura del file — per esempio, in lettura — e sarà discussa fra poco. FILE è un tipo (definito da stdio.h, non primitivo come int o char) i cui valori descrivono file del file system. La funzione restituisce un puntatore a FILE. Esso è null se, e solo se, si è riscontrato un errore. I/O standard: i file Strutture Modalità di apertura dei file Una chiamata alla funzione: FILE *fopen(char *nome, char *modo) stabilisce la modalità di apertura del file tramite la stringa modo. I/O standard: i file Strutture Modalità di apertura dei file Una chiamata alla funzione: FILE *fopen(char *nome, char *modo) stabilisce la modalità di apertura del file tramite la stringa modo. 1 Se modo è "r" (read), il file è aperto in lettura. Se esso è inesistente la funzione restituisce NULL e si ha un errore. I/O standard: i file Strutture Modalità di apertura dei file Una chiamata alla funzione: FILE *fopen(char *nome, char *modo) stabilisce la modalità di apertura del file tramite la stringa modo. 1 2 Se modo è "r" (read), il file è aperto in lettura. Se esso è inesistente la funzione restituisce NULL e si ha un errore. Se modo è "w" (write), il file è aperto in scrittura. Se esso già esiste viene interamente cancellato. Se invece è insistente, viene creato. I/O standard: i file Strutture Modalità di apertura dei file Una chiamata alla funzione: FILE *fopen(char *nome, char *modo) stabilisce la modalità di apertura del file tramite la stringa modo. 1 2 3 Se modo è "r" (read), il file è aperto in lettura. Se esso è inesistente la funzione restituisce NULL e si ha un errore. Se modo è "w" (write), il file è aperto in scrittura. Se esso già esiste viene interamente cancellato. Se invece è insistente, viene creato. Se modo è "a" (append), il file è aperto in modalità di scrittura accodamento: le operazioni di scrittura aggiungono i dati da scrivere alla fine del file. Se esso è insistente, viene creato. I/O standard: i file Strutture Modalità di apertura dei file Una chiamata alla funzione: FILE *fopen(char *nome, char *modo) stabilisce la modalità di apertura del file tramite la stringa modo. 1 2 3 4 Se modo è "r" (read), il file è aperto in lettura. Se esso è inesistente la funzione restituisce NULL e si ha un errore. Se modo è "w" (write), il file è aperto in scrittura. Se esso già esiste viene interamente cancellato. Se invece è insistente, viene creato. Se modo è "a" (append), il file è aperto in modalità di scrittura accodamento: le operazioni di scrittura aggiungono i dati da scrivere alla fine del file. Se esso è insistente, viene creato. Se si tenta di eseguire una di queste operazioni, ma non se ne hanno i permessi, la funzione restituisce NULL. I/O standard: i file 1 Strutture Nota. Le modalità appena viste non permettono di eseguire lettura da e scrittura su il medesimo file aperto tramite un singolo puntatore a FILE. Letture e scritture concorrenti sono possibili in C tramite altre modalità di apertura, cui accenneremo solo brevemente. I/O standard: i file 1 2 3 4 Strutture Nota. Le modalità appena viste non permettono di eseguire lettura da e scrittura su il medesimo file aperto tramite un singolo puntatore a FILE. Letture e scritture concorrenti sono possibili in C tramite altre modalità di apertura, cui accenneremo solo brevemente. Se modo è "r+" (read update), il file è aperto in lettura e scrittura. Se modo è "w+" (write update), il file è aperto in scrittura e lettura. Se modo è "a+" (append update), il file è aperto in modalità di lettura e accodamento. I/O standard: i file 1 2 3 4 5 Strutture Nota. Le modalità appena viste non permettono di eseguire lettura da e scrittura su il medesimo file aperto tramite un singolo puntatore a FILE. Letture e scritture concorrenti sono possibili in C tramite altre modalità di apertura, cui accenneremo solo brevemente. Se modo è "r+" (read update), il file è aperto in lettura e scrittura. Se modo è "w+" (write update), il file è aperto in scrittura e lettura. Se modo è "a+" (append update), il file è aperto in modalità di lettura e accodamento. Per poter usare queste modalità di apertura occorre gestire la posizione corrente all’interno del file fra le letture e le scritture, tramite funzioni quali fseek o fflush. I/O standard: i file 1 2 3 4 Strutture Nota. Le modalità appena viste non permettono di eseguire lettura da e scrittura su il medesimo file aperto tramite un singolo puntatore a FILE. Letture e scritture concorrenti sono possibili in C tramite altre modalità di apertura, cui accenneremo solo brevemente. Se modo è "r+" (read update), il file è aperto in lettura e scrittura. Se modo è "w+" (write update), il file è aperto in scrittura e lettura. Se modo è "a+" (append update), il file è aperto in modalità di lettura e accodamento. 5 Per poter usare queste modalità di apertura occorre gestire la posizione corrente all’interno del file fra le letture e le scritture, tramite funzioni quali fseek o fflush. 6 I dettagli sono in K&R, Cap. 7 e §1 dell’Appendice B. I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) 1 Il parametro pf è il puntatore al file da chiudere. I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) 1 2 Il parametro pf è il puntatore al file da chiudere. La funzione restituisce 0 se l’operazione di chiusura è stata eseguita con successo, e EOF altrimenti. I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) 1 Il parametro pf è il puntatore al file da chiudere. 2 La funzione restituisce 0 se l’operazione di chiusura è stata eseguita con successo, e EOF altrimenti. 3 Un errore durante la chiusura si può riscontrare, per esempio, perché pf è NULL o perché esso non è correttamente associato a un file aperto. I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) 1 Il parametro pf è il puntatore al file da chiudere. 2 La funzione restituisce 0 se l’operazione di chiusura è stata eseguita con successo, e EOF altrimenti. 3 Un errore durante la chiusura si può riscontrare, per esempio, perché pf è NULL o perché esso non è correttamente associato a un file aperto. 4 I file che risultino ancora aperti al momento della terminazione (non anomala) del programma sono automaticamente chiusi. I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) 1 È però sempre una buona idea chiudere esplicitamente i file che non servono più, perché le risorse del sistema operativo sono limitate ed occorre usarle efficientemente. I/O standard: i file Strutture Chiusura dei file Quando un file aperto non è più usato dal programma, lo si può chiudere tramite la funzione: int *fclose(FILE *pf) 1 È però sempre una buona idea chiudere esplicitamente i file che non servono più, perché le risorse del sistema operativo sono limitate ed occorre usarle efficientemente. 2 Oltre a chiudere il file, la chiamata alla funzione forza la scrittura fisica sul file dei dati in uscita che ancora risiedano nella memoria tampone (buffer) in attesa di essere trasferiti. Tutti i sistemi operativi moderni usano tecniche più o meno sofisticate di bufferizzazione dell’I/O, che noi però non potremo spiegare in dettaglio. Si tratta di argomenti che si studiano nei corsi di Sistemi Operativi. I/O standard: i file Strutture Lettura e scrittura di caratteri singoli int getc(FILE *pf) Restituisce il successivo carattere del file pf, che deve essere aperto in lettura, come intero (ossia il suo codice ASCII convertito da char a int), oppure EOF se incontra la fine del file o riscontra un errore. Nota. A differenza della lettura da stdin, la lettura da file non è mai bloccante. Ciò vale anche per tutte le altre funzioni di lettura da file. I/O standard: i file Strutture Lettura e scrittura di caratteri singoli int getc(FILE *pf) Restituisce il successivo carattere del file pf, che deve essere aperto in lettura, come intero (ossia il suo codice ASCII convertito da char a int), oppure EOF se incontra la fine del file o riscontra un errore. Nota. A differenza della lettura da stdin, la lettura da file non è mai bloccante. Ciò vale anche per tutte le altre funzioni di lettura da file. int putc(int c, FILE *pf) Scrive il carattere c, convertito in int, sul file pf, che deve essere aperto in scrittura. Restituisce il carattere scritto oppure EOF se incorre in un errore. I/O standard: i file Strutture Esercizio: Eco da file Si scriva un programma che accetti da riga di comando il nome di un file, e lo visualizzi sul terminale carattere per carattere. Se il nome del file non è specificato o è inesistente o non può essere aperto, il programma termina con un messaggio d’errore appropriato I/O standard: i file Strutture feco.c 1 #include <stdio.h> 2 3 4 5 6 7 8 9 int main(int argc, char *argv[]) //ah, e’ lui { if (argc < 2) { printf("Errore nel numero degli argomenti.\n"); return -1; } 10 FILE *pf; if ( (pf=fopen(argv[1], "r")) == NULL ) { printf("Errore nell’apertura del file: \"%s\".\n", argv[1]); return -1; } 11 12 13 14 15 16 17 /* Esegue l’eco a video, carattere per carattere */ char c; while ( (c=getc(pf)) != EOF ) putchar(c); 18 19 20 21 22 return 0; 23 24 } I/O standard: i file Strutture Lettura e scrittura di dati formattati int fscanf (FILE *pf, const char *formato, ...) int fprintf (FILE *pf, const char *formato, ...) Queste due funzioni sono identiche alle loro controparti scanf e printf, eccetto che il primo argomento indica il file dal o sul quale leggere o scrivere, rispettivamente. In altre parole, int fscanf (stdin, const char *formato, ...) int fprintf (stdout, const char *formato, ...) sono identiche a scanf e printf, che già conosciamo. I/O standard: i file Strutture Lettura e scrittura di stringhe char *fgets (char *s, int max, FILE *pf) L’abbiamo già citata. Legge la prossima riga da pf, fino al più a max-1 caratteri, pone quanto letto in s, aggiungendo in coda il terminatore '\0'. Nota. Include anche l’eventuale '\n' fra i caratteri letti. Restituisce il puntatore s, oppure NULL se incontra la fine del file o se riscontra un errore. I/O standard: i file Strutture Lettura e scrittura di stringhe char *fgets (char *s, int max, FILE *pf) L’abbiamo già citata. Legge la prossima riga da pf, fino al più a max-1 caratteri, pone quanto letto in s, aggiungendo in coda il terminatore '\0'. Nota. Include anche l’eventuale '\n' fra i caratteri letti. Restituisce il puntatore s, oppure NULL se incontra la fine del file o se riscontra un errore. int fputs (char *s, FILE *pf) Scrive la stringa s sul file pf. Restituisce un valore non negativo se la chiamata va a buon fine, e EOF se riscontra un errore. I/O standard: i file Strutture scrivileggi.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include<stdio.h> int main(void) { FILE *pf; //puntatore a file char letta[BUFSIZ]; //buffer di lettura char s[]="Ego te absolvo..."; //una stringa if ((pf=fopen("test", "a")) == NULL) //pf e’ associato al file "test" { printf("Errore I/O.\n"); return 0; } fprintf(pf, "Ecco un intero: %d\n", 45); fprintf(pf, "Ecco un double: %lf\n", 3.1415); fprintf(pf, "Ecco una stringa: %s\n", s); fclose(pf); if ((pf=fopen("test", "r")) == NULL) //pf e’ associato al file "test" { printf("Errore I/O.\n"); return 0; } while ( fgets(letta, BUFSIZ, pf)!= NULL ) //stampa su stdout una riga alla volta. printf("%s",letta); fclose(pf); return 0; } I/O standard: i file Strutture Lettura di stringhe: funzioni deprecate e sicurezza Abbiamo già visto la funzione char ∗ gets(char ∗ s) Essa è “deprecata” dagli standard C recenti, e non va mai usata in programmi reali. (Si usa fgets al suo posto, anche per leggere da stdin.) A fini didattici, per l’esame, è permesso usarla: sarebbe però preferibile abituarsi fin da subito all’uso di fgets. I/O standard: i file Strutture Lettura di stringhe: funzioni deprecate e sicurezza Abbiamo già visto la funzione char ∗ gets(char ∗ s) Essa è “deprecata” dagli standard C recenti, e non va mai usata in programmi reali. (Si usa fgets al suo posto, anche per leggere da stdin.) A fini didattici, per l’esame, è permesso usarla: sarebbe però preferibile abituarsi fin da subito all’uso di fgets. Il motivo per cui l’uso di gets è deprecato è che i programmi che la usano possono andare in buffer overflow: per cenni, ciò accade perché gets può leggere più caratteri dello spazio di memoria (buffer) che il programmatore ha allocato per memorizzarli, il che può permette a un programma malevolo (malware) di attaccare il programma in questione e accedere al sistema su cui esso gira. Sono argomenti trattati nei corsi di Sicurezza Informatica. I/O standard: i file Strutture Le strutture Le strutture permettono di accorpare in una singola unità sintattica tipi di dati compositi. Per esempio, possiamo denotare un punto del piano reale con una coppia di double, le sue coordinate. Per accorpare le due coordinate in una singola unità, dichiariamo: 1 2 3 4 5 struct punto //Etichetta (opzionale) della struttura { double x; //Primo membro double y; //Secondo membro }; I/O standard: i file Strutture Le strutture Le strutture permettono di accorpare in una singola unità sintattica tipi di dati compositi. Per esempio, possiamo denotare un punto del piano reale con una coppia di double, le sue coordinate. Per accorpare le due coordinate in una singola unità, dichiariamo: 1 2 3 4 5 struct punto //Etichetta (opzionale) della struttura { double x; //Primo membro double y; //Secondo membro }; Fatto centrale. Queste righe d codice definiscono un nuovo tipo (senza allocare memoria). I/O standard: i file Strutture Le strutture Le strutture permettono di accorpare in una singola unità sintattica tipi di dati compositi. Per esempio, possiamo denotare un punto del piano reale con una coppia di double, le sue coordinate. Per accorpare le due coordinate in una singola unità, dichiariamo: 1 2 3 4 5 struct punto //Etichetta (opzionale) della struttura { double x; //Primo membro double y; //Secondo membro }; Fatto centrale. Queste righe d codice definiscono un nuovo tipo (senza allocare memoria). Esso è il tipo struct punto. I/O standard: i file Strutture Le strutture Le strutture permettono di accorpare in una singola unità sintattica tipi di dati compositi. Per esempio, possiamo denotare un punto del piano reale con una coppia di double, le sue coordinate. Per accorpare le due coordinate in una singola unità, dichiariamo: 1 2 3 4 5 struct punto //Etichetta (opzionale) della struttura { double x; //Primo membro double y; //Secondo membro }; Fatto centrale. Queste righe d codice definiscono un nuovo tipo (senza allocare memoria). Esso è il tipo struct punto. Se l’etichetta punto — che è un’abbreviazione di ciò che la segue, fino alla graffa di chiusura — è assente, il nome del nuovo tipo va da struct (incluso) alla graffa di chiusura. I/O standard: i file Strutture Dichiarazioni di variabili Segue che è legittimo e sensato dichiarare: struct punto p; //Un punto (non inizializzato) I/O standard: i file Strutture Dichiarazioni di variabili Segue che è legittimo e sensato dichiarare: struct punto p; //Un punto (non inizializzato) Fatto centrale. Questa dichiarazione alloca memoria sufficiente a memorizzare tutti i membri della struttura (memoria per due double). I/O standard: i file Strutture Dichiarazioni di variabili Segue che è legittimo e sensato dichiarare: struct punto p; //Un punto (non inizializzato) Fatto centrale. Questa dichiarazione alloca memoria sufficiente a memorizzare tutti i membri della struttura (memoria per due double). Nota che è anche legittimo e sensato il brano seguente, che codifica dichiarazione e definizione assieme, senza etichetta: 1 2 3 4 5 struct { double x; //Primo membro double y; //Secondo membro } p,q; //Due punti (non inizializzati) I/O standard: i file Strutture Inizializzazione, assegnazioni, parametri Le variabili di tipo struct ... si possono inizializzare, al momento della dichiarazione, con liste di valori costanti, in modo simile a quanto abbiamo visto per gli array. Per esempio: struct punto q = {-0.1, 2}; //Inizializzazione con lista I/O standard: i file Strutture Inizializzazione, assegnazioni, parametri Le variabili di tipo struct ... si possono inizializzare, al momento della dichiarazione, con liste di valori costanti, in modo simile a quanto abbiamo visto per gli array. Per esempio: struct punto q = {-0.1, 2}; //Inizializzazione con lista Tali variabili possono anche essere assegnate. Così, è legittimo: 1 2 3 4 5 6 7 8 struct punto //Etichetta (opzionale) della struttura { double x; //Primo membro double y; //Secondo membro }; struct punto p; //Un punto (non inizializzato) struct punto q = {-0.1, 2}; //Inizializzazione con lista p=q; //Assegna q a p I/O standard: i file Strutture Inizializzazione, assegnazioni, parametri Si possono poi passare le variabili automatiche di tipo struct {...} come parametri alle funzioni. E le funzioni possono restituire valori di un tale tipo strutturato. Per esempio: 1 2 3 4 5 6 funzstrutt.c /* Definizione globale */ struct punto //Etichetta (opzionale) della struttura { double x; //Primo membro double y; //Secondo membro }; 7 8 9 10 11 12 /* Nel main */ struct punto O = {0.0, 0.0}; //Un punto struct punto p = {1.0, 1.0}; //Un altro punto struct punto m;//Un terzo punto (non inizializzato) m=medio(O,p); //restitusice il punto medio degli args I/O standard: i file Strutture Inizializzazione, assegnazioni, parametri Si possono poi passare le variabili automatiche di tipo struct {...} come parametri alle funzioni. E le funzioni possono restituire valori di un tale tipo strutturato. Il prototipo della funzione medio deve allora essere: struct punto medio(struct punto, struct punto); Nota. Anche qui, come sempre in C, il passaggio dei parametri avviene per copia. Naturalmente è possibile dichiarare e passare come argomenti puntatori a strutture. Per esempio, la dichiarazione struct punto *pnt; dichiara (ma non inizializza) un puntatore a struct punto. I/O standard: i file Strutture Accesso ai campi Per accedere a un membro di una struttura si usa la sintassi nome-strutt.membro-strutt Così: I/O standard: i file Strutture Accesso ai campi Per accedere a un membro di una struttura si usa la sintassi nome-strutt.membro-strutt Così: 1 2 3 4 5 struct { double x; //Primo membro double y; //Secondo membro } p; //Un punto (non inizializzato) 6 7 8 p.x=0.0; //Assegna ascissa p.y=1.1; //Assegna ordinata I/O standard: i file Strutture Campo di visibilità dei membri e shadowing Il nome completo del membro ascissa è dunque p.x, e non solo x. La coppia di dichiarazioni: I/O standard: i file Strutture Campo di visibilità dei membri e shadowing Il nome completo del membro ascissa è dunque p.x, e non solo x. La coppia di dichiarazioni: 10 11 struct punto p; int x; non dà quindi luogo ad alcun effetto di shadowing: non vi sono ambiguità possibili fra p.x che qui è la variabile di tipo double, e x che qui invece è la variabile di tipo int. I/O standard: i file Strutture Strutture innestate Un membro di una struttura può essere esso stesso una struttura. Supponiamo di codificare un triangolo nel piano tramite i suoi tre vertici. Così: I/O standard: i file Strutture Strutture innestate Un membro di una struttura può essere esso stesso una struttura. Supponiamo di codificare un triangolo nel piano tramite i suoi tre vertici. Così: 1 2 3 4 5 6 struct tri { struct punto a; //Primo membro struct punto b; //Secondo membro struct punto c; //Terzo membro } t; //Un triangolo (non inizializzato) I/O standard: i file Strutture Strutture innestate Un membro di una struttura può essere esso stesso una struttura. Supponiamo di codificare un triangolo nel piano tramite i suoi tre vertici. Così: 1 2 3 4 5 6 struct tri { struct punto a; //Primo membro struct punto b; //Secondo membro struct punto c; //Terzo membro } t; //Un triangolo (non inizializzato) L’accesso ai singoli vertici si codifica così: 8 t.a={0.0,1.0}; //Inizializzazione del vert a di t I/O standard: i file Strutture Strutture innestate Un membro di una struttura può essere esso stesso una struttura. Supponiamo di codificare un triangolo nel piano tramite i suoi tre vertici. Così: 1 2 3 4 5 6 struct tri { struct punto a; //Primo membro struct punto b; //Secondo membro struct punto c; //Terzo membro } t; //Un triangolo (non inizializzato) L’accesso alle coordinate dei vertici si codifica così: 9 t.b.x=-1.0; //Inizializzazione dell’ascissa del vert b di t I/O standard: i file Strutture Puntatori alle strutture e operatore -> I puntatori alle strutture sono del tutto analoghi ai puntatori alle variabili ordinarie. Così: 1 2 3 4 5 6 struct tri { struct punto a; struct punto b; struct punto c; }; 7 8 9 10 struct tri s, t = {{0,0}, {0,1}, {1,0}}; //Due triangoli struct tri *pt; //Puntatore a struct tri, non inizializzato pt=&t; //pt punta a t I/O standard: i file Strutture Puntatori alle strutture e operatore -> I puntatori alle strutture sono del tutto analoghi ai puntatori alle variabili ordinarie. Così: 1 2 3 4 5 6 struct tri { struct punto a; struct punto b; struct punto c; }; 7 8 9 10 struct tri s, t = {{0,0}, {0,1}, {1,0}}; //Due triangoli struct tri *pt; //Puntatore a struct tri, non inizializzato pt=&t; //pt punta a t Accesso al vertice a di t: 11 s.a=(*pt).a; //accede al vert a di t. Parentesi necessarie. I/O standard: i file Strutture Puntatori alle strutture e operatore -> I puntatori alle strutture sono del tutto analoghi ai puntatori alle variabili ordinarie. Così: 1 2 3 4 5 6 struct tri { struct punto a; struct punto b; struct punto c; }; 7 8 9 10 struct tri s, t = {{0,0}, {0,1}, {1,0}}; //Due triangoli struct tri *pt; //Puntatore a struct tri, non inizializzato pt=&t; //pt punta a t Accesso alla coordinata x del vertice b di t: 12 s.b.x=(*pt).b.x; //accede all’ascissa del vert b di t. I/O standard: i file Strutture Puntatori alle strutture e operatore -> I puntatori alle strutture sono del tutto analoghi ai puntatori alle variabili ordinarie. Così: 1 2 3 4 5 6 struct tri { struct punto a; struct punto b; struct punto c; }; 7 8 9 10 struct tri s, t = {{0,0}, {0,1}, {1,0}}; //Due triangoli struct tri *pt; //Puntatore a struct tri, non inizializzato pt=&t; //pt punta a t Abbreviazione ->. Accesso al vertice c di t: 13 s.c=pt->c; //accede al vert c di t. I/O standard: i file Strutture Puntatori alle strutture e operatore -> I puntatori alle strutture sono del tutto analoghi ai puntatori alle variabili ordinarie. Così: 1 2 3 4 5 6 struct tri { struct punto a; struct punto b; struct punto c; }; 7 8 9 10 struct tri s, t = {{0,0}, {0,1}, {1,0}}; //Due triangoli struct tri *pt; //Puntatore a struct tri, non inizializzato pt=&t; //pt punta a t Abbreviazione ->. Accesso alla coordinata y del vertice c di t: 14 pt->c.y=2.5; //accede all’ordinata del vert c di t. I/O standard: i file Strutture Esercizi 1 Si scriva una funzione che accetti in ingresso due puntatori a strutture che rappresentano punti nel piano, e restituisca un puntatore al punto di norma euclidea minima. 2 Si definisca una struttura atta a rappresentare libri, nella forma Cognome Autore, Titolo, Data Pubblicazione. Si scriva poi una funzione che accetti in ingresso un (puntatore a un) array di tali strutture, e restituisca l’indice del libro di pubblicazione più recente. Segue una soluzione del primo esercizio. Il secondo sarà svolto in classe tempo permettendo, oppure potrete risolverlo in laboratorio. I/O standard: i file Strutture norm.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <stdio.h> #include <math.h> struct punto { double x; double y; }; double norma(struct punto *); struct punto *minnorm(struct punto *, struct punto *); int main(void) { struct punto p = {2.5,1}, q = {1,1}; printf("Dei due punti (%g,%g) e (%g,%g),", p.x,p.y,q.x,q.y); struct punto *pt = minnorm(&p,&q); printf("il punto di norma minima e’ (%g,%g).\n", pt->x,pt->y); return 0; } double norma(struct punto *a) { return sqrt( (a->x)*(a->x)+(a->y)*(a->y) ); } struct punto *minnorm(struct punto *a, struct punto *b) { if (norma(a) < norma (b)) return a; return b; }