Introduzione al linguaggio C Dr. Francesco Fabozzi Corso di Informatica
Area del cerchio Programma che calcola l’area di un cerchio dato il raggio in input #include <stdio.h> main(){ float raggio, area; const float pi = 3.141592; printf(“Inserisci il raggio \n”); scanf(“%f”,&raggio); area = raggio*raggio*pi; printf(“area del cerchio = %f \n”, area); }
Funzione area del cerchio Modifichiamo il programma incapsulando il calcolo dell’area in una funzione #include <stdio.h> float AreaCerchio(float r); main(){ float raggio; printf(“Inserisci il raggio \n”); scanf(“%f”,&raggio); printf(“area del cerchio = %f \n”, AreaCerchio(raggio) ); } float AreaCerchio( float r ){ float area; const float pi = 3.141592; area = r * r * pi; return area; Dichiarazione o prototipo della funzione Definizione della funzione
Funzioni in C Una funzione in C può essere chiamata in qualsiasi parte di un programma Una funzione può accettare una lista di parametri in input (argomenti passati alla funzione) e può restituire un valore in output Una funzione può essere definita o nello stesso file sorgente del main o in un altro file sorgente
Definizione di una funzione La definizione di una funzione deve specificare il suo nome, il tipo restituito, i tipi dei parametri e i loro nomi I nomi dei parametri sono locali alla funzione stessa e non sono visibili a nessun’altra funzione Possono quindi essere usati da altre funzioni senza conflitti tipo_restituito nome_funzione(lista parametri){ … }
Chiamata “per valore” In C gli argomenti di una funzione sono passati “per valore” (by value) Gli argomenti passati alla funzione non sono accessibili direttamente da questa ma sono copiati in variabili temporanee locali alla funzione La funzione opera sulle variabili temporanee Una chiamata di funzione non può modificare una variabile della funzione chiamante
Chiamata “per valore” Esempio: /* elevamento a potenza intera di un intero */ int potenza( int base, int n ){ for( int p = 1; n > 0; n-- ) p *= base; return p; } La chiamata della funzione potenza non altera il valore di n nella funzione chiamante
Parametri formali e reali I parametri usati nella funzione Sono locali alla funzione stessa Parametri reali (actual parameters) Le variabili della funzione chiamante passate alla funzione come parametri Non possono essere alterate dalla funzione stessa
Valore restituito L’istruzione return serve a restituire il valore di una qualsiasi espressione alla funzione chiamante return espressione; Se una funzione non restituisce alcun valore il suo tipo è void e l’istruzione return non è accompagnata da nessuna espressione Il return passa semplicemente il controllo alla funzione chiamante
Valore restituito Anche la funzione main può restituire un valore In genere si fa restituire un intero int main( ){ … } In genere si fa ritornare 0 se il programma termina correttamente return 0;
Dichiarazione di una funzione Prima del main occorre dichiarare le funzioni che saranno usate Nella dichiarazione i nomi dei parametri sono opzionali float AreaCerchio( float ); La dichiarazione (o prototipo) di una funzione deve essere in accordo con la sua definizione
Funzione area del cerchio Modifichiamo ancora la funzione inserendo un controllo sul valore del raggio float AreaCerchio( float r ){ float area; const float pi = 3.141592; if( r < 0 ) { printf(“raggio non valido!”) area = -1.0; } else area = r * r * pi; return area; }
Area di più cerchi Modifichiamo il programma in modo da poter calcolare l’area di n cerchi #include <stdio.h> float AreaCerchio(float r); main(){ float raggio; int ncerchi; printf(“Inserisci il numero di cerchi \n”); scanf(“%d”,&ncerchi); for(int i=0; i<ncerchi; i++) { printf(“Inserisci il raggio del cerchio n. %d \n”, i+1); scanf(“%f”,&raggio); printf( “area = %f \n”, AreaCerchio(raggio) ); }
Area di più cerchi Modifichiamo il programma in modo da poter calcolare l’area di un numero indefinito di cerchi #include <stdio.h> float AreaCerchio(float r); main(){ float raggio; printf(“Inserisci 0 per terminare \n”); printf(“Inserisci il raggio \n”); scanf(“%f”,&raggio); while( raggio != 0 ) { printf(“area = %f \n”, AreaCerchio(raggio)); }
Area di più cerchi Oppure… #include <stdio.h> float AreaCerchio(float r); main(){ float raggio; int ncerchi; printf(“Inserisci 0 per terminare \n”); do{ printf(“Inserisci il raggio \n”); scanf(“%f”,&raggio); printf(“area = %f \n”, AreaCerchio(raggio)); } while( raggio != 0) }
Vettori (arrays) Un vettore (array) è un gruppo di locazioni di memoria identificato con un solo nome Da un punto di vista logico un vettore è una sequenza di N variabili omogenee (cioè dello stesso tipo) A[ 0 ], A[ 1 ], …, A[ N-1 ] A = nome del vettore N = lunghezza o dimensione del vettore
Vettori (arrays) La lunghezza di un vettore va specificata nella dichiarazione della variabile oltre al suo tipo e nome Un array può essere inizializzato in fase di dichiarazione In questo caso è opzionale specificarne la lunghezza Esempi: int A[5] : vettore di 5 elementi interi float raggi[100] : vettore di 100 elementi reali float s[3]={1.3,2.1,3.4} : vettore di 3 elementi reali float s[]={1.3,2.1,3.4}
Elementi di un array Un elemento di un array è identificato dal nome del vettore seguito da un indice intero tra parentesi Esempi: A[4], raggi[56] L’indice parte da 0 e non può essere maggiore della lunghezza del vettore - 1 L’indice può essere specificato con una variabile, una costante o una generica espressione intera Esempio: X[n-m]
Elementi di un array 34 243 3 5670 761 Rappresentazione di un vettore int A[5] A[ 0 ] 34 A[ 1 ] 243 A[ 2 ] 3 A[ 3 ] 5670 A[ 4 ] 761 Il simbolo A indica anche l’indirizzo della prima locazione di memoria del vettore Locazioni di memoria
Vettori come parametri di funzioni Un vettore può essere passato come argomento a una funzione Per specificare che un parametro formale in una funzione deve essere un vettore occorre far seguire il nome da parentesi quadre float leggiElemento( float v[ ], int n ); In questo caso non occorre specificare la dimensione del vettore in quanto essa è definita nella funzione chiamante
Vettori come parametri di funzioni Nel caso dei vettori non si ha la chiamata “per valore” Non viene fatta la copia temporanea del vettore Il valore passato alla funzione è l’indirizzo (o puntatore) della prima locazione del vettore Una funzione può quindi usare e modificare qualsiasi elemento del vettore che le è stato passato
Uso dei vettori L’uso dei vettori è particolarmente efficace quando dobbiamo ripetere la stessa operazione su un gruppo di variabili omogenee In questo caso si può definire un vettore e iterare sul suo indice Altro uso dei vettori è quello di immagazzinare degli elementi per poterli poi ordinare o fare ricerche su di essi
Uso dei vettori Esempio: void sommaVettori(float a[],float b[],float s[],int n){ for(int i = 0; i < n; i++) s[i] = a[i] + b[i]; return; } void copiaVettori(float a[],float b[],int n){ b[i] = a[i]; Il vettore passato alla funzione viene modificato dalla chiamata di funzione
Uso dei vettori Esempio: int cercaElemento(float x,float a[],int n){ int trovato = 0; for(int i = 0; i < n; i++){ if(a[i] == x){ trovato = 1; return trovato; } /* se a[i] != x allora passo all’iterazione successiva */ } /* fine ciclo for */ }
Array multidimensionali E’ possibile definire vettori multidimensionali Un elemento è identificato da più indici Esempio: la dichiarazione int c[2] [2]; Dichiara un vettore con 4 = 2x2 elementi interi c[0] [0], c[1] [0], c[0] [1], c[1] [1] Esempio: dichiarazione e assegnazione int c[ ] [ ] = { {7,12}, {2,16} }; c[0,0] c[1,0] c[0,1] c[1,1]
Puntatori Una variabile è caratterizzata da un nome, un tipo e occupa un certo numero di locazioni di memoria a seconda del tipo indirizzo contenuto … 100 1000 5 a int a = 5; int x; x = a 5 x
Puntatori Una puntatore è una variabile che contiene l’indirizzo della locazione di memoria di un’altra variabile Per dichiarare un puntatore occorre specificare il tipo della variabile di cui deve contenere l’indirizzo seguito da uno * Punt_a è un puntatore a una variabile intera int * Punt_a;
Puntatori Un puntatore quando viene dichiarato non punta a un indirizzo valido, quindi prima di poterlo usare occorre necessariamente inizializzarlo Si puó inizializzare a NULL (indica un puntatore nullo) Punt_a viene inizializzato a un puntatore nullo int * Punt_a = NULL; Puntatore nullo int * Punt_a; *Punt_a = 100; ERRORE!
L’operatore & Per assegnare l’indirizzo di una variabile a un puntatore si fa uso dell’operatore unario & (operatore di referenza) L’operatore & restituisce l’indirizzo di una variabile Punt_a è un puntatore a una variabile intera a cui viene assegnato l’indirizzo della variabile a int a = 5; int * Punt_a; Punt_a = &a; Si dice che: Punt_a “punta” ad a
L’operatore & int a = 5; int * Punt_a; Punt_a = &a; a 5 700000 Punt_a indirizzo contenuto … 700000 800000 5 a 700000 Punt_a Punt_a “punta” ad a
L’operatore & int a = 5; int b = 6; printf(“Valore di a = %d \n”, a); /* stampa 5 */ printf(“Indirizzo di a = %X \n”, &a); /* stampa l’indirizzo di a */ a = b; /* stampa 6 */ /* stampa l’indirizzo di a (invariato) */ Inserisce un numero in esadecimale
L’operatore * L’operatore * (operatore di dereferenza) applicato a un puntatore restituisce il contenuto della locazione a cui il puntatore punta int a = 5; int b = 6; int c = 7; int * Punt_a = &a; int * Punt_b = &b; c = *Punt_a; /* adesso c = 5 */ *Punt_b = a; /* adesso b = 5 */
L’operatore * int a = 5; int b = 6; int * Punt_a = &a; printf(“Valore di a = %d \n”, *Punt_a); /* stampa: Valore di a = 5 */ printf(“Indirizzo di a = %X \n”, Punt_a); /* stampa l’indirizzo di a */ *Punt_a = b; printf(“Valore di a = %d \n”, a); /* stampa: Valore di a = 6 */ Punt_a = &b; printf(“Indirizzo di a = %X \n”, &a); printf(“Indirizzo di b = %X \n”, Punt_a); /* stavolta stampa l’indirizzo di b */
Puntatori void I puntatori void sono puntatori generici in cui non viene specificato il tipo di dato A un puntatore void può essere assegnato l’indirizzo di qualsiasi tipo di dato int m = 1; float x = 4.35; void * pv; int * pa; pv = &m; /* pv punta a una variabile intera */ pa = (int *) pv; /* per assegnare a pa l’indirizzo a cui punta pv occorre fare un cast */ pv = &x; /* pv ora punta a una variabile float */
Puntatori const Un puntatore definito con l’attributo const non può modificare il contenuto della variabile a cui punta const int * pa; int m = 1; pa = &m; *pa = 2; /* errore! pa è un puntatore const */
Avvertenze sui puntatori Non confondere puntatore con variabile puntata Un puntatore contiene un indirizzo di memoria La variabile puntata è un’area della memoria che comincia con l’indirizzo contenuto nel puntatore Questa area di memoria dipende dal tipo della variabile Un puntatore è anch’esso una variabile Occupa uno spazio di memoria L’area di memoria occupata dal puntatore è quella necessaria per contenere l’indirizzo del dato puntato
Puntatori come argomenti di funzioni Un puntatore può essere passato a una funzione come suo argomento E’ il puntatore ad essere copiato come variabile locale nella funzione Tramite il puntatore la funzione ha accesso alla variabile originaria La funzione può modificare il valore della variabile di cui è stato passato il puntatore In questo caso si dice che la variabile è passata per riferimento (by reference) Se non si vuole modificare il valore della variabile occorre passare un puntatore const
Puntatori come argomenti di funzioni Esempio: funzione che ritorna il quadrato di un intero Viene passata la variabile Viene passato il puntatore int quad1( int n ) { n = n * n; return n; } int quad2( int * pn ) { *pn = (*pn) * (*pn); return (*pn); } int i = 3; int m = quad1( i ); /* m = 9, i = 3 */ int i = 3; int * pi = &i; int m = quad2( pi ); /* m = 9, i = 9 */
Puntatori come argomenti di funzioni Esempio: funzione che immagazzina il prodotto scalare di due vettori in un’altra variabile void prodScalare( int dim, float a[], float b[], float * val) { *val = 0; for( int i = 0; i < dim; i++ ) *val = *val + a[i] * b[i]; return; }
Vettori e puntatori Il nome di un array è un puntatore alla prima locazione di memoria dell’array L’accesso agli elementi dell’array può essere effettuato anche tramite puntatori Elementi di un array Indirizzo valore Puntatore & A[ 0 ] A[ 0 ] A *A & A[ 1 ] A[ 1 ] A+1 *(A+1) & A[ 2 ] A[ 2 ] A+2 *(A+2) & A[ 3 ] A[ 3 ] A+3 *(A+3)
Vettori e puntatori C’è una differenza fondamentale tra il nome di un array e un puntatore Un puntatore può cambiare il suo valore Il nome di un array punta sempre al primo elemento dell’array e non può cambiare il suo valore
Vettori e puntatori Esempio: int * pa; int m = 3; int a[5] = {10,21,2,75,9}; pa = a; /* pa punta al primo elemento dell’array */ *(pa+2) = m; /* adesso a[2]=3 pa = &m; /* OK! adesso pa punta alla variabile m */ a = &m /* errore! a non può puntare a un’altra variabile */
Stringhe Una stringa è un vettore di caratteri terminato dal carattere speciale \0 (carattere di fine stringa o terminatore della stringa) / stringa di 14 caratteri */ char frase[] = “Il colore blu”; /* oppure */ char * frase = “Il colore blu”; I l c o r e b u \0 \0 = carattere di fine stringa
Stringhe Una stringa può essere assegnata a un vettore con la funzione scanf() char parola[20]; scanf(“%s”, parola); NOTA: nella chiamata della funzione scanf parola non è preceduto da &
Manipolazione di stringhe La libreria standard del C fornisce delle funzioni per la manipolazione di stringhe #include <string.h> char * strcopy(char* s1, const char * s2) /* copia s2 in s1 e ritorna s1 */ char * strncopy(char* s1, const char * s2, size_t n) /* copia i primi n caratteri di s2 in s1 e ritorna s1 */ char * strcat(char* s1, const char * s2) /* accoda s2 ad s1 e ritorna s1 char * strncat(char* s1, const char * s2, /* accoda i primi n caratteri di s2 in s1 e ritorna s1 */
Manipolazione di stringhe La libreria standard del C fornisce delle funzioni per la manipolazione di stringhe #include <string.h> int strcmp(const char* s1, const char * s2) /* confronta s1 con s2 e ritorna il risultato: <0 se s1<s2 =0 se s1=s2 >0 se s1>s2 */ size_t * strlen(const char * s) /* ritorna la lunghezza di s */