)21'$0(17,',,1)250$7,&$,,

Dimensione: px
Iniziare la visualizzazioe della pagina:

Download ")21'$0(17,',,1)250$7,&$,,"

Transcript

1 )21'$0(17,',,1)250$7,&$,, (6(5&,7$=,21(Qƒ,//,1*8$**,2& (6(5&,=,2Qƒ Definire una macro ODD(i) che restituisca TRUE o FALSE a seconda che il valore intero passato sia rispettivamente dispari o pari. Poiché un intero è dispari se ha il bit meno significativo a 1, ed è pari in caso contrario, una possibile definizione può essere la seguente: #define ODD(i) ((i) & 1) (6(5&,=,2Qƒ Definire una macro CPL2(i) che restituisca il complemento a 2 del valore passatole come argomento. Poiché il complemento a 2 si ottiene sommando 1 al complemento a 1 del numero dato, si ha semplicemente: #define CPL2(a) (1 + (~a)) (6(5&,=,2Qƒ Definire una macro AND(a,b) che restituisca il risultato dell'operazione di AND logico fra i due parametri a e b. Ricordando che nell'operazione di AND il risultato è certamente zero se uno dei due operandi è falso, mentre è uguale all'altro operando in caso contrario, si può scrivere: #define AND(a,b) ((a)? (b) : 0) (6(5&,=,2Qƒ Definire una macro DEMORGAN_AND(a,b) che restituisca il risultato dell'operazione di AND logico fra i due parametri effettuando il calcolo sulla base delle identità del Teorema di De Morgan. Poiché è noto che: si ha immediatamente: A B = ( ( A) ( B) ) 7

2 #define DEMORGAN_AND( a, b ) ( ~( (~a) (~b) ) ) (6(5&,=,2Qƒ Definire una macro GENERIC_SWAP( NAME, ELEM_TYPE ) che definisca una funzione di nome NAME che effettui lo scambio (swap) fra due elementi di tipo ELEM_TYPE: NAME deve ricevere come parametri i puntatori ai due elementi da scambiare. In altri termini: GENERIC_SWAP(int_swap, int) dovrebbe definire una funzione int_swap capace di scambiare due interi GENERIC_SWAP(float_swap, float) dovrebbe definire una funzione float_swap capace di scambiare due valori di tipo float Poiché questa macro è decisamente più complessa di quelle fin qui viste, è necessario fare una precisazione. Come regola generale, le macro devono essere scritte su un'unica riga: qualora sia indispensabile andare a capo, è necessario, per indicare che il testo continua nella riga successiva, porre al termine della riga stessa un backslash \. Chiarito ciò, una macro che effettua quanto richiesto può essere la seguente: #define GENERIC_SWAP( NAME, ELEM_TYPE ) \ void NAME( a, b ) \ ELEM_TYPE *a, *b; \ ELEM_TYPE t; \ t = *a; *a = *b; *b = t; Nel primo dei due casi indicati nel testo dell'esercizio ( GENERIC_SWAP(int_swap, int) ) il risultato sarebbe quindi una funzione int_swap così definita: void int_swap ( a, b ) int *a, *b; int t; t = *a; *a = *b; *b = t; (6(5&,=,2Qƒ Scrivere una funzione che, a ogni invocazione, restituisca il successivo numero primo. Poiché il tempo di vita di una variabile locale di una funzione (variabile DXWRPDWLFD) è quello della funzione stessa, a ogni nuova invocazione delle funzione, tutte le variabili locali sono concettualmente vuote: quelle per le quali è specificato un valore iniziale sono inizializzate a tale valore, le altre hanno un contenuto indefinito. Sebbene questo comportamento sia fortemente desiderabile e opportuno nella stragrande maggioranza dei casi, perché garantisce l'indipendenza di una invocazione da tutte quelle passate e future, esistono casi (come quello in esame) in cui, per il particolare compito svolto da una funzione, è utile che una o più variabili (a tutti gli effetti, YDULDELOL 8

3 GL VWDWR) mantengano il loro valore fra un invocazione e l altra della funzione stessa. Questa necessità insorge particolarmente quando sono richieste IXQ]LRQL GL JHQHUD]LRQH di sequenze di valori che non devono ripetersi e in cui, eventualmente, ogni valore può dipendere dai precedenti. In questi casi, è utile la possibilità, offerta dal C, di GLFKLDUDUHVWDWLFGHOOHYDULDELOLLQWHUQHDXQD IXQ]LRQH. Il significato di questa dichiarazione, del tutto diverso da quello di un'analoga dichiarazione applicata a un oggetto esterno, è che XQD YDULDELOH ORFDOH GLFKLDUDWD VWDWLF PDQWLHQHLOVXRYDORUHIUDXQDFKLDPDWDHODOWUD della stessa funzione, il che è esattamente quanto ci serve per tenere traccia dell'ultimo numero primo generato. Costruiremo quindi una funzione nextprime, senza parametri, che a sua volta si avvarrà di una funzione isprime per controllare che un dato valore sia primo. Quest'ultimo controllo, esclusi i casi banali dei numeri 1 e 2, verrà attuato col metodo di Eratostene, verificando che il numero (dispari) dato non sia divisibile per alcun numero minore della sua radice quadrata. Il programma potrà allora presentarsi come segue: #include <stdio.h> #include <math.h> /* Include la libreria matematica (sqrt) */ #define NO 0 #define YES 1 int isprime(int n) /* Ritorna 0 o 1 secondo se n è primo */ int i, max=sqrt( (double)n ); if (n>0 && n<4) return YES; else if (!(n%2)) return NO; for(i=3; i<=max; i+=2) if (!(n%i)) return NO; return YES; int nextprime(void) static n=0; /* variabile statica con inizializzazione */ if (n>=0 && n<=2) return ++n; else do n+=2; while(!isprime(n)); return n; void main() int p,max; 9

4 do printf("\ngenerare i numeri primi fino a:\t"); scanf("%d",&max); while(max<1); do printf("\nnumero primo:\t%d", p=nextprime() ); while(p<max); L'unico elemento realmente innovativo è costituito dalla funzione nextprime, che contiene al suo interno una dichiarazione di variabile statica il cui valore viene mantenuto fra una chiamata e l'altra. Una variabile statica è sempre inizializzata automaticamente dal sistema a zero, a meno che non sia espressamente indicato un diverso valore. Tale inizializzazione avviene una sola volta, concettualmente SULPD dell'inizio dell'esecuzione del programma. Perciò la variabile n viene azzerata (da codice generato ad hoc dal compilatore) ogni volta che il programma viene ULHVHJXLWR, e QRQ D RJQL LQYRFD]LRQH della funzione nextprime, come invece avverrebbe per una normale variabile locale. (6(5&,=,2Qƒ Scrivere una funzione ULFRUVLYD che sommi i primi 1 numeri naturali. In C, una funzione può, se ciò è utile e necessario, richiamare se stessa, direttamente o indirettamente (cioè attraverso altre funzioni): può, cioè, essere ULFRUVLYD. Dal punto di vista concettuale, non c'è alcuna differenza fra chiamare un'altra funzione o la stessa funzione: in entrambi i casi, infatti, vi è un FOLHQWH (main o altra funzione) che si rivolge a un VHUYLWRUH per l espletamento di un particolare compito. Come caso particolare, una funzione può utilizzare se stessa come suo servitore, qualora ciò sia utile. Conseguentemente, anche il linguaggio tratta i due casi esattamente nello stesso modo. Tecnicamente, ciò è possibile perché ogni attivazione di una nuova funzione produce immediatamente la creazione (sul momento) del relativo DPELHQWH, ossia di una zona con tutti i parametri e tutte le variabili locali della funzione stessa: ergo, ogni nuova attivazione di funzione (anche della stessa funzione) è indipendente da tutte le attivazioni precedenti. Per affrontare un problema con approccio ricorsivo, bisogna innanzitutto modificare il proprio modo di pensare, cambiando metodo rispetto al "classico" approccio iterativo. Infatti, mentre quest'ultimo richiede di cogliere l'essenza del problema ed essere in grado di esprimere il passo generico che porta alla soluzione, l'approccio ricorsivo si limita a tentare di DEEDVVDUHLOJUDGRGLGLIILFROWj del problema, esprimendone la soluzione in termini di alcuni passi elementari HLQROWUHdella soluzione dello stesso problema, in un caso più semplice. Nel caso in esame, anziché vedere il problema come accumulazione progressiva di valori con relativi risultati parziali, per evidenziare un SDVVRHOHPHQWDUHGLGHFRPSRVL]LRQH si può osservare che se 1=0 la somma è banalmente zero, mentre se 1>0 la somma vale 1 più la somma dei valori fino a 1-1. Quest'ultima somma non è altro che la soluzione del medesimo problema (sommare un certo insieme di numeri), applicato però al caso (più semplice) di 1-1 valori anziché a 1valori. 10

5 Questo porta a scrivere direttamente il codice della soluzione che si presenta come segue. #include <stdio.h> int sum_to(unsigned int n) if (n==0) return 0; else return n+sum_to(n-1); main() int n; printf("\nintrodurre N:\t"); scanf("%d",&n); printf("\nsomma fino a %d:\t%d\n", n, sum_to(n)); La funzione sum_to opera in modo concettualmente assai semplice: se Q è zero, il risultato della somma è zero; altrimenti, esso è pari alla somma di tutti gli Q numeri precedenti (il cui calcolo è delegato alla stessa sum_to), più Q stesso. Ad esempio, per Q=4 si ha: sum_to(4) = 4 + sum_to(3) = = 4 + ( 3 + sum_to(2) ) = = 4 + ( 3 + ( 2 + sum_to(1) ) ) = = 4 + ( 3 + ( 2 + (1 + sum_to(0) ) ) ) = = 4 + ( 3 + ( 2 + (1 + 0 ) ) ) = /* */ = 4 + ( 3 + ( ) ) = = 4 + ( ) = = = = 10. Concretamente, ogni espansione di sum_to corrisponde alla creazione di un ambiente per una nuova attivazione di questa funzione. Quindi, in corrispondenza del punto, vi sono cinque attivazioni di sum_to contemporaneamente attive, ognuna in attesa che la successiva produca il valore necessario per concludere la rispettiva operazione. Da in poi, avendo l ultima chiamata di sum_to fornito direttamente un risultato (0), le varie invocazioni iniziano a chiudersi, e inizia a formarsi il risultato finale. Questa successione di chiamate ricorsive può essere seguita col debugger del Turbo C, tramite l'opzione FDOOVWDFN del menù GHEXJ. (6(5&,=,2Qƒ Scrivere una funzione ricorsiva che stampi una parola rovesciata.. Per stampare a rovescio (cioè dall'ultima lettera alla prima) una parola, si può partire dall'idea di stampare innnanzitutto l'ultimo carattere, e poi stampare rovesciati (se esistono) tutti gli altri 11

6 caratteri. Con questa semplice osservazione, si è espresso il problema di stampare rovesciata una parola lunga Q tramite un'operazione elementare (la stampa del singolo carattere) e dello stesso problema in un caso più semplice (stampare rovesciati i precedenti Q-1 caratteri). La funzione richiesta può quindi agire esattamente in questo modo. Più precisamente, se la stringa passatale è lunga zero non fa nulla, se è lunga uno la stampa e termina, mentre se è lunga più di uno stampa l'ultimo carattere e richiama se stessa sulla sottostringa iniziale (privata, cioè, dell'ultimo carattere). Una possibile codifica può essere la seguente: #include <stdio.h> #include <string.h> #define MAXLEN 30 void print_rev(char word[]) int len=strlen(word); char w[maxlen]; if (len<1) return; else if (len==1) putchar(word[0]); else strncpy(w, word, len-1); w[len-1]= \0 ; putchar(word[len-1]); print_rev(w); return; main() int n; char parola[maxlen]; printf("\nintrodurre una parola:\t"); scanf("%s",&parola); print_rev(parola); 12

7 (6(5&,=,2Qƒ Realizzare una funzione che risolva ricorsivamente il problema della 7RUUHGL+DQRL. La Torre di Hanoi Si tratta di un antico gioco, in cui sono date tre torri (sinistra, centrale, e destra) e un certo numero di dischi forati. I dischi hanno diametro diverso gli uni dagli altri, e inizialmente sono infilati uno sull'altro (dal basso in alto) dal più grande al più piccolo sulla torre di sinistra. Scopo del gioco è portarli tutti sulla torre di destra, usando quella centrale come appoggio (torre di transito), e rispettando due regole: a) si può muovere un solo disco alla volta; b) un disco più grande non può mai stare sopra a un disco più piccolo. Una soluzione generale, non ricorsiva, è molto difficile da dare, soprattutto quando il numero dei dischi supera poche unità (già 4 non è banale). La soluzione ricorsiva, invece, è immediata: per spostare Q dischi dalla torre di sinistra alla torre di destra basta supporre di saper spostare gli Q dischi superiori in quella centrale, muovere quello rimasto dalla torre di sinistra a quella di destra, e infine rispostare gli Q superiori da quella centrale a quella di destra. Ciò equivale a dire che il problema di grado Q può essere ricondotto a una mossa elementare e a due problemi di grado Q, che possono essere a loro volta risolti procedendo nello stesso modo. Ovviamente, a forza di decomporre il problema si arriva al punto di spostare un solo disco, che è una mossa elementare fattibile direttamente. Questa strategia, ancorché semplice, non è comoda da gestire a mano: viceversa è adattissima a un calcolatore. Il programma che la implementa è riportato qui di seguito. #include <stdio.h> typedef enum SINISTRA, CENTRALE, DESTRA torre; void hanoi(int d, torre start, torre end, torre transit); main() int d; printf("\ntorre DI HANOI\n\n"); printf("\nquanti dischi?\t"); scanf("%d", &d); printf("\n\nsoluzione:\n"); hanoi(d, SINISTRA, DESTRA, CENTRALE); return 0; void hanoi(int d, torre iniziale, torre finale, torre ausiliaria) if (d==1) printf("muovi un disco dalla torre %d alla torre %d\n", iniziale, finale); return; 13

8 else hanoi(d-1,iniziale, ausiliaria, finale); printf("muovi un disco dalla torre %d alla torre %d\n", iniziale, finale); hanoi(d-1, ausiliaria, finale, iniziale); return; Osserviamo che per modellare la tre torri, si è introdotto, tramite la direttiva typedef, un nuovo tipo torre, che da questo momento è liberamente usabile al pari dei tipi predefiniti del C (int, char, etc). Quindi, sarà possibile, in particolare, definire e usare variabili di tipo torre. 1 Concretamente, essendo questo nuovo tipo definito come un enumerazione, i tre identificatori SINISTRA, CENTRALE e DESTRA sono associati ai tre valori interi 0, 1 e 2: perciò l'output del programma si esprime in termini di "torre 0, torre 1, torre 2". Naturalmente sarebbe semplice prevedere forme di output più chiare e leggibili (ad esempio facendo stampare sinistra in luogo di torre 0, etc.): questo miglioramento è lasciato al lettore. Piuttosto, interessa sottolineare che la funzione hanoi realizza esattamente la logica sopra riportata: infatti, nel caso di un solo disco lo muove direttamente, altrimenti prima ne sposta Q, poi sposta quello rimasto, indi risposta nuovamente i precedenti Q. Le tre torri iniziale, finale e ausiliaria naturalmente variano da una chiamata all'altra, perché i vari sottoproblemi devono in generale spostare dei dischi fra due generiche torri (p. es. dalla torre centrale a quella di destra), per la qual operazione sfruttano come transito la terza torre (ad es. quella di sinistra). (6(5&,=,2Qƒ Realizzare una funzione che copi una stringa in un'altra tramite puntatori. Il problema della copiatura di una stringa in un'altra è interessante perché evidenzia i limiti della "analogia" fra puntatori e vettori. Per questo, nel seguito sono presentate e commentate tre versioni: una errata, le altre due entrambe corrette ma con diversa semantica. Una prima idea può essere quella di assegnare direttamente un vettore di caratteri a un'altro, come nell'esempio che segue: #include <stdio.h> main() char s1[80],s2[80]; printf("\nstringa 1:\t"); scanf("%s",s1); 1 Maggiori dettagli sulla direttiva typedef si possono trovare nel commento all esercizio n

9 V V; /* ATTENZIONE: ERRATO!! */ printf("\nstringa 1:\t%s",s1); printf("\nstringa 2:\t%s",s2); printf("\nmodifica stringa 1:\t"); scanf("%s",s1); printf("\nstringa 1:\t%s",s1); printf("\nstringa 2:\t%s",s2); Tuttavia, assegnare direttamente una stringa a un altra (qui, s1 a s2) QRQ VLJQLILFD FRSLDUOD ma soltanto (tentare di) assegnare all una l indirizzo dell altra (qui, l indirizzo di s1 all indirizzo di s2). Ciò è però errato, non essendo ovviamente possibile cambiare l'indirizzo a cui si trova un vettore. Una tale operazione, DQFRUFKp OLPLWDWD DL VROL SXQWDWRUL, è invece lecita se alla sinistra dell'assegnamento compare un SXQWDWRUHDFKDU, come nell'esempio che segue. #include <stdio.h> main() char s1[80], V; printf("\nstringa 1:\t"); scanf("%s",s1); V V; /* LECITO, ma NON DUPLICA LA STRINGA! */ printf("\nstringa 1:\t%s",s1); printf("\nstringa 2:\t%s",s2); printf("\nmodifica stringa 1:\t"); scanf("%s",s1); printf("\nstringa 1:\t%s",s1); printf("\nstringa 2:\t%s",s2); Ora l'assegnamento è sintatticamente lecito, tuttavia esso SURGXFHVRORXQDFRSLDGLSXQWDWRUL, non delle variabili da essi puntate! Perciò, s1 e s2 referenziano ora entrambi ODVWHVVDVWULQJD, e dunque qualunque modifica fatta sull'una si ritrova immediatamente sull'altra. Per copiare davvero una stringa in un'altra è invece necessaria una copia fisica, uno per volta, di tutti gli elementi, che si può attuare ad esempio tramite un ciclo: #include <stdio.h> void strcpy(char *s, char *t) while((*s=*t)!= \0 ) s++; t++; 15

10 main() char s1[80], /* stavolta, s2 deve avere lo spazio */ printf("\nstringa 1:\t"); scanf("%s",s1); strcpy(s2,s1); printf("\nstringa 1:\t%s",s1); printf("\nstringa 2:\t%s",s2); printf("\nmodifica stringa 1:\t"); scanf("%s",s1); printf("\nstringa 1:\t%s",s1); printf("\nstringa 2:\t%s",s2); Osserviamo che la funzione strcpy, scritta in questo modo, è ridondante: un programmatore esperto l'avrebbe probabilmente sintetizzata così: void strcpy(char *s, char *t) while(*s++=*t++); Infatti, gli aggiornamenti dei due puntatori possono essere fatti efficacemente con due postincrementi, e il confronto col carattere '\0' è, al solito, sovrabbondante. Osserviamo anche che, per quanto sia stretta la correlazione fra i due, YHWWRULHSXQWDWRULQRQVRQR GXHFRQFHWWLLGHQWLFL. Ciò è confermato dal seguente esempio: char msg[] = "testo del messaggio"; /* msg è un VETTORE */ char *msg = "testo del messaggio"; /* msg è un PUNTATORE */ Nel primo caso, msg è il nome di un vettore, la cui lunghezza è fissata staticamente: perciò, i singoli caratteri entro il "testo del messaggio" possono cambiare, ma msg si riferisce sempre alla stessa area di memoria. Non gli si può quindi assegnare, come nel primo degli esercizi precedenti, un altro indirizzo, perché il nome msg è univocamente associato a questa zona di memoria, e non ad altre. Nel secondo caso, invece, essendo msg un puntatore, può essere fatto puntare altrove, ma il contenuto della stringa è una costante che non dovrebbe essere modificata. Tentando di farlo, il risultato è, in generale, indefinito 2. (6(5&,=,2Qƒ 2 La ragione di questo è abbastanza evidente: si pensi a cosa potrebbe accadere se più stringhe condividono, nella rappresentazione dei dati organizzata dal sistema. delle sottostringhe... 16

11 Scrivere una funzione C in grado di riconoscere identificatori, ovvero sequenze di caratteri che rispettino la seguente grammatica: <identificatore> ::= <lettera> <lettera> <caratteri> <caratteri> ::= <lettera> <caratteri> <cifra> <caratteri> <lettera> ::= _ a b c... z A B C... Z <cifra> ::= Tradotto dal formalismo, si tratta evidentemente di riconoscere sequenze di caratteri che cominciano con una lettera o un underscore, e proseguono con lettere, undescore o cifre. Quindi, ci è richiesta una funziona che riceva in ingresso una stringa e, sostanzialmente, risponda SI o NO (oppure TRUE o FALSE, o 1 o 0, eccetera) a seconda che essa rispetti tale sintassi e costituisca, perciò, un identificatore, o meno. Da notare che è opportuno, per generalità, prevedere l'eventualità che esistano spazi iniziali, che ovviamente devono essere saltati. Una funzione che fa questo può essere la seguente. #define NULL 0 char *identifier( char *s ) while ( isspace(*s) ) s++; if (!(isalpha(*s)) && *s!= _ ) return NULL; else s++ ; while ( isalpha(*s) isdigit(*s) *s== _ ) s++ ; if ( isspace(*s) *s== \0 ) return s; else return NULL; ; Questa funzione non soltanto verifica la sintassi degli identificatori: restituisce anche un puntatore al primo carattere nella stringa che non corrisponde alla sintassi (ad esempio uno spazio che separa l'identificatore stesso dalla parola successiva). Qualora la verifica dia esito negativo, per convenzione il valore ritornato è NULL. (6(5&,=,2Qƒ Scrivere una funzione C in grado di riconoscere espressioni matematiche che rispettino la seguente grammatica (che costituisce una versione semplificata di quella illustrata nella lezione n. 34): <espressione> ::= <termine> <termine> + <termine> <termine> - <termine> <termine> ::= <fattore> <fattore> * <fattore> <fattore> / <fattore> <fattore> ::= <identificatore> <numero> ( <espressione> ) In primo luogo, osserviamo che occorrerà disporre di idonee funzioni atte a riconoscere identificatori e numeri, che sono le entità di più basso livello trattate dalla grammatica. Rimandando per gli identificatori all'esercizio precedente, tralasceremo in questa sede di presentare il codice della 17

12 funzione che riconosce numeri, in quanto facilmente realizzabile sulla scorta degli esercizi precedenti; ci concentreremo invece sulla parte, più innovativa, di riconoscimento delle espressioni. Sebbene l'operazione di riconoscimento in sé sia non banale, a causa soprattutto della definizione ricorsiva dei fattori in termini di espressioni, la possibilità offerta dal C di specificare funzioni ricorsive consente di ottenere lo scopo con un codice soprendentemente breve e compatto, come mostra l'esempio sotto riportato. #define NULL 0 char *term(char *); /* dichiarazioni delle funzioni successive */ char *factor(char *); char *identifier(char *); /* vedere esercizio precedente */ char *number(char *); /* da fare a cura del lettore */ char *expression (char *s) char *t; printf("\nespressione..."); t = term(s); if ( t == NULL ) return NULL; else s = t; while ( isspace(*s) ) s++ ; if (*s!= - && s*!= + ) return s; /* è un puro termine */ else s++; while ( isspace(*s) ) s++ ; t = term(s); if ( t == NULL ) return NULL; else return t; char *term (char *s) char *f; printf("...termine..."); f = factor(s); if ( f == NULL ) return NULL; else s = f; while ( isspace(*s) ) s++ ; if (*s!= '*' && *s!= '/' ) return s; /* è un puro fattore */ else s++; while ( isspace(*s) ) s++ ; f = factor(s); if ( f == NULL ) return NULL; else return f; char *factor (char *s) char *t; 18

13 printf("...fattore..."); t = identifier(s); if ( t!= NULL ) return t; else t = number(s); if ( t!= NULL ) return t; else /* espress. fra parentesi */ printf("...(espressione)..."); while ( isspace(*s) ) s++ ; if (*s!= ( ) return NULL; else s++; while ( isspace(*s) ) s++ ; t = expression(s); /* chiamata ricorsiva!! */ if ( t == NULL ) return NULL; else s=t; while ( isspace(*s) ) s++ ; if (*s!= ) ) return NULL; else return ++s; /* un esempio di main */ main() char s[250]; char *t; printf("\nintrodurre l espressione:\t"); scanf("%s", &s); t = expression(s); if ( t==null ) printf("\nespressione errata\n"); else printf("\nespressione OK\nParte residua non riconosciuta:%s\n",t); Come si può vedere, la funzione che analizza i termini è del tutto analoga a quella che analizza le espressioni, in ossequio al fatto che la struttura sintattica delle due entità è assolutamente analoga: in effetti, tutta la differenza sta nel nome dell'entità analizzata e negli operatori (additivi in un caso, moltiplicativi nell'altro) accettati e riconosciuti. Per questo, descriveremo solo la prima. Nella funzione expression, in primo luogo si verifica la presenza di un termine: se esso non esiste, la funzione termina e fallisce restituendo NULL. Altrimenti, si cerca il primo carattere non-spazio e si controlla se si tratta di un operatore di somma ( + o - ): se così non è si termina, restituendo il puntatore a tale elemento non identificato. Se invece si riscontra la presenza di "+" o "-", si parte, tolti gli eventuali spazi, alla ricerca di un nuovo termine. Di nuovo, se esso non viene trovato la funzione fallisce restituendo NULL, diversamente ritorna il puntatore (fornito dalla funzione term) al primo carattere non identificato. La funzione factor è sostanzialmente simile, anche se formalmente diversa. In questo caso, si cerca in primo luogo un identificatore: se lo si trova si termina restituendo, al solito, il puntatore (fornito da identifier) al primo elemento non identificato; altrimenti, e qui è la sola reale differenza, si cambia strada (ovvero si tenta di applicare una diversa regola di produzione), controllando se non si tratti per caso di un numero. Di nuovo, se così è, si restituisce il puntatore al primo carattere non identificato, altrimenti si passa a tentare la terza e ultima via: quella corrispondente a un fattore realizzato racchiudendo una espressione fra parentesi. 19

14 Tolti gli spazi, si procede allora alla ricerca di una parentesi tonda aperta (fallendo se non la si trova), quindi si richiama ricorsivamente la funzione expression originaria per l analisi del contenuto delle parentesi (fallendo se da essa risulta che non si tratta di una espressione corretta), infine si cerca la parentesi chiusa, fallendo se manca. Da notare che, per come è definita la grammatica, vi sono alcune differenze rispetto al modo usuale di scrivere le espressioni: in particolare, ogni operatore di un dato livello deve agire esclusivamente su due entità ben definite, eventualmente delimitate da parentesi. Ne segue che A * 5 4 / 3 - k2 sono legali, mentre a * 5 / k + 8 sono errate; la versione corretta sarebbe: a + (3+5) 4 * (5/8) (5-k) + 8 Questa apparente complicazione è alla base della semplicità con cui è stato possibile scrivere il codice: la grammatica completa della lezione 34 sarebbe stata decisamente più complessa, essenzialmente a causa del fatto che gli operatori devono risultare associativi a sinistra (ovvero, deve essere letto come (7-4)-6, e non come 7-(4-6) ), il che richiede un'analisi della stringa più sofisticata, con capacità di "guardare avanti". Non approfondiamo oltre questo aspetto ora: chi desidera può comunque provare a riflettere su come differenze grammaticali anche piccole possano comportare notevoli complicazioni nell'analizzatore corrispondente. (6(5&,=,2Qƒ Realizzare una programma che ricopi il file dato come primo argomento nel file dato come secondo argomento, assicurandosi di non avere mai linee più lunghe di 40 caratteri. Si tratta evidentemente di operare in due fasi: in primo luogo analizzare i parametri di ingresso recuperando i nomi dei file su cui agire, aprendo tali file rispettivamente in lettura e in scrittura (ed emettendo, in mancanza, opportuni messaggi d'errore), e in secondo luogo attivare un ciclo di copiatura che, ogni 40 caratteri letti, inserisca nel nuovo file il carattere di QHZOLQH. Una possibile codifica può essere la seguente: #include <stdio.h> #define MAXCHAR 40 main(int argc, char **argv) FILE *fin, *fout; void stop(char *); int count=0, ch; if (argc==3) fin=fopen(argv[1],"r"); fout=fopen(argv[2], "w"); if (fin==null) stop("impossibile aprire file d ingresso\n"); 20

15 if (fout==null) stop("impossibile aprire file d uscita\n"); else stop("manca qualche parametro\n"); while(!feof(fin)) ch=fgetc(fin); fputc(ch, fout); if (++count>maxchar) fputc( \n, fout); count=0; fclose(fin); fclose(fout); exit(0); void stop(char *msg) fprintf(stderr,msg); exit(1); La funzione feof garantisce l uscita dal ciclo al termine del file d ingresso; la funzione stop, da noi definita, stampa invece un messaggio d errore e fa abortire il programma. Da notare il controllo sui parametri d'ingresso, che tenta di fornire una messaggistica il più possibile vicina al tipo di errore verificatosi. Osserviamo che questa versione del programma non resetta il contatore qualora il testo d'ingresso contenga già al suo interno dei newline: volendolo fare, basta cambiare la parte centrale del ciclo di copiatura come segue. while(!feof(fin)) if ((ch=fgetc(fin))== \n ) count=0; fputc(ch, fout); if (++count>maxchar) fputc( \n, fout); count=0; (6(5&,=,2Qƒ Scrivere un programma che appenda al file dato come primo argomento il file dato come secondo argomento. 21

16 Anche qui, in funzione dei parametri di ingresso occorre aprire un file in lettura, e l altro in modo append, ovviamente intercettando e segnalando opportunamente le situazioni anomale; quindi, si attiverà un ciclo di copiatura carattere per carattere. Il programma risultante è quello sotto riportato. #include <stdio.h> main(int argc, char **argv) FILE *fin, *fapp; void stop(char *); if (argc==3) fapp=fopen(argv[1],"a"); fin=fopen(argv[2], "r"); if (fin==null) stop("impossibile aprire file d ingresso\n"); if (fapp==null) stop("impossibile aprire file d uscita\n"); else stop("manca qualche parametro\n"); while(!feof(fin)) fputc(fgetc(fin), fapp); fclose(fin); fclose(fapp); exit(0); void stop(char *msg) fprintf(stderr,msg); exit(1); Osserviamo il ciclo di copiatura, che, non essendo più necessario salvare in una variabile il carattere letto, è ridotto all'osso, al punto da scriversi sostanzialmente in un'unica istruzione while. (6(5&,=,2Qƒ Scrivere un programma che sostituisca tutte le minuscole in maiuscole nel file dato come (unico) argomento. Per risolvere il problema, occorre evidentemente essere in grado, in buona sostanza, di aprire un file effettuando sia delle letture sia, ove necessario (in particolare: ove il carattere letto sia una minuscola), delle scritture (qui per sostituire quel carattere con la corrispondente maiuscola). Inoltre, è necessario potersi collocare in una ben precisa posizione sul file, onde sovrascrivere esattamente ciò che si vuole sostituire, e non un carattere qualsiasi. A questo scopo, servono alcune caratteristiche della gestione file del C che fanno parte delle cosiddette IXQ]LRQLGLDJJLRUQDPHQWR ILOHXSGDWHIXQFWLRQV. 22

17 In primo luogo, osserviamo che le modalità di lettura, o di append, o di scrittura fin qui utilizzate per aprire i file sono insufficienti, in quanto le prime due SUHVXSSRQJRQR FKH LO ILOH HVLVWD, provocando il fallimento della fopen in caso contrario, mentre la modalità di scrittura se il file non esiste lo crea, altrimenti lo apre ma FDQFHOODQGR QHO FRQWHPSR RJQL SUHFHGHQWH FRQWHQXWR. In tal caso, la fopen può fallire solo se non è fisicamente possibile creare il file. Per aprire un file esistente senza distruggerlo, ma con la possibilità di modificarne il contenuto, serve una nuova modalità di apertura, detta PRGDOLWjGLDJJLRUQDPHQWR e indicata dall'aggiunta, in coda alla normale specifica di apertura, dello specificatore. Si possono quindi avere due modalità di base "r+" e "w+". Ognuna mantiene le sue caratteristiche di base: quindi, "r+" presuppone che il file esista, lo apre in lettura ma consente anche (alternativamente) di scriverci sopra, mentre "w+", pur consentendo delle letture, mantiene la propria caratteristica base, creando il file se non esiste o cancellandolo completamente se già esiste. La modalità "a+" è analoga a "r+", con la differenza di iniziare ad agire in coda al file. Nel nostro caso, dato che si tratta di agire su un file che non si vuole distruggere o riscrivere, ma soltanto modificare VHOHWWLYDPHQWH in alcuni punti (precisamente, in presenza delle minuscole), la modalità necessaria è chiaramente la "r+". Aperto quindi il file fornito dal parametro di ingresso, e intercettate le eventuali situazioni anomale, occorrerà avviare un ciclo di letture carattere per carattere, controllando a ogni carattere letto se si tratti di una minuscola. In caso positivo, occorrerà retrocedere di una posizione (perché la testina virtuale di lettura/scrittura di un file si sposta concettualmente in avanti di una posizione subito dopo aver letto o scritto un carattere), e sovrascrivere tale carattere con la maiuscola corrispondente. Questi posizionamenti sul file possono essere effettuati con la funzione di libreria fseek. Il programma risultante è quello sotto riportato. #include <stdio.h> #include <ctype.h> main(int argc, char **argv) FILE *file; void stop(char *); int ch; if (argc==2) if ((file=fopen(argv[1], "r+"))==null) stop("impossibile aprire file d ingresso\n"); else stop("manca il nome del file da elaborare\n"); while((ch=fgetc(file))!=eof) if(islower(ch)) fseek(file, ftell(file)-1, SEEK_SET); fputc(toupper(ch), file); fseek(file, 0, SEEK_CUR); fclose(file); exit(0); 23

18 void stop(char *msg) fprintf(stderr,msg); exit(1); La funzione fseek è doppiamente essenziale: serve infatti sia per posizionarsi su una data posizione nel file, VLD D FRQVHQWLUH ODOWHUQDQ]D GL OHWWXUH H VFULWWXUH. In effetti, la semantica del modo di aggiornamento "+" (abbinato a uno qualunque dei modi "r", "w", e "a") ULFKLHGH HVSOLFLWDPHQWHche, dopo una sequenza di letture, e prima di iniziare delle scritture, venga usata una delle funzione di riposizionamento su file. Lo stesso vale dopo una sequenza di scritture, prima di passare a eseguire nuovamente delle letture. La sintassi completa di fseek (e della funzione ausiliaria ftell) è riassunta nel riquadro sottostante. 6LQWDVVLGLIVHHNHIWHOO int fseek(file *f, long offset, int origin); long ftell(file *f); dove: offset dà la posizione, rispetto a origin, a cui portarsi sul file origin dà la posizione, rispetto a cui misurare lo scostamento, e può valere: SEEK_SET per indicare l'inizio del file SEEK_CUR per indicare la posizione corrente nel file SEEK_END per indicare la fine del file PER UN FILE DI TESTO: - offset deve essere o zero, o un valore restituito da ftell. In quest'ultimo caso, origin deve obbligatoriamente valere SEEK_SET. PER UN FILE BINARIO: - la nuova posizione si trova a offset caratteri da origin. Nel nostro caso, la funzione fseek è necessaria una prima volta (con scostamento ftell(file)-1 rispetto all'inizio del file) per retrocedere di una posizione, tornando in corrispondenza del carattere appena letto (una minuscola da sostituire), e una seconda volta (con offset zero rispetto alla posizione corrente) semplicemente per consentire, alla successiva iterazione del ciclo, una lettura: in effetti, in questo secondo caso non si effettua alcun reale riposizionamento. Il ciclo che costituisce il cuore del programma avrebbe anche potuto essere riformulato utilizzando, per esprimere la condizione di controllo, la funzione feof: while(!feof(file)) ch=fgetc(file); if(islower(ch)) fseek(file, ftell(file)-1, SEEK_SET); fputc(toupper(ch), file); fseek(file, 0, SEEK_CUR); 24

19 o anche: while(!feof(file)) if(islower(ch=fgetc(file))) fseek(file, ftell(file)-1, SEEK_SET); fputc(toupper(ch), file); fseek(file, 0, SEEK_CUR); 25

20 (6(5&,=,2Qƒ Scrivere un programma che salvi in un file, dato come argomento, un vettore di interi. Finora l'unico tipo di file considerato è stato il file di caratteri, o ILOH GL WHVWR. Tuttavia, è spesso necessario salvare su file dati che QRQ sono caratteri. In questi casi, è assai utile disporre di un supporto più generale di quello costituito dai file di testo: a questo scopo, il linguaggio C offre il concetto di )LOH%LQDULR. Un file binario serve a supportare qualunque tipo di file, in particolare file che QRQ contengono caratteri. Poiché un file binario non è un file di caratteri, ODILQHGHOILOH deve essere necessariamente testata tramite la primitiva feof, non potendosi più applicare il confronto fra un carattere letto e il carattere EOF. Per leggere e scrivere su un file binario il linguaggio mette a disposizione le due primitive fread e fwrite, la cui sintassi è riassunta nel riquadro sottostante. 6LQWDVVLGLIUHDGHIZULWH size_t fread(void *vet, size_t size, size_t n, FILE *f); size_t fwrite(const void *vet, size_t size, size_t n,file *f); dove: vet n f è un (puntatore a un) vettore di elementi, ognuno di ampiezza size è il numero di elementi da leggere o scrivere è il file (binario) La prima legge dal file, collocandoli nel vettore vet, al più n oggetti, ciascuno di ampiezza pari a size, e restituisce il numero di oggetti effettivamente letti, che può essere minore di quanto richiesto (se il file conteneva meno elementi). Per determinare lo stato di terminazione si devono usare le funzioni feof e ferror. La funzione fwrite viceversa scrive sul file, prelevandoli dal vettore vet, n oggetti, ciascuno di ampiezza pari a size, e restituisce il numero di oggetti scritti, che può essere inferiore al richiesto solo in caso di errore (come ad esempio l'esaurimento di spazio su disco). Usando queste primitive, il problema dato si risolve semplicemente passando a fwrite gli opportuni parametri: il programma si potrà presentare allora come sotto illustrato. #include <stdio.h> main(int argc, char **argv) FILE *file; void stop(char *); int i, n; int tab[]=3, 6, -12, 5, -76, 3, 32, 12, 65, 1, 0, -9 ; if (argc==2) if ((file=fopen(argv[1], "wb"))==null) 26

21 stop("impossibile aprire file d uscita\n"); else stop("manca il nome del file di uscita\n"); n = sizeof(tab)/sizeof(tab[0]); fwrite(tab, sizeof(tab[0]), n, file); fclose(file); exit(0); void stop(char *msg) fprintf(stderr,msg); exit(1); Si noti il metodo, del tutto generale, usato per determinare il numero di elementi nel vettore, come rapporto fra la dimensione del vettore e quella di un suo generico elemento. Da notare che questo programma non verifica che siano stati effettivamente scritti tutti gli elementi: in una applicazione reale, il valore di ritorno di fwrite dovrebbe essere controllato, gestendo il caso in cui l operazione non abbia potuto essere completata. 27

22 (6(5&,=,2Qƒ Scrivere un programma che che legga da un file, dato come argomento, una lista di interi, ponendola in un vettore. Si tratta evidentemente del problema duale del precedente, risolvibile semplicemente usando opportunamente la primitiva fread. Una possibile codifica può quindi essere la seguente: #include <stdio.h> #define MAX 40 main(int argc, char **argv) FILE *file; void stop(char *); int i, n, tab[max]; if (argc==2) if ((file=fopen(argv[1], "rb"))==null) stop("impossibile aprire file d ingresso\n"); else stop("manca il nome del file d ingresso\n"); n=fread(tab, sizeof(tab[0]), MAX, file); fclose(file); for(i=0;i<n;i++) printf("%d%c", tab[i], (i==n-1)? \n : \t ); exit(0); void stop(char *msg) fprintf(stderr,msg); exit(1); Si noti lo sfruttamento della semantica di fread al fine di ottenere "gratis" il numero effettivo di elementi letti come valore di ritorno (salvato in n), fornendo nel contempo MAX come numero massimo di elementi da leggere in modo da evitare di acquisire più elementi di quanti il vettore ne possa contenere. (6(5&,=,2Qƒ Scrivere un programma che stampi una lista di interi su video, stampante o file a seconda del parametro passato come argomento Per ottenere lo scopo, la soluzione più semplice è adottare la primitiva fprintf, passandole come FILE* una variabile opportunamente settata, in precedenza, o uno dei file standard (stdout o stdprn rispettivamente per video e stampante) o il risultato della fopen nel caso di un file su disco. Per comodità, si può stabilire che il default sia lo schermo (e quindi la variabile FILE* valga inizialmente stdout). Il programma assume quindi un aspetto come il seguente: 28

23 #include <stdio.h> main(int argc, char **argv) FILE *out=stdout; char lastch= \n ; int diskfile=0, i, n; int tab[]= 12, 54, -34, 65, 978, 19, 32 ; if (--argc>0) if (strcmp(argv[argc], "prn:")==0) out=stdprn; lastch= \f ; else out=fopen(argv[argc], "a"); diskfile=1; n=sizeof(tab)/sizeof(tab[0]); for(i=0;i<n;i++) fprintf(out, "%d%c", tab[i], (i==n-1)? lastch : \t ); if (diskfile) fclose(out); return 0; Dal punto di vista pratico, il programma emette tutto il proprio output sul file out, indipendentemente da FRVD esso realmente sia. A sua volta, il file out, inizializzato per default a stdout, viene settato su stdprn se l argomento passato vale "prn:", o al file su disco corrispondente al nome fornito, se è presente un qualunque altro argomento. Come si è già accennato, stdprn è lo VWDQGDUG SULQWLQJ GHYLFH, aperto anch'esso automaticamente dal sistema all'inizio dell'esecuzione (come stdin, stdout e stderr). Ogni output diretto su stdprn viene, di fatto, inviato alla stampante. Analogamente, esiste anche lo stream standard stdaux, che corrisponde allo VWDQGDUGDX[LOLDU\GHYLFH, la cui funzione dipende da quale dispositivo fisico (porte seriali, o, altro) è realmente connesso al dispositivo logico "aux:". Il carattere lastch, settato per default a un QHZOLQH, viene modificato nel caso della stampante al carattere \f (form feed) per provocare un salto di pagina. Da notare, infine, ODFXUDSRVWDQHOOHYLWDUHGLFKLXGHUHSHUVHPSUHVWGRXW, dato che non c'è modo di riaprire esplicitamente uno dei file standard, una volta chiusi (le funzioni fopen e freopen, mostrata nell'esempio successivo, richiedono sempre esplicitamente, infatti, un nome di file). A questo scopo, il flag diskfile condiziona l'operazione di chiusura all'essere out un file su disco precedentemente aperto con fopen, impedendola se out risulta inizializzato a stdout (come accade ad esempio in assenza di parametri). 29

24 (6(5&,=,2Qƒ Scrivere un programma che stampi una lista di interi su video o su file a seconda del parametro passato come argomento, UHGLULJHQGRLQPRGRSHUPDQHQWHVWGRXW sul file. In questo caso, è necessaria una primitiva che riutilizzi un descrittore già attivo (predisposto in precedenza da fopen) per "agganciargli" un diverso file fisico. Questo servizio viene svolto in C dalla primitiva di libreria freopen. Il programma può quindi presentarsi come segue: #include <stdio.h> main(int argc, char **argv) int i, n, tab[]= 12, 54, -34, 65, 978, 19, 32 ; /* ridirige permanentemente stdout: il vecchio stdout è perso */ if (--argc>0) if(iuhrshqdujy>dujf@dvwgrxw==null) SHUURU("error redirecting stdout\n"); exit(1); /* tutte le prossime printf andranno sul file */ n=sizeof(tab)/sizeof(tab[0]); for(i=0;i<n;i++) printf("%d%c", tab[i], (i==n-1)? '\n' : '\t'); fclose(stdout); return 0; In effetti, la funzione freopen è simile alla fopen, da cui si distingue perché ULDSUH un file anziché aprirlo ex novo. Per questo, richiede come parametro il nome di un FILE* già esistente, che viene chiuso e riassociato al nuovo file. Da notare anche la funzione perror, che stampa un messaggio d'errore sullo standard error. Più esattamente: perror(msg) equivale a: fprintf(stderr, "%s: %s\n", msg, errmsg) dove errmsg è il messaggio d'errore standard, definito dall'implementazione, relativo all'errore attualmente indicato dalla variabile di sistema errno (error number). (6(5&,=,2Qƒ Scrivere una prima versione semplificata del preprocessore C che effettui l'intercettazione delle #define e costruisca la tabella delle sostituzioni. 30

25 In pratica, si richiede di analizzare il testo di un programma C, intercettando le righe che iniziano con #define, e catturando le due stringhe successive. Per semplicità, in questa versione non si considereranno le #define relative alle macro: quindi non saranno trattati testi da sostituire contenenti spazi o altri separatori, né le definizioni su più righe (che richiederebbero di gestire il carattere \ ). Sotto queste ipotesi, la soluzione può consistere nel leggere ULJDSHUULJD, tramite fgets, il testo del programma, analizzando poi ogni singola riga con sscanf al fine di intercettare le #define. In questo caso, la sscanf stessa può estrarre le due stringhe rappresentanti rispettivamente il testo da cercare e quello da sostituire, riempiendo una riga dell'apposita tabella. Al termine, la stampa della suddetta tabella (organizzata in forma di vettore di strutture) darà il quadro della situazione. Un esempio di programma che realizza tutto questo è riportato qui di seguito. #include <stdio.h> #include <string.h> #define MAXNAME_LEN 30 #define MAXVALUE_LEN 50 typedef struct char name[maxname_len]; char value[maxvalue_len]; tab_entry; #define MAXDEFS 100 #define MAXLINE_LEN 132 static tab_entry tab[maxdefs]; static int tabptr = 0; main(int argc, char **argv) FILE *source; char line[maxline_len]; void get_def(char *line); void print_tab(char *); if(argc<2) fprintf(stderr,"sintassi: DEFS <nomefile.c>\n"); exit(1); else if ((source=fopen(argv[1],"r"))==null) fprintf(stderr,"file sorgente non trovato\n"); exit(2); while (!feof(source)) fgets(line, MAXLINE_LEN, source); if (strncmp(line, "#define", 7)==0) get_def(line); fclose(source); print_tab(argv[1]); return 0; void get_def(char *line) 31

26 if (tabptr<maxdefs) sscanf(line, "#define %s%s", tab[tabptr].name, tab[tabptr].value); tabptr++; else fprintf(stderr, "\nmassimo numero di definizioni trattabili" " superato\n"); exit(3); void print_tab(char *file) int i; printf("\n\n File: %s", file); printf("\n " " "); printf("\n COSTANTE SIMBOLICA " " DEFINIZIONE "); printf("\n " " "); for(i=0; i<tabptr; i++) printf("\n *s *s ", MAXNAME_LEN-2, tab[i].name, MAXVALUE_LEN-10, tab[i].value ); printf("\n " " "); printf("\n\n"); return; Il vettore tab è un vettore (static, e perciò invisibile fuori da questo file) di strutture, ognuna fatta da due stringhe di caratteri denominate name e value. Esso è gestito dall'indice tabptr, pure static e inizializzato a zero. Come previsto in fase di specifica, il ciclo principale del programma legge una a una le righe del file d'ingresso, e verifica se iniziano con la sequenza "#define": se sì, chiama get_def per estrarre la relativa definizione. Alla fine di tutto, print_tab stampa (per ora sullo standard output) la tabella delle sostituzioni. Il cuore della funzione di analisi, get_def, è la funzione standard di libreria sscanf, che è del tutto identica a scanf, tranne per il fatto di prendere il proprio input non dallo standard input (o da un file come fscanf), ma dalla stringa passatale (tipicamente, come in questo caso, precedentemente letta con gets o funzioni similari). Questo approccio risulta utile ogni volta che è necessario sottoporre una linea a diverse analisi (anche consecutive), spesso senza sapere in partenza quale di queste analisi avrà esito positivo. La specifica di conversione %*s usata nella printf della funzione print_tab presenta il massimo grado di parametrizzazione del formato: significa che ODPSLH]]D GHO FDPSR disponibile per stampare s non è costante e nota a priori, ma viene fornita volta per volta nella lista degli argomenti. In questo caso, essa vale, rispettivamente per le due stringhe, MAXNAME_LEN-2 e MAXVALUE_LEN

27 La parola chiave typedef definisce un nuovo tipo, in questo caso chiamato tab_entry, sulla base del prototipo fornitole. Dal momento in cui essa compare, il nuovo tipo può essere usato in ogni luogo in cui possono essere usati i tipi predefiniti del linguaggio (come int, float, char...). La typedef differisce da una #define, in quanto la prima è una keyword del linguaggio C, mentre la seconda è soltanto una direttiva al preprocessore, i cui effetti sono certamente già completati SULPD che la compilazione vera e propria abbia inizio. Da notare che non è indifferente che il nome tab_entry sia posto prima o dopo le parentesi graffe che delimitano la struttura: nel primo caso infatti esso diviene il nome di un tipo, mentre nel secondo è soltanto un'abbreviazione per la successiva elencazione dei membri (tra parentesi graffe), ma non assurge al livello di tipo. Il riquadro sottostante illustra queste due possibilità. typedef struct char name[maxname_len]; char value[maxvalue_len]; tab_entry; struct tab_entry char name[maxname_len]; char value[maxvalue_len]; ; Pertanto, mentre nel primo caso tab_entry può essere usato come nome del tipo di una variabile in fase di dichiarazione/definizione della stessa, nel secondo il nome del tipo è struct tab_entry. (6(5&,=,2Qƒ Scrivere una programma che consenta di vedere la stessa area di memoria o come un numero intero o come sequenza di bytes, utilizzando il costrutto union. Si tratta semplicemente di definire una union avente come campi un int e un vettore di bytes (cioè di unsigned char) lungo quanto un int: la selezione dell'uno o dell'altro consentirà quindi in modo naturale di "commutare", per così dire, fra le due rappresentazioni. Un programma che fa ciò è presentato qui di seguito. 33

28 #include <stdio.h> typedef union int val; unsigned char rep[sizeof(int)]; integer; main(void) integer i; void print_internal_representation(integer); i.val=34; printf("\nvalore: %d\n", i.val); print_internal_representation(i); printf("\nindirizzo di i.val:\t%ld\n", &i.val); printf("\nindirizzo di i.rep:\t%ld\n", &i.rep); return 0; void print_internal_representation(integer i) int k; printf("\nrappresentazione interna:\n\n"); for(k=0; k<sizeof(integer); k++) printf("%4d%c", i.rep[k], (k==sizeof(integer)-1? \n : \t ) ); Sebbene printf e print_internal_representation stampino i valori di due variabili all'apparenza diverse, ciò che viene visualizzato da quest'ultima dipende dall'assegnamento precedente a i.val. In effetti, una union non è altro che una collezione di variabili aventi tutte il medesimo indirizzo di partenza, come, peraltro, si può facilmente verificare. (6(5&,=,2Qƒ Scrivere un programma che allochi dinamicamente spazio per al più 10 stringhe di al più 80 caratteri. Finora, tutte le variabili viste erano o variabili globali (definite e allocate staticamente) o variabili locali a una funzione (definite staticamente e allocate/deallocate automaticamente a run-time). Tuttavia, le variabili definite staticamente hanno dei limiti, primo fra tutti quello di dover conoscere a priori la loro dimensione massima, che le rende inadatte a certi campi di applicazione. Per ovviare a questo problema, il C mette a disposizione due primitive, malloc e free, per allocare e deallocare esplicitamente delle nuove variabili a tempo d'esecuzione. Più precisamente, la primitiva malloc chiede al sistema un'area ampia Qbytes, e ne restituisce l'indirizzo sotto forma di void* (ossia di puntatore generico), mentre la primitiva free libera l'area associata a quell'indirizzo (non è necessario specificare nuovamente la dimensione in quanto provvede il sistema a tenere traccia di queste informazioni). 34

29 Per risolvere il problema dato occorre dunque predisporre un ciclo che legga dieci stringhe da tastiera, ognuna lunga al più 80 caratteri, e le ricopi ognuna in una nuova area di memoria, chiesta da malloc al sistema volta per volta sulla base della OXQJKH]]D HIIHWWLYD della stringa letta. I puntatori a tali aree di memoria potranno essere memorizzati in un apposito YHWWRUHGLSXQWDWRUL. Un possibile programma che realizzi tutto questo è il seguente: #include <stdio.h> #include <stdlib.h> /* contiene prototipi di malloc e free */ #include <string.h> main(void) char line[81]; char *s[10]; int i; for(i=0; i<10; i++) gets(line); s[i]= (char *) malloc(strlen(line)+1); strcpy(s[i], line); for(i=0; i<10; i++) printf("\n%s", s[i]); free(s[i]); return 0; E' importante notare che malloc, quando riserva memoria, non ha alcuna informazione sul tipo di uso che ne verrà fatto; per questo, essa si limita a restituire OLQGLUL]]R di quell'area, sotto forma di void* che, com'è noto, proprio per questo non è dereferenziabile. Per utilizzare praticamente l'area riservata è quindi LQGLVSHQVDELOH usare il cast (char*)per convertire esplicitamente il puro indirizzo in un puntatore del tipo corrispondente all'uso che si fa di tale area di memoria. Dal punto di vista pratico, il programma è completato da un secondo ciclo che rilegge e visualizza le stringhe precedentemente immesse, deallocando quindi l'area di memoria corrispondente tramite free. L'area liberata potrà essere nuovamente utilizzata in successive richieste a malloc, nei tempi e modi stabiliti della politica di gestione della memoria adottata dal compilatore. Per finire, osserviamo che, se tutto si limitasse a questo, al di là della maggior generalità ottenuta nel trattamento stringhe (e in generale di vettori) non si vedrebbe la reale utilità delle primitive di allocazione dinamica, in quanto, pur potendo allocare nuove variabili dinamicamente, e quindi in modo svincolato dalle definizioni statiche, di fatto bisognerebbe però avere definito staticamente il puntatore da utilizzare con la malloc, il che riporterebbe il problema al punto di partenza. 35

FONDAMENTI DI INFORMATICA II

FONDAMENTI DI INFORMATICA II FONDAMENTI DI INFORMATICA II ESERCITAZIONE n 1: Linguaggio C 1. Macro ESERCIZIO n 1 Definire una macro ODD(i) che restituisca TRUE o FALSE a seconda che il valore intero passato sia rispettivamente dispari

Dettagli

FONDAMENTI DI INFORMATICA II

FONDAMENTI DI INFORMATICA II FONDAMENTI DI INFORMATICA II ESERCITAZIONE n 2: Linguaggio C 1. Filtri su file + Stringhe e Vettori (parte 2) ESERCIZIO n 13 Realizzare un programma che ricopi il file dato come primo argomento nel file

Dettagli

Gestione dei file in C

Gestione dei file in C Gestione dei file in C Fondamenti di Informatica Che cos è un file e a cosa serve? Memoria di massa vs memoria centrale q La memoria di massa (disco fisso) è un dispositivo di memorizzazione generalmente

Dettagli

Esercitazioni di Fondamenti di Informatica - Lez. 9 4/12/2018

Esercitazioni di Fondamenti di Informatica - Lez. 9 4/12/2018 Esercitazioni di Fondamenti di Informatica - Lez. 9 /1/018 Esercizi sulla gestione dei file in C Il codice degli esercizi e contenuto nella cartella parte1 1. Creare una funzione C che legga tutto cio

Dettagli

Gestione dei file in C

Gestione dei file in C Gestione dei file in C Fondamenti di Informatica Che cos è un file e a cosa serve? Memoria di massa vs memoria centrale q La memoria di massa (disco fisso) è un dispositivo di memorizzazione generalmente

Dettagli

Dati due punti sul piano calcolare la loro distanza

Dati due punti sul piano calcolare la loro distanza Introduzione al C Primo esempio in C Dati due punti sul piano calcolare la loro distanza Soluzione: la distanza fra due punti si calcola secondo il teorema di Pitagora, con la formula: y Distanza = (lato12

Dettagli

Laboratorio di Programmazione

Laboratorio di Programmazione Laboratorio di Programmazione (Laurea triennale in matematica) Lezione 26 Sommario: Gestione dei file in C. File ad accesso sequenziale: apertura, chiusura, lettura e scrittura. File binari. Gestione files

Dettagli

File binari e file di testo

File binari e file di testo I file File binari e file di testo distinzione tra file binari file di testo si possono usare funzioni diverse per la gestione di tipi di file diversi Programmazione Gestione dei file 2 File binari e file

Dettagli

Il linguaggio C. Breve panoramica su stdio.h

Il linguaggio C. Breve panoramica su stdio.h Il linguaggio C Breve panoramica su stdio.h 1 Input/Output: stdio.h Contiene definizioni di costanti legate all I/O es. EOF (end of file) #define EOF (-1) valore restituito alla fine di un file Contiene

Dettagli

I File. Consente una memorizzazione persistente dei dati, non limitata dalle dimensioni della memoria centarle.

I File. Consente una memorizzazione persistente dei dati, non limitata dalle dimensioni della memoria centarle. I File Il file e l unita logica di memorizzazione dei dati su memoria di massa. Consente una memorizzazione persistente dei dati, non limitata dalle dimensioni della memoria centarle. Generalmente un file

Dettagli

Streams e disk files

Streams e disk files Streams e disk files Streams Un canale è una sequenza di byte di dati Sorgente o destinazione di dati che possono essere associati ad un disco o ad altre periferiche Due tipi di stream: Testo: sequenza

Dettagli

Informatica per Statistica Riassunto della lezioni del 14/11/2012 e 16/11/2012

Informatica per Statistica Riassunto della lezioni del 14/11/2012 e 16/11/2012 Informatica per Statistica Riassunto della lezioni del 14/11/2012 e 16/11/2012 Igor Melatti Cenni sui puntatori in C Per ogni variabile normale dichiarata, il compilatore riserva una precisa quantità di

Dettagli

Le strutture. Una struttura C è una collezione di variabili di uno o più tipi, raggruppate sotto un nome comune.

Le strutture. Una struttura C è una collezione di variabili di uno o più tipi, raggruppate sotto un nome comune. Le strutture Una struttura C è una collezione di variabili di uno o più tipi, raggruppate sotto un nome comune. Dichiarazione di una struttura: struct point { int x; int y; }; La dichiarazione di una struttura

Dettagli

Input/output da file I/O ANSI e I/O UNIX FLUSSI E FILE FLUSSI FLUSSI di TESTO FLUSSI BINARI FILE

Input/output da file I/O ANSI e I/O UNIX FLUSSI E FILE FLUSSI FLUSSI di TESTO FLUSSI BINARI FILE Input/output da file Il linguaggio C non contiene istruzioni di I/O, in quanto tali operazioni vengono eseguite tramite funzioni di libreria standard. Questo approccio rende estremamente flessibile e potente

Dettagli

La gestione dell'errore

La gestione dell'errore La gestione dell'errore Esiste una variabile globale intera, definita nell'header errno.h che viene settata nel caso in cui una chiamata di sistema non possa eseguire correttamente il suo compito. Tale

Dettagli

Introduzione al C. Stream e disk file

Introduzione al C. Stream e disk file Introduzione al C Stream e disk file Stream Un canale è una sequenza di byte di dati Sorgente o destinazione di dati che possono essere associati ad un disco o ad altre periferiche Due tipi di stream:

Dettagli

file fisico file logico

file fisico file logico I files Un file è una struttura di dati residente su una memoria di massa (file fisico) Un programma vede un file come una sequenza di bytes (file logico) Generalizzando, un file può comprendere qualsiasi

Dettagli

Gestione dei File. Credits Prof. Campi

Gestione dei File. Credits Prof. Campi Gestione dei File Credits Prof. Campi 1 Perché i file? Sono strutture dati persistenti Sono solitamente memorizzati sui dischi Si usano dall'interno dei programmi Realizzano la persistenza dei dati cioè

Dettagli

Caratteri e stringhe

Caratteri e stringhe Caratteri e stringhe Caratteri Dato che un computer può memorizzare esclusivamente sequenze di bit, per memorizzare un carattere (e quindi testi) è necessario stabilire una convenzione che associa a un

Dettagli

I files in C. A cura del prof. Ghezzi

I files in C. A cura del prof. Ghezzi I files in C A cura del prof. Ghezzi 1 Perchè i files? Realizzano la persistenza dei dati dati che sopravvivono all esecuzione del programma Sono delle strutture di dati sequenziali 2 Files e S.O. I files

Dettagli

puntatori Lab. Calc. AA 2006/07 1

puntatori Lab. Calc. AA 2006/07 1 puntatori Lab. Calc. AA 2006/07 1 Attributi di un oggetto nome o identificatore; tipo; valore (o valori); indirizzo; Lab. Calc. AA 2006/07 2 Indirizzo Consideriamo la dichiarazione con inizializzazione:

Dettagli

La copia di un file. contare di quanti caratteri sia composto il file (e quindi determinare la dimensione del file di origine)

La copia di un file. contare di quanti caratteri sia composto il file (e quindi determinare la dimensione del file di origine) La copia di un file Nell'esercizio di oggi cerchiamo di implementare un programma che permetta di effettuare la copia di un file di testo. L'operazione di copia, oltre a permettere di creare un nuovo file

Dettagli

Caratteri e stringhe

Caratteri e stringhe Caratteri e stringhe Caratteri Dato che un computer può memorizzare esclusivamente sequenze di bit, per memorizzare un carattere (e quindi testi) è necessario stabilire una convenzione che associa a un

Dettagli

Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018

Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018 Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018 Esercizi sull allocazione dinamica della memoria in C Il codice di questi esercizi é contenuto nella cartella parte1 1. Implementare una lista

Dettagli

Esercizio 2 (punti 7) Dato il seguente programma C: #include <stdio.h> int swap(int * nome, int length);

Esercizio 2 (punti 7) Dato il seguente programma C: #include <stdio.h> int swap(int * nome, int length); Fondamenti di Informatica L-A (A.A. 004/005) - Ingegneria Informatica Prof.ssa Mello & Prof. Bellavista I Prova Intermedia del 11/11/004 - durata h - COMPITO B Esercizio 1 (punti 1) Una associazione di

Dettagli

Gestione dei file. Linguaggio ANSI C Input/Output - 13

Gestione dei file. Linguaggio ANSI C Input/Output - 13 Gestione dei file Necessità di persistenza dei file archiviare i file su memoria di massa. Un file è una astrazione fornita dal sistema operativo, il cui scopo è consentire la memorizzazione di informazioni

Dettagli

Sistemi Operativi (M. Cesati)

Sistemi Operativi (M. Cesati) Sistemi Operativi (M. Cesati) Compito scritto del 19 febbraio 2013 Nome: Matricola: Corso di laurea: Cognome: Crediti da conseguire: 5 6 9 Scrivere i dati richiesti in stampatello. Al termine consegnare

Dettagli

Esercizio 1 (15 punti)

Esercizio 1 (15 punti) Esercizio 1 (15 punti) Corsi di laurea in Ingegnera Elettronica e Ingegneria Gestionale Fondamenti di Programmazione / Fondamenti di Informatica I Prova scritta del 16 giugno 2017 Il noto gioco MasterMind

Dettagli

Input / Output. Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard

Input / Output. Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard Stdio.h Input / Output Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard Sia i file che i dispositivi (tastiera, schermo...) sono visti come

Dettagli

Gestione dei files. Prof. Francesco Accarino IIS Altiero Spinelli Sesto San Giovanni Via Leopardi 132

Gestione dei files. Prof. Francesco Accarino IIS Altiero Spinelli Sesto San Giovanni Via Leopardi 132 Gestione dei files Prof. Francesco Accarino IIS Altiero Spinelli Sesto San Giovanni Via Leopardi 132 Struttura del disco fisso Un disco fisso è composto da una serie di piatti sovrapposti Ogni piatto è

Dettagli

Lezione 8: Stringhe ed array multidimensionali

Lezione 8: Stringhe ed array multidimensionali Lezione 8: Stringhe ed array multidimensionali Vittorio Scarano Corso di Laurea in Informatica Università degli Studi di Salerno Struttura della lezione AVVISO: la lezione di laboratorio di 28/5 non si

Dettagli

Input/Output. Lettura e scrittura Caratteri e Stringhe: Terminale e file. Input/output. caratteri stringhe formattato ascii binari

Input/Output. Lettura e scrittura Caratteri e Stringhe: Terminale e file. Input/output. caratteri stringhe formattato ascii binari Input/Output Lettura e scrittura Caratteri e Stringhe: Terminale e file Input/output console file caratteri stringhe formattato ascii binari Linguaggio ANSI C Input/Output - 1 La libreria standard del

Dettagli

FUNZIONI. attribuire un nome ad un insieme di istruzioni parametrizzare l esecuzione del codice

FUNZIONI. attribuire un nome ad un insieme di istruzioni parametrizzare l esecuzione del codice Funzioni FUNZIONI Spesso può essere utile avere la possibilità di costruire nuove istruzioni che risolvono parti specifiche di un problema Una funzione permette di attribuire un nome ad un insieme di istruzioni

Dettagli

Esercitazione 11. Liste semplici

Esercitazione 11. Liste semplici Esercitazione 11 Liste semplici Liste semplici (o lineari) Una lista semplice (o lineare) è una successione di elementi omogenei che occupano in memoria una posizione qualsiasi. Ciascun elemento contiene

Dettagli

Ingresso ed Uscita in C. Informatica 1 / 15

Ingresso ed Uscita in C. Informatica 1 / 15 Ingresso ed Uscita in C Informatica 1 / 15 Input e Output in C Linguaggio C: progettato per essere semplice e con poche istruzioni Non esistono istruzioni di ingresso / uscita (I/O)!!! Ingresso ed uscita

Dettagli

Lezione 11: Liste a Puntatori e Input/Output

Lezione 11: Liste a Puntatori e Input/Output Lezione 11: Liste a Puntatori e Input/Output Vittorio Scarano Laboratorio di Informatica I Corso di Laurea in Informatica Università degli Studi di Salerno Struttura della lezione Richiamo: strutture (struct)

Dettagli

Primo programma in C

Primo programma in C Primo programma in C Struttura minima di un file C Applicazioni C in modo console Struttura del programma Commenti Direttive #include Definizione di variabili Corpo del main 2 Struttura minima di un file

Dettagli

Le strutture. Una struttura C è una collezione di variabili di uno o più tipi, raggruppate sotto un nome comune.

Le strutture. Una struttura C è una collezione di variabili di uno o più tipi, raggruppate sotto un nome comune. Le strutture Una struttura C è una collezione di variabili di uno o più tipi, raggruppate sotto un nome comune. Dichiarazione di una struttura: struct point { int x; int y; }; La dichiarazione di una struttura

Dettagli

Un file è un astrazione di memorizzazione di dimensione potenzialmente illimitata (ma non infinita), ad accesso sequenziale.

Un file è un astrazione di memorizzazione di dimensione potenzialmente illimitata (ma non infinita), ad accesso sequenziale. IL CONCETTO DI FILE Un file è una astrazione fornita dal sistema operativo, il cui scopo è consentire la memorizzazione di informazioni su memoria di massa. Concettualmente, un file è una sequenza di registrazioni

Dettagli

Elementi lessicali. Lezione 4. La parole chiave. Elementi lessicali. Elementi lessicali e espressioni logiche. Linguaggi di Programmazione I

Elementi lessicali. Lezione 4. La parole chiave. Elementi lessicali. Elementi lessicali e espressioni logiche. Linguaggi di Programmazione I Lezione 4 Elementi lessicali e espressioni logiche Matricole 2-3 Elementi lessicali il linguaggio C ha un suo vocabolario di base i cui elementi sono detti token esistono 6 tipi di token: parole chiave

Dettagli

I/O Avanzato in C: scheda riassuntiva

I/O Avanzato in C: scheda riassuntiva Funzione sscanf r = sscanf(str, "formato", &variabili ) ; str Stringa da cui acquisire i dati "formato" Sequenza dei campi da leggere ("%d", "%s",... ) &variabili Variabili nelle quali depositare i valori

Dettagli

Indice. La gestione dei file in C e gli stream. Apertura e chiusura di un file. Operazioni sui file. Accesso sequenziale e non sequenziale

Indice. La gestione dei file in C e gli stream. Apertura e chiusura di un file. Operazioni sui file. Accesso sequenziale e non sequenziale File in C Indice La gestione dei file in C e gli stream Apertura e chiusura di un file Operazioni sui file Accesso sequenziale e non sequenziale Gestione dei file in C In C all interno della standard library

Dettagli

GESTIONE DEI FILE IN C. Docente: Giorgio Giacinto AA 2008/2009

GESTIONE DEI FILE IN C. Docente: Giorgio Giacinto AA 2008/2009 Università degli Studi di Cagliari Corso di Laurea Specialistica in Ingegneria per l Ambiente ed il Territorio Corso di Laurea Specialistica in Ingegneria Civile - Strutture FONDAMENTI DI INFORMATICA 2

Dettagli

Input / Output. Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard

Input / Output. Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard Stdio.h Input / Output Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard Sia i file che i dispositivi (tastiera, schermo...) sono visti come

Dettagli

Il linguaggio C. Puntatori e dintorni

Il linguaggio C. Puntatori e dintorni Il linguaggio C Puntatori e dintorni 1 Puntatori : idea di base In C è possibile conoscere e denotare l indirizzo della cella di memoria in cui è memorizzata una variabile (il puntatore) es : int a = 50;

Dettagli

L'Allocazione Dinamica della Memoria nel linguaggio C

L'Allocazione Dinamica della Memoria nel linguaggio C L'Allocazione Dinamica della Memoria nel linguaggio C Prof. Rio Chierego riochierego@libero.it http://www.riochierego.it/informatica.htm Sommario Questo documento tratta l'allocazione dinamica della memoria

Dettagli

Introduzione alla programmazione in linguaggio C

Introduzione alla programmazione in linguaggio C Introduzione alla programmazione in linguaggio C Il primo programma in C commento Header della libreria Funzione principale Ogni istruzione in C va terminata con un ; Corso di Informatica AA. 2007-2008

Dettagli

Una stringa di caratteri in C è un array di caratteri terminato dal carattere '\0' a p e \0

Una stringa di caratteri in C è un array di caratteri terminato dal carattere '\0' a p e \0 STRINGHE: ARRAY DI CARATTERI Una stringa di caratteri in C è un array di caratteri terminato dal carattere '\0' s a p e \0 0 1 2 3 Un vettore di N caratteri può dunque ospitare stringhe lunghe al più N-1

Dettagli

Corso di Informatica A.A

Corso di Informatica A.A Corso di Informatica A.A. 2009-2010 Lezione 13 Corso di Informatica 2009-2010 Lezione 13 1 Input/output da file Stream Si definisce stream una sequenza di bytes di dati che può essere in ingresso (input

Dettagli

Allocazione dinamica memoria

Allocazione dinamica memoria Allocazione dinamica memoria Marco Casazza 11/12/2017 1 Esercizio 1 1 /* 2 Creare una lista bidirezionale di interi, ovvero una lista 3 che permette lo scorrimento in entrambe le direzioni ( dal primo

Dettagli

Gestione dei File C. Generalità

Gestione dei File C. Generalità Gestione dei File C 1 Generalità Il linguaggio C non contiene alcuna istruzione di Input/Output. Tali operazioni vengono svolte mediante chiamate a funzioni definite nella libreria standard contenute nel

Dettagli

Compendio sottoinsieme del C++ a comune col C. (Libreria standard, Input/Output, Costanti, Dichiarazioni e typedef, Memoria Dinamica)

Compendio sottoinsieme del C++ a comune col C. (Libreria standard, Input/Output, Costanti, Dichiarazioni e typedef, Memoria Dinamica) Compendio sottoinsieme del C++ a comune col C (Libreria standard, Input/Output, Costanti, Dichiarazioni e typedef, Memoria Dinamica) Librerie 1/2 Il solo insieme di istruzioni di un linguaggio di programmazione

Dettagli

Allocazione dinamica della memoria

Allocazione dinamica della memoria Allocazione dinamica della memoria Allocazione statica: limiti Per quanto sappiamo finora, in C le variabili sono sempre dichiarate staticamente la loro esistenza deve essere prevista e dichiarata a priori

Dettagli

Gestione dei file. Stefano Ferrari. Università degli Studi di Milano Programmazione. anno accademico

Gestione dei file. Stefano Ferrari. Università degli Studi di Milano Programmazione. anno accademico Gestione dei file Stefano Ferrari Università degli Studi di Milano stefano.ferrari@unimi.it Programmazione anno accademico 2016 2017 Gli stream Si dice stream qualsiasi sorgente di dati in ingresso e qualsiasi

Dettagli

Funzioni e Ricorsione

Funzioni e Ricorsione Funzioni e Ricorsione La ricorsione consiste nella possibilità di definire una funzione in termini di se stessa Nel codice di una funzione ricorsiva compare una (o più di una) chiamata alla funzione stessa

Dettagli

Struttura dei programmi e Variabili

Struttura dei programmi e Variabili Linguaggio C Struttura dei programmi e Variabili 1 La struttura generale dei programmi! Struttura del programma: Direttive Funzioni Dichiarazioni Istruzioni Di assegnamento direttive Di ingresso e uscita

Dettagli

Allocazione dinamica della memoria

Allocazione dinamica della memoria Allocazione dinamica della memoria Violetta Lonati Università degli studi di Milano Dipartimento di Informatica Laboratorio di algoritmi e strutture dati Corso di laurea in Informatica Violetta Lonati

Dettagli

Fondamenti di Informatica T. Linguaggio C: i puntatori

Fondamenti di Informatica T. Linguaggio C: i puntatori Linguaggio C: i puntatori Il puntatore E` un tipo di dato, che consente di rappresentare gli indirizzi delle variabili allocate in memoria. Dominio: Il dominio di una variabile di tipo puntatore è un insieme

Dettagli

I file possono essere manipolati (aperti, letti, scritti ) all interno di programmi C. dischi nastri cd

I file possono essere manipolati (aperti, letti, scritti ) all interno di programmi C. dischi nastri cd Per poter mantenere disponibili i dati tra le diverse esecuzioni di un programma (persistenza dei dati) è necessario poterli archiviare su memoria di massa. dischi nastri cd GESTIONE DEI FILE I file possono

Dettagli

Corso di Fondamenti di Informatica Ingegneria delle Comunicazioni BCOR Ingegneria Elettronica BELR Introduzione al C Unità 9 File

Corso di Fondamenti di Informatica Ingegneria delle Comunicazioni BCOR Ingegneria Elettronica BELR Introduzione al C Unità 9 File Corso di Fondamenti di Informatica Ingegneria delle Comunicazioni BCOR Ingegneria Elettronica BELR Introduzione al C Unità 9 File D. Bloisi, A. Pennisi, S. Peluso, S. Salza, C. Ciccotelli Sommario Input/Output

Dettagli

#include <stdio.h> /* l esecuzione comincia dalla funzione main */ int main()

#include <stdio.h> /* l esecuzione comincia dalla funzione main */ int main() Primi passi Il mio primo programma #include /* l esecuzione comincia dalla funzione main */ int main() { printf( Hello World!\n" ); return 0; /* il programma termina con successo */ } /* fine

Dettagli

Files in C endofile

Files in C endofile Files in C Il C vede i file semplicemente come un flusso (stream) sequenziale di bytes terminati da un marcatore speciale che determina la fine del file (end-of-file). 0 1 2 3 4... endofile A differenza

Dettagli

Dichiarazioni e tipi predefiniti nel linguaggio C

Dichiarazioni e tipi predefiniti nel linguaggio C Politecnico di Milano Dichiarazioni e tipi predefiniti nel linguaggio C Variabili, costanti, tipi semplici, conversioni di tipo. Premessa Programmi provati sul compilatore Borland C++ 1.0 Altri compilatori:

Dettagli

Esercizi. I File ed il C

Esercizi. I File ed il C Politecnico di Milano Esercizi File di testo,, file binari I File ed il C Insieme omogeneo di dati, memorizzato su disco e caratterizzato da un nome La lunghezza di un file non è fissata a priori Astrazione

Dettagli

Laboratorio di Informatica L-A 1

Laboratorio di Informatica L-A 1 Funzioni e Ricorsione La ricorsione consiste nella possibilità di definire una funzione in termini di se stessa È basata sul principio di induzione matematica: se una proprietà P vale per n=n 0 e si può

Dettagli

Linguaggio C: i file

Linguaggio C: i file Dipartimento di Elettronica ed Informazione Politecnico di Milano Informatica A - GES Prof. Plebani A.A. 2006/2007 Linguaggio C: i file La presente dispensa e da utilizzarsi ai soli fini didattici previa

Dettagli

Linguaggio C. Vettori, Puntatori e Funzioni Stringhe. Università degli Studi di Brescia. Prof. Massimiliano Giacomin

Linguaggio C. Vettori, Puntatori e Funzioni Stringhe. Università degli Studi di Brescia. Prof. Massimiliano Giacomin Linguaggio C Vettori, Puntatori e Funzioni Stringhe Università degli Studi di Brescia Prof. Massimiliano Giacomin SCHEMA DELLA LEZIONE RELAZIONE TRA VETTORI E PUNTATORI (e le stringhe letterali come caso

Dettagli

#include <stdio.h> main() { - 1 -

#include <stdio.h> main() { - 1 - Un primo esempio di programma Ogni programma C deve contenere una funzione speciale chiamata main che indica il punto in cui inizia l esecuzione del programma. La funzione main è unica all interno di ogni

Dettagli

C: panoramica. Violetta Lonati

C: panoramica. Violetta Lonati C: panoramica Violetta Lonati Università degli studi di Milano Dipartimento di Scienze dell Informazione Laboratorio di algoritmi e strutture dati Corso di laurea in Informatica AA 2009/2010 Violetta Lonati

Dettagli

Lezione 19 e Allocazione dinamica della memoria - Direttive al preprocessore - Libreria standard - Gestione delle stringhe

Lezione 19 e Allocazione dinamica della memoria - Direttive al preprocessore - Libreria standard - Gestione delle stringhe Lezione 19 e 20 - Allocazione dinamica della memoria - Direttive al preprocessore - Libreria standard - Gestione delle stringhe Valentina Ciriani (2005-2008) Laboratorio di programmazione Valentina Ciriani

Dettagli

Lab 12 Allocazione dinamica della memoria

Lab 12 Allocazione dinamica della memoria Fondamenti di Informatica e Laboratorio T-AB Ingegneria Elettronica e Telecomunicazioni Lab 12 Allocazione dinamica della memoria Lab17 1 Esercizio 0 Un file di testo di nome bambini.txt, contiene informazioni

Dettagli

Sistemi Operativi (M. Cesati)

Sistemi Operativi (M. Cesati) Sistemi Operativi (M. Cesati) Compito scritto del 17 febbraio 2014 Nome: Matricola: Corso di laurea: Cognome: Crediti da conseguire: 5 6 9 Scrivere i dati richiesti in stampatello. Al termine consegnare

Dettagli

giapresente( ) leggi( ) char * strstr(char * cs, char * ct) NULL

giapresente( ) leggi( ) char * strstr(char * cs, char * ct) NULL Materiale di ausilio utilizzabile durante l appello: tutto il materiale è a disposizione, inclusi libri, lucidi, appunti, esercizi svolti e siti Web ad accesso consentito in Lab06. L utilizzo di meorie

Dettagli

Input / Output. Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard

Input / Output. Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard Stdio.h Input / Output Come già detto, input e output sono realizzati in C da funzioni di stdio.h all'interno della libreria standard Sia i file che i dispositivi (tastiera, schermo...) sono visti come

Dettagli

I/O da tastiera + Alessandra Giordani Lunedì 2 maggio

I/O da tastiera + Alessandra Giordani Lunedì 2 maggio I/O da tastiera + costrutti while e if Alessandra Giordani agiordani@disi.unitn.it Lunedì 2 maggio 2011 http://disi.unitn.it/~agiordani/ Ripasso funzione printf() Usata per stampare il contenuto di una

Dettagli

Esercitazione di Reti degli elaboratori

Esercitazione di Reti degli elaboratori Esercitazione di Prof.ssa Chiara Petrioli Christian Cardia, Gabriele Saturni Cosa vedremo in questa lezione? Gli Array Gli array multidimensionali Le stringhe I puntatori Esercizi Pagina 1 Gli Array Definizione

Dettagli

Tipi di dati strutturati e Linguaggio C. Record o strutture Il costruttore struct in C

Tipi di dati strutturati e Linguaggio C. Record o strutture Il costruttore struct in C Tipi di dati strutturati e Linguaggio C Record o strutture Il costruttore struct in C Dati strutturati Record Un record o struttura è una struttura dati ottenuta aggregando elementi di tipo diverso che

Dettagli

Informatica per Statistica Riassunto della lezione del 21/10/2011

Informatica per Statistica Riassunto della lezione del 21/10/2011 Informatica per Statistica Riassunto della lezione del 1/10/011 Igor Melatti Costrutti del linguaggio C: ripasso Si consideri il programma C alla Figura 1 ci sono due dichiarazioni di funzioni, insertion

Dettagli

Stringhe e allocazione dinamica della memoria

Stringhe e allocazione dinamica della memoria Stringhe e allocazione dinamica della memoria Esercizio Scrivere un programma strings.c che legge da standard input una sequenza di parole separate da uno o più spazi, e stampa le parole lette, una per

Dettagli

Introduzione al C. Lezione 4 Allocazione dinamica della memoria. Rossano Venturini. Pagina web del corso

Introduzione al C. Lezione 4 Allocazione dinamica della memoria. Rossano Venturini. Pagina web del corso Introduzione al C Lezione 4 Allocazione dinamica della memoria Rossano Venturini rossano@di.unipi.it Pagina web del corso http://didawiki.cli.di.unipi.it/doku.php/informatica/all-b/start Lezioni di ripasso

Dettagli

Università degli Studi di Milano

Università degli Studi di Milano Università degli Studi di Milano Corso di Laurea in Sicurezza dei Sistemi e delle Reti Informatiche Lezione 3 Input/Output elementare. Operatori, espressioni e istruzioni FABIO SCOTTI Laboratorio di programmazione

Dettagli

Passaggio dei parametri

Passaggio dei parametri Passaggio dei parametri Per valore Il valore viene copiato dall environment esterno all environment della funzione o procedura Cambiamenti dei parametri così passati non si riflettono sull environment

Dettagli

Ingresso ed Uscita in C. Luca Abeni

Ingresso ed Uscita in C. Luca Abeni Ingresso ed Uscita in C Luca Abeni Input e Output in C Linguaggio C: progettato per essere semplice e con poche istruzioni Non esistono neanche istruzioni di ingresso / uscita (I/O)!!! Ingresso ed uscita

Dettagli

Ricorsione. La ricorsione consiste nella possibilità di definire una funzione in termini di se stessa

Ricorsione. La ricorsione consiste nella possibilità di definire una funzione in termini di se stessa Funzioni e Ricorsione La ricorsione consiste nella possibilità di definire una funzione in termini di se stessa È basata sul principio di induzione matematica: se una proprietà P vale per n=n 0 e si può

Dettagli

Sono file di caratteri, organizzati in linee. Ogni linea è terminata da un marcatore di fine linea (newline, carattere \n ).

Sono file di caratteri, organizzati in linee. Ogni linea è terminata da un marcatore di fine linea (newline, carattere \n ). I file Il file è l unità logica di memorizzazione dei dati su memoria di massa. Consente una memorizzazione persistente dei dati, non limitata dalle dimensioni della memoria centrale. Generalmente un file

Dettagli

Introduzione al linguaggio C Primi programmi

Introduzione al linguaggio C Primi programmi Introduzione al linguaggio C Primi programmi Violetta Lonati Università degli studi di Milano Dipartimento di Scienze dell Informazione Laboratorio di algoritmi e strutture dati Corso di laurea in Informatica

Dettagli

Input/Output su disco

Input/Output su disco Input/Output su disco In C, la gestione dei dispositivi di lettura (tastiera, file su disco,...) e scrittura (monitor, file su disco, stampante,...) viene effettuata mediante canali di comunicazione. Tali

Dettagli

GESTIONE DEI FILE. File come tipo di dati

GESTIONE DEI FILE. File come tipo di dati GESTIONE DEI FILE File come tipo di dati Nel linguaggio C, i file vengono trattati come un tipo di dati derivato, cioè ottenuto dai tipi elementari esistenti. In pratica, quando si apre e si gestisce un

Dettagli

Costanti e Variabili

Costanti e Variabili Parte 3 Costanti e Variabili Identificatori Un identificatore è un nome che viene associato a diverse entità (costanti, tipi, variabili, funzioni, ecc.) e serve ad identificare la particolare entità Gli

Dettagli

INPUT E OUTPUT DI VALORI NUMERICI

INPUT E OUTPUT DI VALORI NUMERICI INPUT E OUTPUT DI VALORI NUMERICI FUNZIONI DI I/O PER NUMERI Per utilizzare le funzioni di Input/Output bisogna includere il file di intestazione (header file) denominato contiene la

Dettagli

Linguaggio C. Generalità sulle Funzioni. Variabili locali e globali. Passaggio di parametri per valore.

Linguaggio C. Generalità sulle Funzioni. Variabili locali e globali. Passaggio di parametri per valore. Linguaggio C Generalità sulle Funzioni. Variabili locali e globali. Passaggio di parametri per valore. 1 Funzioni Generalizzazione del concetto di funzione algebrica: legge che associa a valori delle variabili

Dettagli

Lo scopo. Il primo esperimento. Soluzione informale. Le variabili

Lo scopo. Il primo esperimento. Soluzione informale. Le variabili Lo scopo 2 Il primo esperimento Si vuole scrivere un programma in linguaggio C che chieda all utente di introdurre da tastiera due numeri interi e visualizzi il valore della loro somma sul video Ver. 2.4

Dettagli