Sistemi operativi, A.A. 2011/2012 (M. Cesati) Scaletta esercitazione #6, 19.04.2012 1 Realizzazione di una struttura di dati dinamica: la lista semplice 1.1 Problema con l'implementazione della precedente lezione: non gestisce l'elemento in cima alla lista 1.2 Inserimento di un elemento in una lista (nuova versione) +------------------------------------------------------------------+ | void insert_after_node(struct node_t *new, struct node_t **pnext)| | { | | new->next = *pnext; | | *pnext = new; | | } | +------------------------------------------------------------------+ 1.3 Cancellazione di un elemento da una lista (nuova versione) +-----------------------------------------------------------------+ |struct node_t *remove_after_node(struct node_t **pnext) | |{ | | struct node_t *d = *ppos; | | *ppos = d->next; | | return d; | |} | +-----------------------------------------------------------------+ 1.4 Perche' queste funzioni utilizzano l'indirizzo di un campo next invece del piu' naturale indirizzo di un elemento della lista? 1.4.1 Perche' cosi' funzionano anche per la testa della lista: +----------------------------------------------------------+ |struct node_t *list_head = NULL; /* iniz. lista vuota */ | |/* per inserire il primo elemento: */ | |insert_after_node(new, &list_head); | |/* per cancellare il primo elemento: */ | |remove_after_node(&list_head); | +----------------------------------------------------------+ 1.5 Scansione degli elementi di una lista +-----------------------------------------------------------------+ |struct node_t *head, *p; | |for (p=head; p != NULL; p = p->head) | | do_some_work_on_element(p); | +-----------------------------------------------------------------+ 2 Le API della libreria standard per l'I/O bufferizzato 2.1 Apertura di un file con fopen() +----------------------------------------------------+ |FILE *fopen(const char *path, const char *mode); | +----------------------------------------------------+ 2.1.1 Modalita' di apertura: "r" sola lettura "r+" lettura e scrittura, dall'inizio "w" scrittura, tronca o crea il file "w+" lettura e scrittura, tronca o crea il file "a" scrittura in coda (append) "a+" lettura e scrittura in coda (append) 2.2 Scrittura su un file con fprintf() +----------------------------------------------------+ |int fprintf(FILE *stream, const char *format, ...); | +----------------------------------------------------+ 2.2.1 Sintassi della stringa formato come printf() 2.2.2 printf(...) equivalente a fprintf(stdout,...) 2.3 Lettura da un file con fscanf() +----------------------------------------------------+ |int fscanf(FILE *stream, const char *format, ...); | +----------------------------------------------------+ 2.3.1 Sintassi analoga a quella di fprintf() 2.3.2 fscanf() restituisce il numero di assegnazioni 2.3.3 scanf(...) equivalente a fscanf(stdin,...) 2.4 Chiusura di un file con fclose() +----------------------------------------------------+ |int fclose(FILE *fp); | +----------------------------------------------------+ 2.4.1 E' importante controllare il valore restituito soprattutto se si lavora su file system remoti 3 Esercizio: Scrivere una funzione che legge dallo standard input un insieme di numeri interi "int" (separati da spazi o '\n'), li riordina per valori crescenti e li scrive in standard output 3.1 Funzione feof(): restituisce 0|1 se il flusso e' finito +-------------------------------------------------------------------+ |#include | |#include | |#include | |struct node_t { | | int val; | | struct node_t *next; | |}; | |struct node_t *alloc_node(void) | |{ | | struct node_t *p; | | p = malloc(sizeof(struct node_t)); | | if (p == NULL) { | | fprintf(stderr, "Memory allocation error\n"); | | exit(EXIT_FAILURE); | | } | | return p; | |} | |void insert_after_node(struct node_t *new, struct node_t **pnext) | |{ | | new->next = *pnext; | | *pnext = new; | |} | |void insert_sorted_list(struct node_t *new, struct node_t **pnext) | |{ | | struct node_t *p; | | for (p = *pnext; p != NULL; pnext=&p->next, p=p->next) | | if (p->val > new->val) { | | /* inserisci new dopo pnext, ossia prima di p */ | | insert_after_node(new, pnext); | | return; | | } | | /* l'elemento va inserito in fondo alla lista */ | | insert_after_node(new, pnext); | |} | |void insert_value(int v, struct node_t **phead) | |{ | | struct node_t *new; | | | | new = alloc_node(); | | new->val = v; | | insert_sorted_list(new, phead); | |} | |void print_list(struct node_t *h) | |{ | | struct node_t *p; | | for (p=h; p != NULL; p = p->next) | | printf("%d ", p->val); | | printf("\n"); | |} | |int main(int argc, char *argv[]) | |{ | | struct node_t *list_head = NULL; | | int v; | | if (argc != 1) { | | fprintf(stderr, "Usage: %s [< file] [> file]\n", argv[0]); | | return EXIT_FAILURE; | | } | | while (feof(stdin) == 0) | | if (fscanf(stdin, "%d", &v) == 1) { | | insert_value(v, &list_head); | | } | | print_list(list_head); | | return EXIT_SUCCESS; | |} | +-------------------------------------------------------------------+ 4 Funzioni di I/O senza stringa formato 4.1 fgetc() e fputc() trasferiscono un singolo carattere +-------------------------------------------------------------+ |int fgetc(FILE *stream); | |int fputc(int c, FILE *stream); | +-------------------------------------------------------------+ 4.1.1 getchar() e putchar() sono applicate a stdin/stdout 4.2 fgets() legge una stringa terminata da '\n' o dalla condizione EOF +-------------------------------------------------------------+ |char *fgets(char *s, int size, FILE *stream); | +-------------------------------------------------------------+ 4.2.1 gets() legge da stdout <--- DA NON USARE!! (non ha size) 4.3 fputs() scrive una stringa terminata da '\0' +-------------------------------------------------------------+ |int fputs(const char *s, FILE *stream); | +-------------------------------------------------------------+ 4.3.1 puts() scrive su stdout e aggiunge '\n' 4.4 fread() legge da un file un vettore di strutture +-----------------------------------------------------------------+ |size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);| +-----------------------------------------------------------------+ 4.5 fwrite() scrive su un file un vettore di strutture +-----------------------------------------------------------------+ |size_t fwrite(const void *ptr, size_t size, size_t nmemb, | | FILE *stream); | +-----------------------------------------------------------------+ 5 Esercizio proposto: scrivere un programma che riceve il nome di un file come argomento, controlla le parole ripetute piu' di una volta, e scrive in standard output le 10 parole piu' ripetute, con le loro frequenze ====