vuoi
o PayPal
tutte le volte che vuoi
LETTURA DA FILE
La funzione fread legge dati dal file fp e li trasferisce nel vettore buf.
n = fread(buf, dimensione, elementi, fp);
● buf: è il vettore dove devono essere trasferite le informazioni lette dal file
● dimensione: rappresenta la dimensione in byte di un elemento del vettore (sizeof(tipo))
● elementi: indica il numero di elementi del vettore
● fp: FILE POINTER
Il valore di ritorno della fread (n) indica il numero di elementi letti dal file;
● Se n<0: che è stato commesso qualche errore, per esempio il file non è stato aperto.
● Per file grandi (quasi tutti): fread deve essere ripetuta più di una volta, leggendo a ogni
chiamata soltanto una limitata porzione del file, fino a quando n==elementi. (es con un while)
Altri esempi:
fscanf (fp,”%tipo”,variabili) scrive formattato sul file.
fgets (stringa,fp) scrive formattato.
fgetc(car,fp) scrive formattato
POSIZIONE DEL PUNTATORE
In C è possibile anche gestire la posizione del puntatore (FILE POINTER) attraverso 2
funzioni:
● fseek:
consente di muovere il puntatore di lettura/ scrittura in una qualunque posizione.
err = fseek(fp, n, mode);
● fp: FILE POINTER;
● n: indica di quanti byte il file pointer deve essere spostato;(con n negativo il file
pointer deve essere spostato indietro invece che in avanti)
● mode: indica a partire da quale posizione muovere il file pointer:
mode == 0 significa che lo spostamento è a partire dall’inizio,
mode== 1 è dalla posizione corrente
mode== 2 è a partire dalla fine del file
● err: Il valore di ritorno della funzione fseek è negativo se si è verificato
un errore, maggiore o uguale a 0 in caso contrario.
● ftell:
consente di conoscere attualmente la posizione del FILE POINTER
n=ftell(fp);
● ftell restituisce il valore della posizione del FP nel file.
Se il valore restituito è negativo c’è stato un errore, per esempio, se il file non è mai
stato aperto.
in questo esempio, fgetc(file) prende i caratteri dal file aperto, li salva in ‘ch’, e volta
che il ciclo va avanti la posizione ‘pos’ viene stampata grazie a ftell.
14 bit operators
Il C fornisce un ricco insieme di operatori per il trattamento dei bit:
● & AND bit a bit
● | OR bit a bit
● ^ OR esclusivo
● << shift sinistra
● >> shift destra
● ~ complemento a 1
Occorre precisare che questi operatori non lavorano su variabili di tipo float o double, ma su
variabili di tipo char e int: questo perchè i valori in virgola mobile non hanno alcun
significato(i bit sono o 0 o 1).
Le operazioni per il trattamento dei bit, con l’eccezione delle operazioni di shift e
complemento, si applicheranno tipicamente a variabili di tipo unsigned char e unsigned
int, poiché il segno aritmetico non ha significato.
Le operazioni di shift << e >> traslano il primo operando, rispettivamente a sinistra e a
destra, di un numero di posizioni corrispondenti al valore del secondo operando. Es:
a=a<<2
L’effetto delle operazioni di shift cambia a seconda che le si applichi a variabili senza segno
o con segno:
In generale valgono le seguenti regole:
● se si esegue lo shift a destra di una variabile unsigned i bit vacanti a sinistra sono
rimpiazzati con degli 0;
● se si esegue lo shift a destra di una variabile con segno i bit vacanti a sinistra sono
rimpiazzati con il bit del segno su certe macchine (shift aritmetico) e con 0 su altre
(shift logico);
● se si esegue lo shift a sinistra di una variabile unsigned, i bit vacanti a destra sono
rimpiazzati con degli 0;
● se si esegue lo shift a sinistra di una variabile con segno, i bit vacanti a destra sono
rimpiazzati con degli 0 e il bit del segno rimane inalterato.
15 LA RICORSIONE
Una funzione viene detta ricorsiva quando nel suo corpo richiama se stessa. Puo richiamarsi
direttamente o indirettamente:
• Ricorsione Diretta: la procedura invoca direttamente nel suo corpo se stessa
int f(int x) { …. f(x) …. }
• Ricorsione indiretta: la procedura invoca un’altra procedura che a sua volta chiama la
procedura originaria int f(int x) { …. g(x) …. }
int g(int x) { …. f(x) …. }
come si pensa ad una funzione ricorsiva?:
si basa sul principio di induzione:
consideriamo una proprietà P(x) che si basa sui numeri naturali (x) e ipotizziamo che:
• P(x) sia vera per x = 1, (Base dell'induzione)
• P sia vera per un valore generico n (Ipotesi Induttiva)
se a partire dalla verità di P(n) riusciamo a dimostrare la verità di P(n + 1), allora P(k) è vera
per qualsiasi valore di k
(spiegazione terra terra: se è una proprietà basata sui numeri naturali, e riusciamo a dimostrare
queste 2 cose:
1. è vera per x=1
2. se è vera per un numero, è vera anche per il suo successivo (n+1),
allora è vera per tutti i numeri naturali)
Basandosi su questo ci si approccia ad una funzione ricorsiva (approccio divide et impera):
● Divide: a partire dal problema da risolvere P sui dati di ingresso, si individuano
problemi del medesimo tipo di quelli originari, ma aventi dimensioni più piccole. La
suddivisione va ripetuta fino a problemi tali da conoscerne la soluzione; tali casi
vengono detti casi base.
● Impera: si ritiene, per ipotesi, di essere in grado di risolvere ciascuno dei sotto
problemi in cui si è decomposto P correttamente e di conoscerne la soluzione.
● Combina: a partire dalle soluzioni, ritenute corrette, dei sotto problemi in cui si è
diviso P, si costruisce la soluzione corretta al problema P stesso.
esempio (che ormai abbiamo capito)
esistono vari tipi di ricorsione:
● diretta e indiretta (già spiegati prima)
● Ricorsione Lineare: Un algoritmo si dice ricorsivo lineare se nel suo corpo è
presente una sola chiamata a se stesso.
● Ricorsione in coda: Un algoritmo ricorsivo si dice che è ricorsivo in coda se la
chiamata ricorsiva è l'ultima istruzione dell'algoritmo stesso.
● Ricorsione Multipla: Un algoritmo si dice a ricorsione multipla se nel suo corpo
sono presenti più chiamate a se stesso. Un caso semplice e molto frequente di
ricorsione multipla è quella detta binaria che si presenta quando sono presenti due
sole chiamate ricorsive.
● Ricorsione Mutua: Un algoritmo si dice a ricorsione mutua se è composto da una
prima funzione che al suo interno ne chiama una seconda che a sua volta richiama la
prima. (indiretta)
● Ricorsione Annidata: Un algoritmo si dice a ricorsione annidata se è composto da
una funzione che ha come argomento una chiamata alla funzione stessa.
esempio
esempi di esercizi: calcolo fattoriale, potenza, torre di hanoi, fibonacci, mcd…
16 GTK
La libreria gtk è utilizzata per implementare applicazioni grafiche con il linguaggio c.
Si parla di un nuovo tipo di programmazione, la programmazione ad eventi:
Questa tipologia di approccio consiste nel creare gli “ eventi “ come per esempio un pulsante
che può essere cliccato in una pagina o una casella di testo che viene riempita. Dopodiché
si associano le azioni agli eventi (esempio, la chiusura del programma se viene cliccato il
pulsante “chiudi” oppure il click del mouse su un pulsante qualsiasi).
Fin tanto che un evento non viene riconosciuto, la macchina rimane in un loop infinito
(stato di attesa)
Per creare un programma con applicazioni grafiche, prima si include la libreria:
#include<gtk/gtk.h>
Dopodichè si passa alla creazione di una finestra
GtkWidget *window; (tipo FILE *fp;)
La finestra è uno dei componenti principali,e, come tutti gli altri, per essere creata bisogna
dichiarare un puntatore a tipo GtkWidget.
Per creare la finestra si esegue il comando:
window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
Infine bisogna visualizzarla con il comando apposito:
gtk_widget_show(window);
Dopodichè bisogna mettersi in attesa di eventi:
gtk_main();
Nel momento in cui ci si mette in attesa di eventi, il programma entra in un loop infinito.
Quando uno dei widget riceve un evento, vengono notificati più segnali al programma,
dopodiché bisogna connettere questi segnali al widget per far in modo che funzionino.
Quindi per sviluppare un’interfaccia grafica, i procedimenti sono:
1. Disegnare la finestra.
2. Scegliere gli eventi.
3. Definire quali azioni svolgere in corrispondenza degli eventi.
4. Collegare le azioni ai relativi eventi e componenti.
5. Visualizzare le componenti (come la finestra con le varie cose)
6. Attendere gli eventi.
Per disegnare un’interfaccia grafica si possono utilizzare vari componenti:
● Etichette (label)
● Pulsanti
● Caselle di testo
● Checkbox ecc..
Per esempio,
per creare un’etichetta (label), come per le window, inizialmente si deve dichiarare un
puntatore a widget: Widget *label;
label=gtk_new_label(“inserisci il testo”);
oppure il pulsante: Widget* button;
button=new_button_with_label(“clicca qui”);
GESTIONE DEGLI EVENTI
Quando un oggetto riceve un evento, bisogna segnalarlo al programma.
Per gestire un evento, bisogna collegarlo ad un’azione tramite il metodo:
g_signal_connect();
All’interno di questo metodo si passano come parametri:
● L’oggetto in questione. Tipo “button”
● L’evento. Tipo: “clicked”
● Il Callback(AzioneDaSvolgere)
● I dati aggiuntivi che l’utente vuole passare oltre allo svolgimento dell’azione.
Esempio:
const char *message = "Hello from the callback!";
// Collegamento del gestore di segnali con il dato utente
g_signal_connect(button, "clicked", G_CALLBACK(mybuttonclicked),
(gpointer)message);
In questo esempio, oltre all’azione, l’utente passa un messaggio ( ), che
(gpointer)message
viene inserito nella funzione tramite il suo puntatore.
Tuttavia, nessuno programma le interfacce grafiche, esistono appositi software come glade,
che semplificano la creazione di oggetti. Il programmatore dovrà solo occuparsi di scrivere
correttamente i callback();.
10 SOTTOPROGRAMMI
Quando si scrive un programma succede spesso che si debba eseguire numerose volte una
stessa sequenza di istruzioni, sugli stessi dati o su dati diversi: Per evitare di riscrivere più
volte queste sequenze di istruzioni e per limitare le dimensioni dei programmi, il linguaggio C
dispone di una particolare struttura chiamata sottoprogrammi.
● Un sottoprogramma (o funzione) è un modulo di programma la cui esecuzione può
essere invocata più volte nel corso del programma principale.
● La funzione può calcolare uno o pi&ugra