TIPI DI DATO
Un tipo di dato T è definito come:
 un dominio di valori, D
 un insieme di funzioni F1,..,Fn sul dominio D
 un insieme di predicati P1,..,Pm sul dominio D
T = { D, {F1,...,Fn}, {P1,...,Pm} }
TIPI DI DATO
I tipo di dato si differenziano in scalari e
strutturati.
t ipi di da t o
sca la r i
pr edefin it i
int
char
float
double
st r u t t u r a t i
defin it i
da ll'u t en t e
cost r u t t or i
pr edefin it i
enum
[]
struct
union
pu n t a t or i
defin it i
da ll'u t en t e
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,
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, 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 identificata da un
indice compreso fra 0 e N-1.
Definizione di una variabile di tipo array:
<tipo> <nomeArray> [ <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 valore massimo.
Specifica di I livello:
Inizialmente, si assuma come massimo di tentativo il
primo elemento.
Poi, si confronti via 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 valore m
massimo.
0 = v[0]  m0  v[0]
Specifica di I livello:
, v[i]) il
Inizialmente, si assuma comemmassimo
dii-1tentativo
i = max(m
 mi  v[0],v[1]..v[i]
primo elemento.
Poi, si confronti via 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-1tentativo
col valore massimo ospitato nel vettore.
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!
#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 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
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 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 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 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 rappresentano temperature in gradi Kelvin.
Calcolare la media delle
s0temperature
= 0, k0 = 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.
sN-1 = sN-2 + v[N-1],
Al termine (quando o un elemento vale
kN -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;
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 + ... a1 x + a0
Idea: riscriverlo così (Metodo di Horner):
(...( ( an x + an-1) x + an-2 ) x + ... + a1) x + a0
Invariante:
v0 = an
vi+1 = vi * x + an-1-i
per 0  i  n-1
VALUTAZIONE DI UN POLINOMIO
Esempio:
p3(x) = 3 x3 + 2 x2 + 6 x + 4
si riscrive come:
p3(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:
p3(x) = 3 x3 + 2 x2 + 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, 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 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 caratteri, perché
una cella è destinata al terminatore '\0'.
STRINGHE DI CARATTERI
Un array di N caratteri può ben essere usato
per memorizzare stringhe più corte
s
d
i
\0
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, elencando le singole
componenti:
char s[4] = {'a', 'p', 'e', '\0'};
oppure anche, più brevemente, con la forma
compatta seguente:
char s[4] = "ape" ;
Il carattere di terminazione ‘\0’ è automaticamente
incluso in fondo. Attenzione alla lunghezza!
ESEMPIO
Problema:
Data una stringa di caratteri, calcolarne la
lunghezza.
Ipotesi:
La stringa è “ben formata”, ossia correttamente
terminata dal carattere ‘\0’.
Specifica:
• scandire la stringa elemento per elemento, fino a
trovare il terminatore ‘\0’ (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 caratteri.
main() {
char s[] = "Nel mezzo del cammin di";
int lung=0;
for (lung=0; s[lung]!='\0'; lung++);
/* lung rappresenta il risultato */
}
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 ‘\0’.
Specifica:
• scandire la stringa elemento per elemento, fino a
trovare il terminatore ‘\0’ (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”
main() {
char s[] = "Nel mezzo del cammin di";
char s2[40];
int i=0;
for (i=0; s[i]!='\0'; i++)
s2[i] = s[i]; Al termine, occorre garantire
s2[i] = '\0';
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, con eventuale troncamento se
la stringa è più lunga dell’array dato.
Specifica:
• scandire la stringa elemento per elemento, o fino a
trovare il terminatore ‘\0’ (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
c’è spazio nell’array destinazione
#define N 20
main() {
char s[] = "Nel mezzo del cammin di";
char s2[N];
int i=0;
for (i=0; s[i]!='\0' && i<N-1; i++)
s2[i] = s[i];
s2[i] = '\0';
La condizione è i<N-1 perché
}
deve rimanere uno spazio per ‘\0’
ESEMPIO
Problema:
Date due stringhe di caratteri, decidere quale
precede l’altra in ordine alfabetico.
Rappresentazione dell’informazione:
• poiché vi possono essere tre risultati (s1<s2,
s1==s2, s2<s1), un boolean non basta
• possiamo usare:
– due boolean (uguale e precede)
– tre boolean (uguale, s1precedes2, s2precedes1)
– un intero (negativo, zero, positivo)
scegliamo la terza via.
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 s1[] = "Sempre caro mi fu quell’\
ermo colle";
char s2[] = "Sempre odiai quell’orrido\
colle";
int i, stato;
for (i=0; s1[i]!='\0' && s2[i]!='\0' &&
s1[i]==s2[i] ; i++);
stato = s1[i]-s2[i]; negativo  s1 precede s2
}
positivo  s2 precede s1
zero  s1 è uguale a s2
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 cammin di";
char s2[40];
s2 = 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 cammin di";
char s2[40];
s2 = s;
}
ERRORE DI COMPILAZIONE:
incompatible types in assignment !!
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
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é,
• 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 (s2 = 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 del cammin di";
char s2[40];
s2 = s;
}
Questo assegnamento viene interpretato
come il tentativo di cambiare l’indirizzo
iniziale della stringa s2, cosa evidentemente impossibile !!!
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 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 passa per riferimento!!
v
w



0
1
2
3
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 lunghezza.
Codifica:
int lunghezza(char s[]) {
int lung=0;
for (lung=0; s[lung]!='\0'; 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 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]!='\0'; lung++);
return lung;
}
Per il C è identico!!
… a così:
int lunghezza(char *s) {
int lung=0;
for (lung=0; s[lung]!='\0'; lung++);
return lung;
}
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 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]
...
*(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, 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
size
p
q
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 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 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 findMax(int v[], int dim);
ESEMPIO
Il cliente:
main() {
int max, v[] = {43,12,7,86};
max = findMax(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 findMax(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 findMax(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 findMax(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 lunghezza.
Codifica:
int lunghezza(char s[]) {
int lung=0;
for (lung=0; s[lung]!='\0'; lung++);
return lung;
}
Nel caso delle stringhe, la dimensione non serve
perché può essere dedotta dalla posizione dello ‘\0’
UNA VARIANTE
Da così...
int lunghezza(char *s) {
int lung=0;
for (lung=0; s[lung]!='\0'; lung++);
return lung;
}
Sfrutta il fatto che
… a così:
s è una copia dell’indirizzo iniziale
della stringa, ergo
si può modificare!
int lunghezza(char *s) {
char *s0 = s;
while (*s!='\0') s++;
return s-s0;
Sfrutta l’aritmetica dei puntatori
}
UNA VARIANTE “DA HACKER”
Uno hacker la compatterebbe prima così...
int lunghezza(char *s) {
char *s0 = s;
while (*s) s++;
return s-s0;
}
Il test “diverso da 0”
è tautologico!
… e poi così:
int lunghezza(char *s) {
char *s0 = s;
while (*s++);
return s-s0;
}
Il post-incremento può
essere inglobato.
ESEMPIO
Problema:
Scrivere una procedura che copi una stringa in
un’altra.
Codifica:
void strcpy(char dest[], char source[]) {
while (*source) { *dest = *(source++);}
*dest = '\0';
}
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ù 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:
• 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,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 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 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,
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,
ognuna identificata da un nome.
Definizione di una variabile di tipo struttura:
struct [<etichetta>] {
{ <definizione-di-variabile> }
}
<nomeStruttura> ;
STRUTTURE - ESEMPIO
struct persona {
char nome[20];
int eta;
float stipendio;
} pers ;
struct
persona
nome
età
Definisce una variabile
pers strutturata nel
modo illustrato.
stringa di
20 char
un intero
stipendio
un float
ESEMPI
struct punto {
int x, y;
} p1, p2 ;
p1 e p2 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
notazione puntata.
Ad esempio:
p1.x = 10;
p1.y = 20;
p2.x = -1;
p2.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
} f1;
struct frutto f2 ;
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;
} f1 = {"mela", 70};
struct frutto f2 = {"arancio", 50};
int peso = f1.peso + f2.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 rappresenta la struttura nel suo complesso.
Quindi, è possibile:
• assegnare una struttura a un’altra (f2 = f1)
• 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;
} f1 = {"mela", 70};
struct frutto f2 = {"arancio", 50};
f1 = f2;
}
Equivale a copiare f2.peso in f1.peso,
e f2.nome in f1.nome.
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 f1, struct frutto f2){
struct frutto f;
f.peso = f1.peso + f2.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...
.. 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 string20 {
char s[20];
} s1 = {"Paolino Paperino" },
s2 = {"Gastone Fortunato" };
s1 = s2;
}
/* FUNZIONA!! */
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 è 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 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
II° argomento
IV° argomento
III° argomento
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 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
• 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
• Attenzione:
da argv[1]
ad argv[argc-1] vi sono
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 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 RHide: Run / Arguments...
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 contaMinuscole(char s[]);
int main(int argc, char* argv[]) {
int sum=0, i;
for(i=1; i<argc; i++)
sum += contaMinuscole(argv[i]);
return sum;
}
ESEMPIO 1
Codifica
#include <ctype.h>
/* islower() */
int contaMinuscole(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 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;
char ch = toupper(argv[1][0]);
char *s = argv[2];
while (*s)
if (toupper(*s++)==ch) cont++;
return cont;
}
Scarica

17-Array e stringhe