TIPI DI DATO Un tipo di dato T

  • Slides: 98
Download presentation
TIPI DI DATO Un tipo di dato T è definito come: · un dominio

TIPI DI DATO Un tipo di dato T è definito come: · un dominio di valori, D · un insieme di funzioni F 1, . . , Fn sul dominio D · un insieme di predicati P 1, . . , Pm sul dominio D T = { D, {F 1, . . . , Fn}, {P 1, . . . , Pm} }

TIPI DI DATO I tipo di dato si differenziano in scalari e strutturati.

TIPI DI DATO I tipo di dato si differenziano in scalari e strutturati.

TIPI DI DATO In C si possono definire nuovi tipi strutturati. Vi sono due

TIPI DI DATO In C si possono definire nuovi tipi strutturati. Vi sono due costruttori fondamentali: [ ] (array) struct (strutture)

STRUTTURE Una struttura è una collezione finita di variabili non necessariamente dello stesso tipo,

STRUTTURE Una struttura è una collezione finita di variabili non necessariamente dello stesso tipo, ognuna identificata da un nome. struct persona nome età stringa di 20 char un intero stipendio un float

ARRAY (vettori) Un array è una collezione finita di N variabili dello stesso tipo,

ARRAY (vettori) Un array è una collezione finita di N variabili dello stesso tipo, ognuna identificata da un indice compreso fra 0 e N-1 v 0 1 2 3 v[0] v[3] v[1] v[2]

ARRAY Un array è una collezione finita di N variabili dello stesso tipo, ognuna

ARRAY Un array è una collezione finita di N variabili dello stesso tipo, ognuna identificata da un indice compreso fra 0 e N-1. Definizione di una variabile di tipo array: <tipo> <nome. Array> [ <costante> ]; Esempi: int v[4]; char nome[20]; v 0 1 2 3 v[0] v[3] v[1] v[2]

ESEMPIO Problema: scrivere un programma che, dato un vettore di N interi, determini il

ESEMPIO Problema: scrivere un programma che, dato un vettore di N interi, determini il valore massimo. Specifica di I livello: Inizialmente, si assuma come massimo di tentativo il primo elemento. Poi, si confronti via il massimo di tentativo con gli elementi del vettore: nel caso se ne trovi uno maggiore del massimo di tentativo attuale, si aggiorni il valore del massimo. Al termine, il valore del massimo di tentativo coincide col valore massimo ospitato nel vettore.

ESEMPIO Problema: scrivere un programma che, dato un vettore di N interi, determini il

ESEMPIO Problema: scrivere un programma che, dato un vettore di N interi, determini il valore m massimo. 0 = v[0] m 0 v[0] Specifica di I livello: , v[i]) il Inizialmente, si assuma comemmassimo dii-1 tentativo i = max(m mi v[0], v[1]. . v[i] primo elemento. Poi, si confronti via il massimo di tentativo con gli elementi del vettore: nel caso se ne trovi uno maggiore del massimo di tentativo attuale, si aggiorni il mn-1 v[0], v[1]…v[n-1] valore del massimo. cioè m è il maxcoincide cercato. Al termine, il valore del massimo din-1 tentativo col valore massimo ospitato nel vettore.

ESEMPIO Codifica: Espressione di inizializzazione di un array #define DIM 4 main() { int

ESEMPIO Codifica: Espressione di inizializzazione di un array #define DIM 4 main() { int v[DIM] = {43, 12, 7, 86}; int i, max=v[0]; for (i=1; i<DIM; i++) if (v[i]>max) max = v[i]; /* ora max contiene il massimo */ }

ESEMPIO Codifica: Se vi è una inizializzazione esplicita, la dimensione dell’array può essere omessa!

ESEMPIO Codifica: Se vi è una inizializzazione esplicita, la dimensione dell’array può essere omessa! #define DIM 4 main() { int v[] = {43, 12, 7, 86}; int i, max=v[0]; for (i=1; i<DIM; i++) if (v[i]>max) max = v[i]; /* ora max contiene il massimo */ }

ESEMPIO - UNA VARIANTE Anziché inizializzare l’array a priori, calcoliamo i valori iniziali come

ESEMPIO - UNA VARIANTE Anziché inizializzare l’array a priori, calcoliamo i valori iniziali come parte dell’algoritmo. #define DIM 4 Nessuna inizializzazione main() { Calcolo valori iniziali int i, max, v[DIM]; for (i=0; i<DIM; i++) v[i]=i+1; for (max=v[0], i=1; i<DIM; i++) if (v[i]>max) max = v[i]; } Inizializzazione di due variabili

DIMENSIONE FISICA vs. LOGICA · Un array è una collezione finita di N celle

DIMENSIONE FISICA vs. LOGICA · Un array è una collezione finita di N celle dello stesso tipo · Questo non significa che si debbano per forza usare sempre tutte! · La dimensione logica di un array può essere inferiore (mai superiore!) alla sua dimensione fisica · Spesso, la porzione di array realmente utilizzata dipende dai dati d’ingresso.

DIMENSIONE FISICA vs. LOGICA Esempio È data una serie di rilevazioni di temperature espresse

DIMENSIONE FISICA vs. LOGICA Esempio È data una serie di rilevazioni di temperature espresse in gradi Kelvin. Ogni serie è composta di al più 10 valori, ma può essere più corta. Il valore “-1” indica che la serie delle temperature è finita. Scrivere un programma che, data una serie di temperature memorizzata in un vettore, calcoli la media delle temperature fornite.

DIMENSIONE FISICA vs. LOGICA Il vettore deve essere dimensionato Esempio per 10 celle (caso

DIMENSIONE FISICA vs. LOGICA Il vettore deve essere dimensionato Esempio per 10 celle (caso peggiore). . . È data una serie di rilevazioni di temperature espresse in gradi Kelvin. Ogni serie è composta di al più 10 valori, ma può essere più corta. Il valore “-1” indica che la serie delle temperature è finita. … ma la porzione realmente Scrivere un programma che, data una serie di usata può essere minore! temperature memorizzata in un vettore, calcoli la media delle temperature fornite.

ESEMPIO Problema: È dato un vettore di al più 10 interi non negativi, che

ESEMPIO Problema: È dato un vettore di al più 10 interi non negativi, che rappresentano temperature in gradi Kelvin. Calcolare la media delle temperature fornite. Specifica di I livello: • calcolare la somma di tutti gli elementi del vettore, e nel frattempo contare quanti sono • il risultato è il rapporto fra la somma degli elementi così calcolata e il numero degli elementi.

ESEMPIO Problema: È dato un vettore di al più 10 interi non negativi, che

ESEMPIO Problema: È dato un vettore di al più 10 interi non negativi, che rappresentano temperature in gradi Kelvin. Calcolare la media delle s 0 temperature = 0, k 0 = 0 fornite. Specifica di II livello: Inizialmente, poni uguale a 0 una variabile S che sk = sk-1 + v[k], rappresenti la somma corrente, e poni a 0 un kk+1 = kk +uguale 1, k<N indice K che rappresenti l’elemento corrente. A ogni passo, aggiungi l’elemento corrente a una variabile S che funga da somma. s. N-1 = s. N-2 + v[N-1], Al termine (quando o un elemento vale k. N -1, = Noppure hai esaminato N elementi), l’indice K rappresenta il numero totale di elementi: il risultato è il rapporto S/K.

ESEMPIO Codifica: #define DIM 10 main() { int k, v[DIM] = int media, s=0;

ESEMPIO Codifica: #define DIM 10 main() { int k, v[DIM] = int media, s=0; for (k=0; k<DIM s += v[k]; media = s / k; } Dimensione fisica = 10 Dimensione logica = 4 {273, 340, 467, -1}; && v[k]>=0; k++) Condizione di prosecuzione del ciclo: la serie di dati non è finita (v[k] 0) e ci sono ancora altre celle nell’array (k<DIM)

ESERCIZIO: VALUTAZIONE DI UN POLINOMIO pn(x) = an xn + an-1 xn-1 +. .

ESERCIZIO: VALUTAZIONE DI UN POLINOMIO pn(x) = an xn + an-1 xn-1 +. . . a 1 x + a 0 Idea: riscriverlo così (Metodo di Horner): (. . . ( ( an x + an-1) x + an-2 ) x +. . . + a 1) x + a 0 Invariante: v 0 = an vi+1 = vi * x + an-1 -i per 0 i n-1

VALUTAZIONE DI UN POLINOMIO Esempio: p 3(x) = 3 x 3 + 2 x

VALUTAZIONE DI UN POLINOMIO Esempio: p 3(x) = 3 x 3 + 2 x 2 + 6 x + 4 si riscrive come: p 3(x) = ( ( 3 x + 2 ) x + 6 ) x + 4 dove l’array dei coefficienti è: a[4] 4 6 2 3 0 1 2 3 Grado: n = 3 Array: dim = 4 Posizione i-ma = coeff. i-mo

VALUTAZIONE DI UN POLINOMIO Il polinomio: p 3(x) = 3 x 3 + 2

VALUTAZIONE DI UN POLINOMIO Il polinomio: p 3(x) = 3 x 3 + 2 x 2 + 6 x + 4 Il cliente: main(){ Grado: n = 3 double a[] = {4, 6, 2, 3}; Array: dim = 4 double x = -1. 0; double v = horner(x, a, 3); }

VALUTAZIONE DI UN POLINOMIO double horner(double x, double a[], int n){ return pol(x, a,

VALUTAZIONE DI UN POLINOMIO double horner(double x, double a[], int n){ return pol(x, a, 0, n, a[n]); } double pol(double x, double a[], int i, int n, double v){ return (i==n) ? v : pol(x, a, i+1, v*x+a[n-i-1]); }

STRINGHE DI CARATTERI Una stringa di caratteri in C è un array di caratteri

STRINGHE DI CARATTERI Una stringa di caratteri in C è un array di caratteri terminato dal carattere '' s a p e 0 1 2 3 Un vettore di N caratteri può dunque ospitare stringhe lunghe al più N-1 caratteri, perché una cella è destinata al terminatore ''.

STRINGHE DI CARATTERI Un array di N caratteri può ben essere usato per memorizzare

STRINGHE DI CARATTERI Un array di N caratteri può ben essere usato per memorizzare stringhe più corte s d i 0 1 2 3 In questo caso, le celle oltre la k-esima (k essendo la lunghezza della stringa) sono concettualmente vuote: praticamente sono inutilizzate e contengono un valore casuale.

STRINGHE DI CARATTERI Una stringa di caratteri si può inizializzare, come ogni altro array,

STRINGHE DI CARATTERI Una stringa di caratteri si può inizializzare, come ogni altro array, elencando le singole componenti: char s[4] = {'a', 'p', 'e', ''}; oppure anche, più brevemente, con la forma compatta seguente: char s[4] = "ape" ; Il carattere di terminazione ‘’ è automaticamente incluso in fondo. Attenzione alla lunghezza!

ESEMPIO Problema: Data una stringa di caratteri, calcolarne la lunghezza. Ipotesi: La stringa è

ESEMPIO Problema: Data una stringa di caratteri, calcolarne la lunghezza. Ipotesi: La stringa è “ben formata”, ossia correttamente terminata dal carattere ‘’. Specifica: • scandire la stringa elemento per elemento, fino a trovare il terminatore ‘’ (che esiste certamente) • il risultato è l’indice corrispondente al terminatore.

ESEMPIO Codifica: Non avendo dato una dimensione esplicita, l’array è automaticamente dimensionato a lung+1

ESEMPIO Codifica: Non avendo dato una dimensione esplicita, l’array è automaticamente dimensionato a lung+1 caratteri. main() { char s[] = "Nel mezzo del cammin di"; int lung=0; for (lung=0; s[lung]!=''; lung++); /* lung rappresenta il risultato */ }

ESEMPIO Problema: Data una stringa di caratteri, copiarla in un altro array di caratteri

ESEMPIO Problema: Data una stringa di caratteri, copiarla in un altro array di caratteri (di lunghezza non inferiore). Ipotesi: La stringa è “ben formata”, ossia correttamente terminata dal carattere ‘’. Specifica: • scandire la stringa elemento per elemento, fino a trovare il terminatore ‘’ (che esiste certamente) • nel fare ciò, copiare l’elemento nella posizione corrispondente dell’altro array.

ESEMPIO Codifica: La dimensione deve essere tale da garantire che la stringa non “debordi”

ESEMPIO Codifica: La dimensione deve essere tale da garantire che la stringa non “debordi” main() { char s[] = "Nel mezzo del cammin di"; char s 2[40]; int i=0; for (i=0; s[i]!=''; i++) s 2[i] = s[i]; Al termine, occorre garantire s 2[i] = ''; che anche la nuova stringa sia “ben formata”, inserendo } esplicitamente il terminatore.

ESEMPIO Problema: Data una stringa di caratteri, copiarla in un altro array di caratteri,

ESEMPIO Problema: Data una stringa di caratteri, copiarla in un altro array di caratteri, con eventuale troncamento se la stringa è più lunga dell’array dato. Specifica: • scandire la stringa elemento per elemento, o fino a trovare il terminatore ‘’ (che esiste certamente), o fino alla lunghezza dell’array di destinazione (-1) • nel fare ciò, copiare l’elemento nella posizione corrispondente dell’altro array.

ESEMPIO Codifica: Si prosegue solo se non si è incontrato il terminatore e inoltre

ESEMPIO Codifica: Si prosegue solo se non si è incontrato il terminatore e inoltre c’è spazio nell’array destinazione #define N 20 main() { char s[] = "Nel mezzo del cammin di"; char s 2[N]; int i=0; for (i=0; s[i]!='' && i<N-1; i++) s 2[i] = s[i]; s 2[i] = ''; La condizione è i<N-1 perché } deve rimanere uno spazio per ‘’

ESEMPIO Problema: Date due stringhe di caratteri, decidere quale precede l’altra in ordine alfabetico.

ESEMPIO Problema: Date due stringhe di caratteri, decidere quale precede l’altra in ordine alfabetico. Rappresentazione dell’informazione: • poiché vi possono essere tre risultati (s 1<s 2, s 1==s 2, s 2<s 1), un boolean non basta • possiamo usare: – due boolean (uguale e precede) – tre boolean (uguale, s 1 precedes 2, s 2 precedes 1) – un intero (negativo, zero, positivo) scegliamo la terza via.

ESEMPIO Problema: Date due stringhe di caratteri, decidere quale precede l’altra in ordine alfabetico.

ESEMPIO Problema: Date due stringhe di caratteri, decidere quale precede l’altra in ordine alfabetico. Specifica: • scandire uno ad uno gli elementi di egual posizione delle due stringhe, o fino alla fine delle stringhe, o fino a che se ne trovano due diversi – nel primo caso, le stringhe sono uguali – nel secondo, sono diverse • nel secondo caso, confrontare i due caratteri così trovati, e determinare qual è il minore – la stringa a cui appartiene tale carattere precede l’altra

ESEMPIO la costante stringa continua a capo Codifica: main() { char s 1[] =

ESEMPIO la costante stringa continua a capo Codifica: main() { char s 1[] = "Sempre caro mi fu quell’ ermo colle"; char s 2[] = "Sempre odiai quell’orrido colle"; int i, stato; for (i=0; s 1[i]!='' && s 2[i]!='' && s 1[i]==s 2[i] ; i++); stato = s 1[i]-s 2[i]; negativo s 1 precede s 2 } positivo s 2 precede s 1 zero s 1 è uguale a s 2

UNA RIFLESSIONE Nell’esempio della copiatura: Data una stringa di caratteri, copiarla in un altro

UNA RIFLESSIONE Nell’esempio della copiatura: Data una stringa di caratteri, copiarla in un altro array di caratteri (di lunghezza non inferiore). abbiamo deciso di copiare la stringa nell’array carattere per carattere. Avremmo potuto fare diversamente? Perché non copiarla “tutta in un colpo”?

IL CONTROESEMPIO Perché non fare così? main() { char s[] = "Nel mezzo del

IL CONTROESEMPIO Perché non fare così? main() { char s[] = "Nel mezzo del cammin di"; char s 2[40]; s 2 = s; } Perché non dovrebbe funzionare? ? ? PERCHÉ GLI ARRAY NON POSSONO ESSERE MANIPOLATI “IN TOTO” !

IL CONTROESEMPIO Perché non fare così? main() { char s[] = "Nel mezzo del

IL CONTROESEMPIO Perché non fare così? main() { char s[] = "Nel mezzo del cammin di"; char s 2[40]; s 2 = s; } ERRORE DI COMPILAZIONE: incompatible types in assignment !!

ARRAY “VISTI DA VICINO” Concettualmente, un array è una collezione finita di N variabili

ARRAY “VISTI DA VICINO” Concettualmente, un array è una collezione finita di N variabili dello stesso tipo, ognuna identificata da un indice compreso fra 0 e N-1 v 0 1 2 3 v[0] v[3] v[1] v[2] Praticamente, le cose non stanno proprio così.

ARRAY “VISTI DA VICINO” In C un array è in realtà un puntatore costante

ARRAY “VISTI DA VICINO” In C un array è in realtà un puntatore costante che punta a un’area di memoria pre-allocata, di dimensione prefissata. v 0 1 2 3 Pertanto, il nome dell’array è un sinonimo per il suo indirizzo iniziale: v &v[0]

CONSEGUENZA Il fatto che il nome dell’array indichi • non l’array in sé, •

CONSEGUENZA Il fatto che il nome dell’array indichi • non l’array in sé, • ma l’indirizzo iniziale dell’area di memoria ad esso associata ha una importante conseguenza: È impossibile denotare un array nella sua globalità, in qualunque contesto.

CONSEGUENZA Quindi, non è possibile: • assegnare un array a un altro (s 2

CONSEGUENZA Quindi, non è possibile: • assegnare un array a un altro (s 2 = s) • che una funzione restituisca un array E soprattutto: • passare un array come parametro a una funzione non significa affatto passare l’intero array !!

IL CONTROESEMPIO Ecco perché non si compilava! main() { char s[] = "Nel mezzo

IL CONTROESEMPIO Ecco perché non si compilava! main() { char s[] = "Nel mezzo del cammin di"; char s 2[40]; s 2 = s; } Questo assegnamento viene interpretato come il tentativo di cambiare l’indirizzo iniziale della stringa s 2, cosa evidentemente impossibile !!!

ARRAY PASSATI COME PARAMETRI Poiché un array in C è un puntatore costante che

ARRAY PASSATI COME PARAMETRI Poiché un array in C è un puntatore costante che punta a un’area di memoria pre-allocata, di dimensione prefissata, il nome dell’array: • non rappresenta l’intero array • è un alias per il suo indirizzo iniziale (v &v[0] ) v 0 1 2 3

ARRAY PASSATI COME PARAMETRI Quindi, passando un array a una funzione: • non si

ARRAY PASSATI COME PARAMETRI Quindi, passando un array a una funzione: • non si passa l’intero array !! • si passa solo (per valore!) il suo indirizzo iniziale (v &v[0] ) v w 0 1 2 3

ARRAY PASSATI COME PARAMETRI Conclusione: • agli occhi dell’utente, l’effetto finale è che l’array

ARRAY PASSATI COME PARAMETRI Conclusione: • agli occhi dell’utente, l’effetto finale è che l’array passa per riferimento!! v w 0 1 2 3

CONCLUSIONE A livello fisico: • il C passa i parametri sempre e solo per

CONCLUSIONE A livello fisico: • il C passa i parametri sempre e solo per valore • nel caso di un array, si passa il suo indirizzo iniziale (v &v[0] ) perché tale è il significato del nome dell’array A livello concettuale: • il C passa per valore tutto tranne gli array, che vengono trasferiti per riferimento.

ESEMPIO Problema: Data una stringa di caratteri, scrivere una funzione che ne calcoli la

ESEMPIO Problema: Data una stringa di caratteri, scrivere una funzione che ne calcoli la lunghezza. Codifica: int lunghezza(char s[]) { int lung=0; for (lung=0; s[lung]!=''; lung++); return lung; La dimensione non serve, perché } tanto viene passato solo l’indirizzo iniziale (non tutto l’array)

UN’ALTRA RIFLESSIONE • Ma se quello che passa è solo l’indirizzo iniziale dell’array, che

UN’ALTRA RIFLESSIONE • Ma se quello che passa è solo l’indirizzo iniziale dell’array, che è un puntatore. . . • . . . allora tanto vale adottare direttamente la notazione a puntatori nella intestazione della funzione!! In effetti, l’una o l’altra notazione sono, a livello di linguaggio, assolutamente equivalenti – non cambia niente nel funzionamento – si rende solo più evidente ciò che accade comunque

ESEMPIO Da così. . . int lunghezza(char s[]) { int lung=0; for (lung=0; s[lung]!='�';

ESEMPIO Da così. . . int lunghezza(char s[]) { int lung=0; for (lung=0; s[lung]!=''; lung++); return lung; } Per il C è identico!! … a così: int lunghezza(char *s) { int lung=0; for (lung=0; s[lung]!=''; lung++); return lung; }

UN’ULTIMA RIFLESSIONE Ma se le due notazioni char *s char s[] sono identiche agli

UN’ULTIMA RIFLESSIONE Ma se le due notazioni char *s char s[] sono identiche agli occhi del compilatore, Cosa possiamo dire dei due operatori * e [] ? C’è qualche relazione?

OPERATORI DI DEREFERENCING • L’operatore *, applicato a un puntatore, accede alla variabile da

OPERATORI DI DEREFERENCING • L’operatore *, applicato a un puntatore, accede alla variabile da esso puntata • L’operatore [], applicato a un nome di array e a un intero i, accede alla i-esima variabile dell’array Sono entrambi operatori di dereferencing *v v[0]

ARITMETICA DEI PUNTATORI • Oltre a *v v[0], vale anche: *(v+1) v[1]. . .

ARITMETICA DEI PUNTATORI • Oltre a *v v[0], vale anche: *(v+1) v[1]. . . *(v+i) v[i] • Espressioni della forma p+i vanno sotto il nome di aritmetica dei puntatori, e denotano l’indirizzo posto i celle dopo l’indirizzo denotato da p (celle, non bytes!)

ARITMETICA DEI PUNTATORI Più in generale: • se p è un puntatore a T,

ARITMETICA DEI PUNTATORI Più in generale: • se p è un puntatore a T, e n è un intero positivo, l’espressione p+n denota un altro puntatore a T, che punta “n celle dopo” l’indirizzo puntato da p • Se n è negativo, la cella denotata da p+n in realtà precede di n posizioni quella puntata da p.

ARITMETICA DEI PUNTATORI q = p+n (n>0) +n*size p q

ARITMETICA DEI PUNTATORI q = p+n (n>0) +n*size p q

ARITMETICA DEI PUNTATORI Analogamente, • se q e p sono puntatori a T, l’espressione

ARITMETICA DEI PUNTATORI Analogamente, • se q e p sono puntatori a T, l’espressione q-p denota un intero, che rappresenta il numero di celle comprese fra q e p • se q precede p, l’intero denotato è negativo. NB: somme di puntatori, come p+q, sono illegali in quanto prive di significato.

CONCLUSIONE • Gli operatori * e []sono intercambiabili *(v+i) v[i] • Ne basterebbe uno

CONCLUSIONE • Gli operatori * e []sono intercambiabili *(v+i) v[i] • Ne basterebbe uno solo: il C li fornisce entrambi solo per nostra comodità – in effetti, operare sui vettori scrivendo *(v+i) sarebbe poco pratico! • Internamente, il compilatore C converte ogni espressione con [] nella corrispondente espressione con *

ESEMPIO Problema: Scrivere una funzione che, dato un array di N interi, ne calcoli

ESEMPIO Problema: Scrivere una funzione che, dato un array di N interi, ne calcoli il massimo. Si tratta di riprendere l’esercizio già svolto, e impostare la soluzione come funzione anziché codificarla direttamente nel main. Dichiarazione della funzione: int find. Max(int v[], int dim);

ESEMPIO Il cliente: main() { int max, v[] = {43, 12, 7, 86}; max

ESEMPIO Il cliente: main() { int max, v[] = {43, 12, 7, 86}; max = find. Max(v, 4); } Trasferire esplicitamente la dimensione dell’array è NECESSARIO, in quanto la funzione, ricevendo solo l’indirizzo iniziale, non avrebbe modo di sapere quanto è lungo l’array !

ESEMPIO La funzione: int find. Max(int v[], int dim) { int i, max; for

ESEMPIO La funzione: int find. Max(int v[], int dim) { int i, max; for (max=v[0], i=1; i<dim; i++) if (v[i]>max) max=v[i]; return max; }

ESEMPIO La funzione: int find. Max(const int v[], int dim) { int i, max;

ESEMPIO La funzione: int find. Max(const int v[], int dim) { int i, max; for (max=v[0], i=1; i<dim; i++) if (v[i]>max) max=v[i]; return max; } Per evitare che la funzione modifichi l’array (visto che è passato per riferimento), si può imporre la qualifica const Se lo si tenta: cannot modify a const object

ESEMPIO La funzione: int find. Max(const int *v, int dim) { int i, max;

ESEMPIO La funzione: int find. Max(const int *v, int dim) { int i, max; for (max=v[0], i=1; i<dim; i++) if (v[i]>max) max=v[i]; return max; Volendo si può usare anche la } notazione a puntatore: tanto, per il linguaggio sono equivalenti!! … e la si può anche mischiare con l’altra!

ESEMPIO Problema: Data una stringa di caratteri, scrivere una funzione che ne calcoli la

ESEMPIO Problema: Data una stringa di caratteri, scrivere una funzione che ne calcoli la lunghezza. Codifica: int lunghezza(char s[]) { int lung=0; for (lung=0; s[lung]!=''; lung++); return lung; } Nel caso delle stringhe, la dimensione non serve perché può essere dedotta dalla posizione dello ‘’

UNA VARIANTE Da così. . . int lunghezza(char *s) { int lung=0; for (lung=0;

UNA VARIANTE Da così. . . int lunghezza(char *s) { int lung=0; for (lung=0; s[lung]!=''; lung++); return lung; } Sfrutta il fatto che … a così: s è una copia dell’indirizzo iniziale int lunghezza(char *s) { della stringa, ergo char *s 0 = s; si può modificare! while (*s!='') s++; return s-s 0; } Sfrutta l’aritmetica dei puntatori

UNA VARIANTE “DA HACKER” Uno hacker la compatterebbe prima così. . . int lunghezza(char

UNA VARIANTE “DA HACKER” Uno hacker la compatterebbe prima così. . . int lunghezza(char *s) { char *s 0 = s; while (*s) s++; return s-s 0; } Il test “diverso da 0” è tautologico! … e poi così: int lunghezza(char *s) { char *s 0 = s; while (*s++); return s-s 0; } Il post-incremento può essere inglobato.

ESEMPIO Problema: Scrivere una procedura che copi una stringa in un’altra. Codifica: void strcpy(char

ESEMPIO Problema: Scrivere una procedura che copi una stringa in un’altra. Codifica: void strcpy(char dest[], char source[]) { while (*source) { *dest = *(source++); } *dest = ''; }

LIBRERIA SULLE STRINGHE Il C fornisce una nutrita libreria di funzioni per operare sulle

LIBRERIA SULLE STRINGHE Il C fornisce una nutrita libreria di funzioni per operare sulle stringhe: #include < string. h > Include funzioni per: • copiare una stringa in un’altra (strcpy) • concatenare due stringhe (strcat) • confrontare due stringhe (strcmp) • cercare un carattere in una stringa (strchr) • cercare una stringa in un’altra (strstr) • . . .

ARRAY MULTIDIMENSIONALI È anche possibile definire matrici e, più in generale, array a più

ARRAY MULTIDIMENSIONALI È anche possibile definire matrici e, più in generale, array a più dimensioni int matrice[N][M]; • N indica il numero di righe – numerate da 0 a N-1 • M indica il numero di colonne – numerate da 0 a M-1

ARRAY MULTIDIMENSIONALI Per selezionare la cella di indici i, j: x = matrice[i][j]; Attenzione:

ARRAY MULTIDIMENSIONALI Per selezionare la cella di indici i, j: x = matrice[i][j]; Attenzione: • matrice[i, j] ha un altro significato!! – l’espressione i, j denota un solo numero (j), non la coppia di indici necessaria! • matrice[k] denota l’intera riga k – non c’è modo di denotare un’intera colonna

ARRAY MULTIDIMENSIONALI Esempio (somma elementi di una matrice) main(){ float m[4][4] = { {1,

ARRAY MULTIDIMENSIONALI Esempio (somma elementi di una matrice) main(){ float m[4][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {4, 3, 2, 1}, {9, 8, 7, 6}}; float somma = 0; int i, j; for (i=0; i<4; i++) for (j=0; j<4; j++) somma += m[i][j]; }

ARRAY MULTIDIMENSIONALI E per passarli a una funzione? • il C passa sempre solo

ARRAY MULTIDIMENSIONALI E per passarli a una funzione? • il C passa sempre solo l’indirizzo iniziale • però, nella funzione occorre specificare tutte le dimensioni successive alla prima int f(int mm[4][4], int n, int m) {. . . } int f(int *mm[4], int n, int m) {. . . }

ARRAY MULTIDIMENSIONALI Perché occorre specificare le dimensioni successive alla prima? • perché un array

ARRAY MULTIDIMENSIONALI Perché occorre specificare le dimensioni successive alla prima? • perché un array multidimensionale è, di fatto, un array di array – è visto come un array di N “cose”, ciascuna delle quali, in effetti, è un altro array • per distinguere dove cominciano le diverse righe, bisogna sapere quanto sono grandi le singole colonne – bisogna perciò sapere quante sono le colonne

STRUTTURE Una struttura è una collezione finita di variabili non necessariamente dello stesso tipo,

STRUTTURE Una struttura è una collezione finita di variabili non necessariamente dello stesso tipo, ognuna identificata da un nome. struct persona nome età stringa di 20 char un intero stipendio un float

STRUTTURE Una struttura è una collezione finita di variabili non necessariamente dello stesso tipo,

STRUTTURE Una struttura è una collezione finita di variabili non necessariamente dello stesso tipo, ognuna identificata da un nome. Definizione di una variabile di tipo struttura: struct [<etichetta>] { { <definizione-di-variabile> } } <nome. Struttura> ;

STRUTTURE - ESEMPIO struct persona { char nome[20]; int eta; float stipendio; } pers

STRUTTURE - ESEMPIO struct persona { char nome[20]; int eta; float stipendio; } pers ; struct persona nome età stipendio Definisce una variabile pers strutturata nel modo illustrato. stringa di 20 char un intero un float

ESEMPI struct punto { int x, y; } p 1, p 2 ; p

ESEMPI struct punto { int x, y; } p 1, p 2 ; p 1 e p 2 sono fatte ciascuna da due interi di nome x e y struct data { int giorno, mese, anno; } d ; d è fatta da tre interi di nome giorno, mese e anno

STRUTTURE Una volta definita una variabile struttura, si accede ai singoli campi mediante la

STRUTTURE Una volta definita una variabile struttura, si accede ai singoli campi mediante la notazione puntata. Ad esempio: p 1. x = 10; p 1. y = 20; p 2. x = -1; p 2. y = 12; d. giorno = 25; d. mese = 12; d. anno = 1999; Ogni campo si comporta e si usa come una normale variabile.

UN ALTRO ESEMPIO main(){ struct frutto { char nome[20]; int } f 1; struct

UN ALTRO ESEMPIO main(){ struct frutto { char nome[20]; int } f 1; struct frutto f 2 ; peso; . . . } Non occorre ripetere l’elenco dei campi perché è implicito nell’etichetta frutto, che è già comparsa sopra.

ESEMPIO main(){ struct frutto { char nome[20]; int peso; } f 1 = {"mela",

ESEMPIO main(){ struct frutto { char nome[20]; int peso; } f 1 = {"mela", 70}; struct frutto f 2 = {"arancio", 50}; int peso = f 1. peso + f 2. peso; } Non c’è alcuna ambiguità perché ogni variabile di nome peso è definita nel proprio environment.

UNA PRECISAZIONE A differenza di quanto accade con gli array, il nome della struttura

UNA PRECISAZIONE A differenza di quanto accade con gli array, il nome della struttura rappresenta la struttura nel suo complesso. Quindi, è possibile: • assegnare una struttura a un’altra (f 2 = f 1) • che una funzione restituisca una struttura E soprattutto: • passare una struttura come parametro a una funzione significa passare una copia

ASSEGNAMENTO FRA STRUTTURE main(){ struct frutto { char nome[20]; int peso; } f 1

ASSEGNAMENTO FRA STRUTTURE main(){ struct frutto { char nome[20]; int peso; } f 1 = {"mela", 70}; struct frutto f 2 = {"arancio", 50}; f 1 = f 2; } Equivale a copiare f 2. peso in f 1. peso, e f 2. nome in f 1. nome.

STRUTTURE PASSATE COME PARAMETRI • Il nome della struttura rappresenta, come è naturale, la

STRUTTURE PASSATE COME PARAMETRI • Il nome della struttura rappresenta, come è naturale, la struttura nel suo complesso – niente scherzi come con agli array…!! • quindi, non ci sono problemi nel passarle a come parametro a una funzione: avviene il classico passaggio per valore • è perciò possibile anche restituire come risultato una struttura • tutti i campi vengono copiati, uno per uno!

ESEMPIO struct frutto macedonia( struct frutto f 1, struct frutto f 2){ struct frutto

ESEMPIO struct frutto macedonia( struct frutto f 1, struct frutto f 2){ struct frutto f; f. peso = f 1. peso + f 2. peso; strcpy(f. nome, "macedonia"); return f; } La funzione di libreria strcpy() copia la costante stringa “macedonia” in f. nome. Si crea una nuova struct frutto, la si inizializza e la si restituisce come risultato.

RIFLESSIONE Se una struttura, anche molto voluminosa, viene copiata elemento per elemento. . .

RIFLESSIONE Se una struttura, anche molto voluminosa, viene copiata elemento per elemento. . . perché non usare una struttura per incapsulare un array? In effetti: • il C non rifiuta di manipolare gli array come un tutt’uno “per principio”: è solo la conseguenza del modo in cui si interpreta il loro nome • quindi, “chiudendoli in una struttura” dovremmo riuscirci!

E INFATTI. . . main(){ struct string 20 { char s[20]; } s 1

E INFATTI. . . main(){ struct string 20 { char s[20]; } s 1 = {"Paolino Paperino" }, s 2 = {"Gastone Fortunato" }; s 1 = s 2; } /* FUNZIONA!! */

STRUTTURE CHE RACCHIUDONO ARRAY Usando una struttura per “racchiudere” un array, si fornisce all’array

STRUTTURE CHE RACCHIUDONO ARRAY Usando una struttura per “racchiudere” un array, si fornisce all’array esattamente quello che gli mancava: un modo per denotare “il tutto” ossia un “contenitore” dotato di nome, che consenta di riferirsi all’array nella sua globalità.

ARGOMENTI DALLA LINEA DI COMANDO Come si è detto più volte, il main è

ARGOMENTI DALLA LINEA DI COMANDO Come si è detto più volte, il main è una funzione come le altre: • ha un nome convenzionale, fissato • è la funzione invocata per far partire il programma. Ma… CHI LA INVOCA? Visto che è una funzione, HA DEI PARAMETRI?

ARGOMENTI DALLA LINEA DI COMANDO Il main è una funzione • invocata dal sistema

ARGOMENTI DALLA LINEA DI COMANDO Il main è una funzione • invocata dal sistema operativo • cui è passato un array di stringhe • che corrispondono agli argomenti scritti dall’utente sulla linea di comando Esempio di invocazione da linea di comando: C: > prog pippo 12 paperino 23 nome del programma I° argomento IV° argomento III° argomento

ARGOMENTI DALLA LINEA DI COMANDO Perciò, main ha due parametri: • un intero che

ARGOMENTI DALLA LINEA DI COMANDO Perciò, main ha due parametri: • un intero che rappresenta la lunghezza dell’array – int argc (argument counter) • l’array di stringhe vero e proprio (ovviamente, si passa il suo indirizzo iniziale) – char* argv[] (argument vector) Ogni elemento dell’array è un puntatore a carattere, che punta a uno degli argomenti della linea di comando.

ARGOMENTI DALLA LINEA DI COMANDO Quindi, l’interfaccia completa del main è la seguente: int

ARGOMENTI DALLA LINEA DI COMANDO Quindi, l’interfaccia completa del main è la seguente: int main(int argc, char* argv[]) Se non servono, argc e argv possono essere omessi, nel qual caso il main assume la forma semplificata già nota: int main() Valore di ritorno: può essere usato per restituire al Sistema Operativo un codice numerico di errore. Convenzione: 0 = OK, ogni altro valore = un tipo di errore

ARGOMENTI DALLA LINEA DI COMANDO • argv[0] è il nome del programma stesso •

ARGOMENTI DALLA LINEA DI COMANDO • argv[0] è il nome del programma stesso • da argv[1] ad argv[argc-1] vi sono gli argomenti passati, nell’ordine • argv[argc] è per convenzione NULL argv argc 5 "prog" "pippo" "12" "paperino" "23" (NULL) argv[0] argv[1] argv[2] argv[3] argv[4]

ARGOMENTI DALLA LINEA DI COMANDO • argv[0] è il nome del programma stesso •

ARGOMENTI DALLA LINEA DI COMANDO • argv[0] è il nome del programma stesso • Attenzione: da argv[1] ad argv[argc-1] vi sono aree del sistema gli argomenti passati, nell’ordine operativo disponibili solo in lettura • argv[argc] è per convenzione NULL argv argc 5 "prog" "pippo" "12" "paperino" "23" (NULL) argv[0] argv[1] argv[2] argv[3] argv[4]

ARGOMENTI DALLA LINEA DI COMANDO Problema: Come passare argomenti dalla linea di comando… quando

ARGOMENTI DALLA LINEA DI COMANDO Problema: Come passare argomenti dalla linea di comando… quando non c’è una linea di comando, come negli ambienti di sviluppo integrati (DJGPP / Turbo C) ? C: > prog pippo 12 paperino 23 Esiste un’apposita opzione da menù.

ARGOMENTI DALLA LINEA DI COMANDO In Turbo C: Options / Environment / Debugger

ARGOMENTI DALLA LINEA DI COMANDO In Turbo C: Options / Environment / Debugger

ARGOMENTI DALLA LINEA DI COMANDO In RHide: Run / Arguments. . .

ARGOMENTI DALLA LINEA DI COMANDO In RHide: Run / Arguments. . .

ESEMPIO 1 Problema: Scrivere un programma che analizzi gli argomenti passati dalla linea di

ESEMPIO 1 Problema: Scrivere un programma che analizzi gli argomenti passati dalla linea di comando, e restituisca il numero di lettere minuscole. Specifica: Per ogni argomento da 1 ad argc-1, occorre: • recuperare l’argomento (una stringa) • contare le minuscole presenti in tale stringa • sommare questo valore alla variabile che rappresenta il numero totale di minuscole. Alla fine si restituisce il valore di tale variabile.

ESEMPIO 1 Codifica int conta. Minuscole(char s[]); int main(int argc, char* argv[]) { int

ESEMPIO 1 Codifica int conta. Minuscole(char s[]); int main(int argc, char* argv[]) { int sum=0, i; for(i=1; i<argc; i++) sum += conta. Minuscole(argv[i]); return sum; }

ESEMPIO 1 Codifica #include <ctype. h> /* islower() */ int conta. Minuscole(char *s){ int

ESEMPIO 1 Codifica #include <ctype. h> /* islower() */ int conta. Minuscole(char *s){ int n=0; while (*s) if (islower(*s++)) n++; return n; }

ESEMPIO 2 Problema: Scrivere un programma che, dati un carattere e una stringa sulla

ESEMPIO 2 Problema: Scrivere un programma che, dati un carattere e una stringa sulla linea di comando, conti quante volte il carattere compare nella stringa (sia in versione maiuscola che minuscola), e restituisca questo valore come risultato del main. Specifica: • occorre in primis recuperare gli argomenti • poi, si scandisce la stringa carattere per carattere e si contano le occorrenza del carattere dato (facendo attenzione alle maiuscole e minuscole) Alla fine si restituisce il risultato di tale conteggio.

ESEMPIO 2 Codifica #include <ctype. h> int main(int argc, char* argv[]) { int cont=0;

ESEMPIO 2 Codifica #include <ctype. h> int main(int argc, char* argv[]) { int cont=0; char ch = toupper(argv[1][0]); char *s = argv[2]; while (*s) if (toupper(*s++)==ch) cont++; return cont; }