Sei sulla pagina 1di 14

2

Puntatore
 E’ una variabile che contiene l’indirizzo di
memoria di un oggetto (variabile o costante)
I puntatori  Esempio
p è un puntatore e “punta” A55 12 x
alla variabile x che in
A54 ...
questo esempio ha indirizzo
Ver. 2.4 di memoria A55) A53 ...
int x=12; x:A55 A52 ...
12 A51 ...
p
A55 A50 A55 p
© 2010 - Claudio Fornaro - Corso di programmazione in C

3 4

Definizione Assegnazione
 Sintassi  Ad un puntatore si possono assegnare solo
tipo *nomeVariabile; indirizzi di memoria o il valore NULL che indica
che il puntatore non punta a nulla
 Esempi
int *punt;  La costante 0 in un contesto dove è atteso un
char x, *p, *q, y; puntatore (inizializzazione e assegnamento di
un puntatore, confronto con un puntatore)
x e y sono variabili di tipo char viene convertita in NULL dal compilatore
p e q sono variabili di tipo puntatore-a-char  L’operatore di indirizzo ‘&’ (“ampersand”)
calcola l’indirizzo di memoria di una variabile
(si dice che ne restituisce il puntatore)
int *p = &x; inizializza p con l’indirizzo di x
p = &y; assegna a p l’indirizzo di y
5 6

Utilizzo Utilizzo
 Si accede all’oggetto puntato da un puntatore  Sia p sia *p sono L-value modificabili, ossia
per mezzo dell’operatore ‘*’ detto sono un “qualcosa” (variabile, elemento di
operatore di deriferimento (“dereference”) vettore, …) a cui si può assegnare un valore
o di indirezione (“indirection”)  L’operatore ‘*’ ha priorità superiore a quella
int x=12, *p = &x;  p “punta a” x degli operatori matematici
*p = 24;  x ora vale 24 x = 6 * *p; equivale a: x = 6 * (*p);
*p += 6;  x ora vale 30  Per visualizzare il valore di un puntatore si
 p è il puntatore può utilizzare la direttiva %p in una printf
*p è l’oggetto puntato (qui è x)
 Si noti che la sintassi per indicare l’oggetto
puntato e quella della definizione del puntatore
sono identiche: *p

7 8

Tipi e puntatori Puntatori a void


 L’informazione relativa al tipo è necessaria per  Sono puntatori generici e non possono essere
permettere ai puntatori di conoscere la dereferenziati (non si può scrivere *p),
dimensione dell’oggetto puntato (usata possono essere utilizzati solo come contenitori
nell’aritmetica dei puntatori)
temporanei di valori di tipo puntatore (a
 Poiché oggetti di tipo diverso possono avere qualsiasi tipo)
dimensione diversa, l’assegnazione tra void *h;
puntatori di tipo diverso è in genere errata e il
compilatore genera un warning  Non serve il cast (void *) per copiare un
int *p, x=12; puntatore non-void in un puntatore void
long *q, y=26; h = p; (supponendo ad esempio int *p)
p = &x;  OK!
 Qualsiasi tipo di puntatore può essere
q = &y;  OK!
q = p;  NO! Warning confrontato con un puntatore a void
q = &x;  NO! Warning  NULL è definito come: (void *)0
9 10

Puntatori a void Puntatori e vettori


 Per dereferenziare il valore di un puntatore a  Il nome (senza parentesi) di un vettore-di-T
void è necessario assegnarlo ad un puntatore è un valore costante di tipo puntatore-a-T,
al tipo appropriato (non void) per poter corrisponde all’indirizzo di memoria del primo
conoscere la dimensione dell’oggetto puntato elemento di vettore
 Può essere necessario il cast (tipo *) per int vett[100];
copiare un puntatore void in un puntatore int *p;
non-void (i compilatori C non lo richiedono, i p = vett;
compilatori C++ sì). l’indirizzo di memoria di vett viene messo in
In riferimento all’esempio precedente: p, equivale a scrivere: p = &vett[0] (le
int *q; parentesi hanno priorità maggiore di &)
q = h;  OK, compilatore C
q = (int *)h;  OK, compilatore C++
*q = 23;  x ora contiene 23

11 12

Puntatori e vettori Equivalenza puntatori e vettori


 Attenzione:  Una variabile di tipo puntatore-a-T, assegnata
vett = p;  NO! in modo che punti a (cioè contenga l’indirizzo
Non si può assegnare un valore a vett in di) un oggetto di tipo vettore-di-T , può essere
quanto NON è una variabile puntatore, ma un utilizzata come se fosse un vettore-di-T
int vett[25];
“sinonimo” di un indirizzo di memoria int *p = vett;
Gli indirizzi di memoria sono valori costanti vett:
stabiliti dal compilatore, non sono variabili e p
quindi non hanno uno spazio in memoria
modificabile per contenere un valore Ad esempio, qui p[3] equivale a vett[3]
Il termine “puntatore” viene comunemente (e  Il compilatore internamente trasforma le
impropriamente) usato al posto di “indirizzo di espressioni con notazione vettoriale [] in
memoria” (es. “&a dà il puntatore ad a”) espressioni con i puntatori
13 14

Aritmetica dei puntatori Aritmetica dei puntatori


 Quando un puntatore punta ad un vettore,  L’istruzione p++ porta p a puntare a vett[1]
gli può essere sommato un valore intero N, il (ne contiene l’indirizzo) quindi ora p punta a
risultato è l’indirizzo di memoria dell’elemento vett[1] e p[3] corrisponde a vett[4]
di posizione N del vettore
vett: vett:
32 15 23 55 32 11 27 32 15 23 55 32 11 27

p p
p+3 p+3
p punta a (contiene l'indirizzo di) vett  E’ lecito sottrarre un valore N ad un puntatore
p+3 punta a (produce l'indir. di) vett[3] se l’elemento puntato risultante fa ancora parte
*(p+3) equivale a vett[3] del vettore (nell’esempio precedente p-1
punta a vett[0])

15 16

Equivalenza puntatori e vettori Equivalenza puntatori e vettori


 Il nome di un vettore può essere utilizzato  Senza il decadimento, vett+1 punterebbe al
come puntatore costante : primo byte dopo la fine di vett
*(vett+2) equivale a: vett[2]  Eccezioni:
vett++ è un errore (vett è costante!)  vett in un sizeof (descritto altrove) non decade
 Il nome di un vettore di T (es. int) che e per questo dà la dimensione dell’intero vettore,
dovrebbe essere una costante di tipo non quella del primo elemento
puntatore-a-vettore-di-T (o meglio  &vett inibisce il decadimento e produce una
quantità costante di tipo puntatore-a-vettore-di-int
indirizzo-di-vettore-di-T ), in C “decade” al
tipo puntatore-a-T
 Grazie al decadimento vett è visto come
puntatore (costante) a int e punta al suo
primo elemento e vett+1 punta a vett[1]
17 18

Equivalenza puntatori e vettori Aritmetica dei puntatori


 Una variabile di tipo puntatore-a-T non “sa”  Attenzione che il puntatore non “sfori” i limiti
se il valore (scalare) a cui punta è singolo o è del vettore (lo standard non richiede che il
l’elemento di un vettore, lo sa solo il compilatore faccia controlli, molti compilatori
programmatore e sta a questi utilizzarlo in lo offrono opzionalmente, ma ciò riduce le
modo coerente prestazioni)
int x = 10, vett[10], *p, *q;  E’ lecito che un puntatore punti a quello che
p = &x; sarebbe l’elemento del vettore successivo
p++; NO! Non esiste l’oggetto puntato da p+1 all’ultimo, ma questo puntatore può essere
q = p+1; NO! Idem utilizzato solo per calcolare la differenza tra
p = vett; puntatori (vedere prossima slide)
p++; SI’! Ora p punta a vett[1]
q = p+1; SI’! Ora q punta a vett[2]

19 20

Aritmetica dei puntatori Priorità dell’operatore *


 Due puntatori a elementi dello stesso vettore  Dalla tabella delle priorità si vede che
possono essere sottratti, il valore ottenuto + l’operatore di deriferimento * ha priorità quasi
1 è il numero di elementi del vettore compresi massima, inferiore solo alle parentesi (e a ‘->’
tra quelli puntati dai due puntatori (inclusi): e a ‘.’) e associatività da destra a sinistra
p = &vett[4];  Quindi, considerando che gli operatori * e ++
q = &vett[10]; hanno stessa priorità e associatività da D a S:
d = q-p+1;  7: numero degli elementi *p++ equivale a *(p++)  incrementa p
dalla posizione 4 alla *++p equivale a *(++p)  incrementa p
posizione 10 inclusi ++*p equivale a ++(*p)  incrementa *p
 Due puntatori possono essere confrontati inoltre:
solo se fanno parte dello stesso vettore oppure (*p)++  incrementa *p
uno dei due è NULL (o 0) *p+1 equivale a (*p)+1 e non a *(p+1)
Copia di stringhe 21
Copia di stringhe 22

1a versione 2a versione
 La stringa y viene copiata in x  La stringa y viene copiata in x
char x[30], y[30], *t=x, *s=y; char x[30], y[30], *t=x, *s=y;
int i=0; int i=0;
gets(y);
while (s[i] != '\0') gets(y);
{ while ((t[i] = s[i]) != '\0')
t[i] = s[i]; i++;
i++; printf("%s\n", x);
}  Il '\0' viene copiato nel ciclo stesso
t[i] = '\0';
printf("%s\n", x);  Qui s e t vengono inutilmente usati come
 Il '\0' viene copiato fuori dal ciclo semplici sinonimi di x e y, non come puntatori
 Qui s e t vengono inutilmente usati come  Nota: non si può scrivere t[i] = s[i++]
semplici sinonimi di x e y, non come puntatori nella condizione del while (side effect)

Copia di stringhe 23
Copia di stringhe 24

3a versione 4a versione
 La stringa y viene copiata in x  La stringa y viene copiata in x
char x[30], y[30], *t=x, *s=y; char x[30], y[30], *t=x, *s=y;
gets(y); gets(y);
while ( (*t = *s) != '\0') while (*t++ = *s++)
{ ;
t++; printf("%s\n", x);
s++;
 Il '\0' viene copiato nel ciclo stesso
}
printf("%s\n", x);  Nota: l‘istruzione nulla è più chiara se scritta
in una riga a sé stante
 Il '\0' viene copiato nel ciclo stesso
 Nota: != '\0' può essere omesso
25 26

Puntatori e stringhe Puntatori e stringhe


 Si noti la differenza tra le seguenti definizioni:  Si considerino i seguenti esempi:
 char str[100];  char str[] = "ciao";
RISERVA spazio per contenere i caratteri, è una E’ l’inizializzazione di una variabile stringa:
variabile e il suo contenuto può essere modificato il compilatore riserva memoria per str e vi copia i
 char *s; caratteri di "ciao"; la stringa costante "ciao"
non esiste in memoria: è stata usata dal
NON RISERVA spazio per contenere i caratteri, compilatore per inizializzare la stringa str, ma
quindi per essere utilizzata come stringa le si deve esiste in memoria la stringa variabile "ciao"
assegnare una stringa “vera”: str[0]='m';  SI’
 Assegnazione di una stringa variabile:  char *s = "hello";
s = str;
E’ l’inizializzazione di una variabile puntatore:
scanf("%s", s);  SI’
il compilatore determina l’indirizzo della stringa
 Assegnazione di una stringa costante: costante "hello" (che esiste in memoria) e lo
s = "salve"; assegna alla variabile puntatore s
scanf("%s", s);  NO s[0]='b';  NO! "hello" è costante!

27 28

Puntatori e stringhe Puntatori e stringhe


 Si considerino i seguenti esempi (cont.):  Noti i puntatori, si possono completare le
 s = "salve"; informazioni già date sulle funzioni relative
E’ l’assegnazione ad una variabile puntatore: il alle stringhe aggiungendo quanto segue:
compilatore determina l’indirizzo della stringa  le funzioni di copia di stringhe strcpy, strncpy,
costante "salve" (che esiste in memoria) e lo strcat e strncat restituiscono il puntatore alla
assegna alla variabile puntatore s stringa di destinazione
s[4]='o';  NO! "salve" è costante!
 le funzioni di parsing strchr, strrchr, strstr,
 s = str; strpbrk e strtok restituiscono il puntatore
E’ l’assegnazione ad una variabile puntatore: il all’oggetto cercato o NULL se non lo trovano
compilatore determina l’indirizzo della stringa
variabile str (che esiste in memoria) e lo assegna
alla variabile puntatore s
s[0]='m';  SI’
29 30

Funzioni sui blocchi di byte Funzioni sui blocchi di byte


 Blocchi di byte: sequenze di byte (caratteri)  memcpy(t,s,n)
qualsiasi, il carattere di codice ASCII 0 ('\0') copia n byte da s a t
è un carattere normale e non viene utilizzato  memmove(t,s,n)
come terminatore (non c’è alcun terminatore) copia n byte da s a t
 Una porzione di memoria (allocata in qualsiasi (i blocchi possono anche essere sovrapposti)
 memcmp(s,t,n)
modo) viene trattata come generico blocco di
byte da alcune funzioni contenute in confronta byte per byte secondo il codice
<string.h> ASCII i primi n byte di s e di t, il valore
restituito è un int come quello della strcmp
 Per riservare una porzione di memoria si può  memchr(s,c,n)
definire una variabile o una stringa o utilizzare cerca il byte c tra i primi n byte di s, dà NULL
la funzione malloc (trattata in altre slide) se non lo trova o il puntatore ad esso se trova
 Nelle seguenti funzioni, s (source) e  memset(s,c,n)
t (target ) sono puntatori a blocchi di byte copia il byte c in tutti i primi n byte di s

Puntatori const 31
Puntatori const 32

Puntatore variabile a valore costante Puntatore variabile a valore costante


L’assegnazione di un valore di tipo puntatore-
}
 int const *p; 
sono equivalenti
const int *p; a-costante (l’indirizzo di un valore costante) ad
p è una variabile di tipo puntatore-a-costante una variabile di tipo puntatore-a-variabile
(a un oggetto costante di tipo int) genera un Warning del compilatore perché
const int x, y; permette di by-passare la restrizione (const)
const int *p;  puntatore-a-costante const int x = 12;
p = &x;  SI’, p è una variabile int y = 10;
p = &y;  SI’, p è una variabile int *p;  puntatore-a-variabile
*p = 13;  NO, *p è costante const int *q;  puntatore-a-costante
p = &x;  Warning
*p = 5;  non dà errore
q = &x;  OK
*p = 5;  dà errore
Puntatori const 33 34

Puntatore costante a valore variabile Esercizi


 int * const p; 1. Si scriva un programma che chieda una
p è una costante di tipo puntatore-a-variabile stringa all’utente e conti quanti siano i
(a un oggetto variabile di tipo int) caratteri che la costituiscono, NON si usi la
 Le costanti possono essere solo inizializzate funzione strlen della libreria standard.
int x, y; 2. Scrivere un programma che verifichi se la
int * const p = &x;  inizializzazione stringa data in input è palindroma o no
*p = 13;  SI’, *p è una variabile 3. Scrivere un programma che chieda n valori
p = &y;  NO, p è una costante interi (massimo 100), li collochi in un vettore e
inverta il vettore (scambiando il primo
elemento con l'ultimo, il secondo con il
penultimo, etc.). Si usi la notazione vettoriale.
4. Come il precedente, ma si usino i puntatori.

35 36

Esercizi Esercizi
5. Scrivere un programma che data una stringa 8. Scrivere un programma che chieda N valori
in input dica se la stessa contiene almeno una (massimo 100), li collochi in un vettore e li
‘A’ tra i primi 10 caratteri. ordini in senso crescente (senza usare vettori
6. Si scriva un programma che chieda all’utente ausiliari).
2 stringhe e concateni la seconda alla fine 9. Scrivere un programma che verifichi se la
della prima, NON si usi la funzione strcat stringa data è composta di due parti uguali,
della libreria standard, si usino i puntatori e trascurando il carattere centrale se la
non la notazione vettoriale. Attenzione a lunghezza è dispari (es. “CiaoCiao”,
terminarla con il carattere ‘\0’. “CiaoXCiao”).
7. Si scriva un programma che chieda all’utente 10. Scrivere un programma che date 2 stringhe in
2 stringhe e indichi se la seconda è uguale input indichi quante volte la più corta è
alla parte terminale della prima. contenuta nella più lunga.
37 38

Esercizi Vettori di puntatori


11. Scrivere un programma che legga da un file  Gli elementi di questi vettori sono puntatori
un testo e spezzi su più righe quelle più int *vett[10];
lunghe di N caratteri (N chiesto all’utente). Le definisce un vettore di 10 puntatori a int (le
righe si possono spezzare solo dove c’è uno [ ] hanno priorità maggiore dell’operatore *)
spazio (che non va riportato nella riga  Esempio di inizializzazione
int a, b, c;
successiva). L’output deve essere salvato in int *vett[10]={NULL};
un altro file. vett[0]=&a;
vett[1]=&b;
vett[2]=&c;
I valori da vett[3] a vett[9] sono tutti
NULL in quanto è stato inizializzato il primo
elemento (i successivi sono automaticamente
a 0 e 0 viene convertito in NULL)

39 40

Vettori di puntatori Puntatori a puntatori


 Esempio di inizializzazione errata  Variabili che contengono l'indirizzo di memoria
int a, b, c; di un puntatore
int *vett[10]={&a, &b, &c};  Esempio
E’ errato perché questi inizializzatori sono int a, *b, **c;
indirizzi di variabili automatiche (descritto in a = 12;
b = &a;
altre slide). Se le variabili fossero invece di
c = &b;
classe static sarebbe stato corretto, per Il puntatore c punta ad una variabile (b) che
queste variabili i valori di vett si devono punta ad un int (a) a:B4A
assegnare come visto nell’esempio precedente 12
b:A5B
B4A
c:5D6
A5B
41 42

Puntatori a puntatori Puntatori e matrici


 Esempio  Una matrice è un vettore di vettori e quindi,
int a=10, b=20, c=30; considerando che l’associatività di [] è da
int *v[3], int **w; sinistra a destra, si ha che:
v[0]=&a; v[1]=&b; v[2]=&c; int Mx[7][5];
w = v; definisce un vettore di 7 elementi
ciascuno delle quali è un vettore di 5 int
w++;
v è un vettore di puntatori, cioè è l'indirizzo di  Gli elementi del vettore Mx sono i 7 vettori
memoria (“puntatore”) di un puntatore, quindi identificati da Mx[i]; quindi Mx[i] è l’indirizzo
v+1 è l'indirizzo del secondo puntatore del di memoria di ciascuno dei 7 vettori di 5 int
vettore v (ossia è pari a &v[1], punta a b)  Poiché in seguito al decadimento il nome di un
Anche w è un puntatore ad un puntatore, vettore-di-T ha tipo puntatore-a-T, anche gli
quando viene incrementato, punta al Mx[i] non sono di tipo vettore-di-int ma
puntatore successivo, come v decadono al tipo puntatore-a-int, (essendo
indirizzi, non si può assegnare loro un valore)

43 44

Puntatori e matrici Puntatori e matrici


 Il “decadimento” del tipo del nome di un  Nella definizione di puntatore seguente
vettore a puntatore può avvenire 1 sola volta int (*p)[5]
 Allora il tipo di una matrice decade a è necessario che la dimensione delle colonne
puntatore-a-vettore-di-T e non a puntatore-a- (5) sia specificata perché definisce un
T (quindi incrementando questo puntatore si puntatore-a-vettore-di-cinque-interi
punta al vettore di 5 int successivo)
 Poiché Mx è un puntatore-a-vettore-di-5-interi
 Mx non è di tipo puntatore-a-int come:
int *p (e non un puntatore ad un intero), allora:
Mx+1 punta all’elemento successivo, ossia al
 Mx non è di tipo puntatore-a-puntatore-a-int:
int **p successivo vettore di 5 interi (la seconda riga
della matrice): Mx+1 aggiunge all’indirizzo di
 Mx è di tipo puntatore-a-vettore-di-5-int
Mx il numero corretto di byte per puntare al
come: int (*p)[5]
quindi Mx+1 punta al secondo vettore di 5 int secondo vettore di 5 int
45 46

Puntatori e matrici Puntatori e matrici


 Ricapitolando:  Ricapitolando (continuazione):
 Mx[1][2]  Mx
 è un valore scalare  è l’indirizzo di un vettore di 7 vettori di 5 int
 è di tipo int
 è di tipo puntatore-a-vettore-di-int (“decade
 è modificabile
una volta sola”), più precisamente è un
 Mx[1][2]+1 somma 1 al contenuto di Mx[1][2]
puntatore-a-vettore-di-5-int (senza il 5 nel tipo
 Mx[1] il compilatore non saprebbe di quanti byte
 è l’indirizzo di un vettore di 5 int spostarsi per puntare al vettore-di-5-int
 è di tipo puntatore-a-int (“decade”) successivo quando si scrive Mx+1)
 non è modificabile  non è modificabile
 Mx[1]+1 punta a Mx[1][1]  Mx+1 è l’indirizzo di memoria di Mx[1]

47 48

Puntatori e matrici Vettori di stringhe


 Si notino le differenze tra:  Essendo una stringa un vettore di caratteri,
int *p; puntatore-a-int un vettore di stringhe è in realtà un vettore di
int (*q)[5]; puntatore-a-vett-di-5-int vettori di caratteri, cioè una matrice di char
 Poiché Mx è un puntatore-a-vettore-di-5-int: char str[4][20]={"uno", "due"};
 p = Mx; definisce un vettore di 4 stringhe di 20 char
è errata Le 4 stringhe sono identificate da str[i]
 q = Mx;
è corretta e q+1 è l’indirizzo del secondo vettore di str:
5 elementi (equivale a Mx[1]) str[0] u n o \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
 p = &Mx[0][0]; str[1] d u e \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
è corretta e p è l’indirizzo del primo elemento dei str[2]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
Mx[0] str[3]\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
49 50

Vettori di puntatori e matrici Vettori di puntatori e matrici


 Si notino le differenze tra:  a[2] = "hello";
char a[4][8]={"un","otto","sei"}; ERRORE: a[2]non è un puntatore
char *b[4]={"un","otto","sei"}; strcpy(a[2], "hello");
CORRETTO
a: b:  b[2] = "hello";
un\0
un\000000 CORRETTO: b[2] è una variabile puntatore a cui
otto\0000 otto\0 viene assegnato l’indirizzo di memoria di una stringa
sei\00000
sei\0 costante
\00000000 NULL strcpy(b[2], "hello");
ERRORE: b[2] punta a una stringa costante
 a[i]è l’indirizzo costante della riga i di a, tale
 Entrambi a[1][0] e b[1][0] sono il carattere 'c'
riga è una stringa variabile di 8 char  a[1][0]='m';  SI’! L’oggetto puntato da a[1] è una
 b[i] è una variabile puntatore con l’indirizzo stringa variabile
b[1][0]='m';  NO! L’oggetto puntato da b[1] è una
della riga i, stringa costante di 4 caratteri: ad

stringa costante
esempio b[2] contiene l’indirizzo di "sei\0"

51 52

Const per puntatori a puntatori Const per puntatori a puntatori


1. int** x; 4. int * const * x
x è una variabile di tipo puntatore x è una variabile di tipo puntatore
a un puntatore variabile a un puntatore costante
a un oggetto variabile di tipo int a un oggetto variabile di tipo int
2. const int** x; 5. const int * const * x
x è una variabile di tipo puntatore x è una variabile di tipo puntatore
a un puntatore variabile a un puntatore costante
a un oggetto costante di tipo int a un oggetto costante di tipo int
3. int ** const x; 6. const int * const * const x
x è una costante di tipo puntatore x è una costante di tipo puntatore
a un puntatore variabile a un puntatore costante
a un oggetto variabile di tipo int a un oggetto costante di tipo int
53 54

Homework 7 Homework 7 (esempio)


Scrivere un programma che legga da un file al prima
massimo un certo numero MAXRIGHE di righe di Nel mezzo del cammin di nostra vita
testo e le memorizzi in una matrice di caratteri mi ritrovai per una selva oscura
(MAXRIGHE x 100), una riga del file per
ché la diritta via era smarrita.
ciascuna riga della matrice.
Si definisca un vettore di puntatori a carattere e Ahi quanto a dir qual era è cosa dura
esta selva selvaggia e aspra e forte
lo si inizializzi in modo che il primo puntatore
punti alla prima riga, il secondo alla seconda, dopo
ecc. Si ordinino le stringhe scambiando tra di Nel mezzo del cammin di nostra vita
loro solo i puntatori e le si visualizzino ordinate. mi ritrovai per una selva oscura
Facoltativo: misurare il rapporto tra il tempo ché la diritta via era smarrita.
necessario per ordinare scambiando le stringhe Ahi quanto a dir qual era è cosa dura
e scambiando i puntatori. esta selva selvaggia e aspra e forte

Potrebbero piacerti anche