Lezione 5

Transcript

Lezione 5
Introduzione al C
I puntatori
I puntatori
•I puntatori rappresentano uno degli aspetti più importanti e
problematici del C.
•La potenza del C è legata in larga misura ai puntatori.
• Consentono l’allocazione dinamica della memoria e
costituiscono un mezzo tramite il quale una funzione può
modificare il contenuto di un argomento.
Memoria
vint
1000
1001
12252
vchar
1002
1003
vfloat
1004
1005
1006
90
1007
1008
1009
1010
1200.156004
int vint = 12252;
char vchar = 90;
float vfloat = 1200.156004;
Quando una variabile viene dichiarata, il compilatore alloca
spazio in memoria per contenere i valori della variabile.
Dichiarazione e inizializzazione dei
puntatori
• Variabili puntatore
– Contengono come valore degli indirizzi di memoria
– Di norma, una variabile contiene direttamente un valore specifico
(riferimento diretto)
count
7
– Un puntatore contiene invece l’indirizzo di una variabile che
contiene il valore specificato (riferimento indiretto)
• Se per esempio la variabile p_vint contiene l’indirizzo della
variabile vint allora si dice che p_vint “punta a” vint
countPtr
count
7
Dichiarazione di puntatori
La forma generale di dichiarazione di un puntatore è:
tipo *p_nomep_nome-variabile;
variabile;
dove
•tipo indica il tipo di base del puntatore e deve essere un
tipo C valido
• p_nome-variabile è il nome della variabile puntatore.
Per esempio per dichiarare p come puntatore ad un intero si
utilizza questa dichiarazione:
int *p;
Il tipo di base di un puntatore determina il tipo di dato a
cui punterà.
Inizializzazione di puntatori
Dopo aver dichiarato un puntatore, esso deve essere
inizializzato, altrimenti non può essere usato
– I puntatori possono essere inizializzati con 0,
NULL, o con un indirizzo
− 0 o NULL - il puntatore non farà riferimento a
nessun dato (NULL è preferibile)
File di
intestazione
<stddef.h>
Operatori sui puntatori
• & (operatore di indirizzo)
– Restituisce l’indirizzo dell’operando
int y = 5;
int *yPtr;
yPtr = &y; //assegna a yPtr
l’indirizzo di y
– yPtr “punta a” y
yPtr
y
5
yptr
500000
600000
y
600000
Il valore di yptr
è l’indirizzo di y
5
…continua
• * (operatore di deriferimento / rinvio / risoluzione del
riferimento)
– Restituisce il valore dell’oggetto puntato dal suo
operando
*yptr restituisce il valore di y (yptr punta a y)
– * può essere usato nelle frasi di assegnazione
*yptr = 7; // assegna 7 a y
vint
p_vint
12252
1002
1003
1004
1005
1006
90
int *p_vint;
char *p_vchar;
float *p_vfloat;
1007
1008
1009
1010
1200.156004
p_vfloat
1001
vfloat
p_vchar
1000
vchar
*p_vint vale 12252;
*p_vchar vale 90;
*p_vfloat vale 1200.156004;
p_vint = &vint;
&vint;
p_vchar = &vchar;
&vchar;
p_vfloat = &vfloat;
&vfloat;
…continua
• * e & sono l’uno il complemento dell’altro
*&yptr -> * (&yptr) -> * (address of
yptr)-> restituisce il valore a cui punta l’operando,
ossia restituisce yptr
&*yptr -> &(*yptr) -> &(y) -> restituisce
l’indirizzo di y, che è yptr
Utilizzo delle variabili puntatore
int i = 20;
int *p;
int *p2;
…
p = &i;
…
p2 = p;
…
*p2 = 20;
• In questo caso *p2 = 20 è tecnicamente
equivalente a *p = 20 che a i = 20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
Using the & and * operators */
#include <stdio.h>
int main()
{
int a;
int *aPtr;
a = 7;
aPtr = &a;
/* a is an integer */
/* aPtr is a pointer to an integer */
/* aPtr set to address of a */
printf( "The address of a is %p"
"\nThe value of aPtr is %p", &a, aPtr );
printf( "\n\nThe value of a is %d"
"\nThe value of *aPtr is %d", a, *aPtr );
printf( "\n\nShowing that * and & are inverses of "
"each other.\n&*aPtr = %p"
"\n*&aPtr = %p\n", &*aPtr, *&aPtr );
return 0;
}
Esecuzione
Esecuzione
The
The address
address of
of aa is
is 0012FF88
0012FF88
The
value
of
aPtr
is
The value of aPtr is 0012FF88
0012FF88
The
The value
value of
of aa is
is 77
The
The value
value of
of *aPtr
*aPtr is
is 77
Proving
Proving that
that ** and
and && are
are complements
complements of
of each
each other.
other.
&*aPtr
=
0012FF88
&*aPtr = 0012FF88
*&aPtr
*&aPtr == 0012FF88
0012FF88
Chiamata per riferimento delle
funzioni
• Chiamate per riferimento con puntatori come argomenti
– Passare l’indirizzo dell’argomento usando l’operatore &
– Ciò permette di modificare il contenuto delle locazioni in memoria
– I vettori non sono passati con & perchè il nome del vettore è già un
puntatore
• operatore *
– Usato come alias/nickname per la variabile all’interno della
funzione
void raddoppia(int *number)
{
*number = 2 * (*number);
}
*number è usato come nickname per la variabile passata
1
/*
2
Cube a variable using call-by-reference
3
with a pointer argument */
4
5
#include <stdio.h>
6
7
void cubeByReference( int * );
/* prototype */
8
9
è passato l’indirizzo di number cubeByReference aspetta un
puntatore (l’indirizzo di una
variabile).
int main()
10 {
11
int number = 5;
12
13
printf( "The original value of number is %d", number );
14
cubeByReference( &number );
15
printf( "\nThe new value of number is %d\n", number );
16
17
All’interno di
cubeByReference, è usato
*nPtr (*nPtr è number).
return 0;
18 }
19
20 void cubeByReference( int *nPtr )
21 {
22
*nPtr = *nPtr * *nPtr * *nPtr;
/* cube number in main */
23 }
The
The original
original value
value of
of number
number is
is 55
The
The new
new value
value of
of number
number is
is 125
125
Utilizzare il qualificatore const con i
puntatori
• qualificatore const - la variabile non può essere modificata
– Utile usare const se la funzione non ha bisogno di modificare una
variabile
– Cercare di modificare una
const genera un errore del compilatore
• puntatori const - puntano sempre alla stessa locazione di
memoria
– Devono essere inizializzati al momento della dichiarazione
int *const myPtr = &x;
• Tipo int *const - puntatore costante ad un dato int
const int *myPtr = &x;
• Puntatore ad un dato const int
const int *const Ptr = &x;
• puntatore const ad un dato const int
1
/* fig07_13.c
2
Attempting to modify a constant pointer to
3
non-constant data */
4
5
#include <stdio.h>
6
7
int main()
8
{
9
int x, y;
10
11
è possibile cambiare *ptr - x
non è una costante.
int * const ptr = &x; /* ptr is a constant pointer to an
12
integer. An integer can be modified
13
through ptr, but ptr always points
14
to the same memory location. */
15
*ptr = 7;
16
ptr = &y;
17
18
Modificare ptr è un errore ptr è un puntatore costante.
return 0;
19 }
FIG07_13.c:
FIG07_13.c:
Error
Error E2024
E2024 FIG07_13.c
FIG07_13.c 16:
16: Cannot
Cannot modify
modify aa const
const object
object in
in
function
main
function main
***
*** 11 errors
errors in
in Compile
Compile ***
***
Espressioni con puntatori e
l’aritmetica dei puntatori
• Le operazioni aritmetiche possono essere eseguite anche
con puntatori
– Incremento/decremento di un puntatore (++ o --)
– Aggiungere un intero ad un puntatore ( + o += , - o -=)
– I puntatori possono essere sottratti uno dall’altro
– Operazioni non significative, a meno che non siano
eseguite su vettori
…continua
• Vettore int di 5 elementi su una macchina in cui gli interi sono
rappresentati in 4 byte
– vPtr punta al primo elemento, v[0] alla locazione 3000,
(vPtr = 3000)
– vPtr +=2; pone vPtr a 3008
• vPtr punta a v[2] (incrementato di 2, ma ogni intero occupa
4 byte)
locazione
3000
3004
v[0]
v[1]
variabile puntatore vPtr
3008
v[2]
3012
v[3]
3016
v[4]
…continua
• Sottrazione di puntatori
– Restituisce il numero di elementi fra i due
puntatori
vPtr2 = v[2];
vPtr = v[0];
v=vPtr2 - vPtr
si ha v=2.
• Confronto di puntatori ( <, == , > )
– Solo fra puntatori che si riferiscono allo stesso
vettore
– Determinare se un puntatore fa riferimento ad un
elemento con indice più alto di quello puntato
dall’altro puntatore
– Determinare se un puntatore sia NULL
…continua
• Puntatori allo stesso tipo possono essere assegnati
l’uno all’altro
– Eccezione: puntatore a void (ovvero void *)
• Puntatore generico, rappresenta qualsiasi tipo
• Non è necessario l’operatore cast per
convertire un puntatore a un puntatore void
• Non è possibile risolvere il riferimento di un
puntatore void
Relazione tra puntatori e vettori
• I vettori e i puntatori sono strettamente correlati
– Il nome di un vettore è come un puntatore
costante
– I puntatori possono essere utilizzati nelle
operazioni al posto degli indici di un vettore
• Sia b[5] un vettore e bPtr un puntatore
bPtr = b;
Il nome del vettore è in realtà l’indirizzo del primo
elemento
oppure
bPtr = &b[0];
che esplicitamente assegna a bPtr l’indirizzo del
primo elemento
…continua
• L’elemento b[n]
– può essere reperito con l’espressione *(bPtr + n )
– n è lo scostamento dal puntatore (offset)
*bPtr+3
– Il nome stesso del vettore può usare l’aritmetica dei
puntatori.
b[3] è equivalente a *(b + 3)
– Anche con i puntatori possono essere usati gli indici
(notazione con puntatore/indice)
bPtr[3] è equivalente b[3]
Nota
• È compito del programmatore sapere quando
l’incremento di un puntatore ha significato.
Altrimenti si rischia di accedere a zone di
memoria estranee all’array con risultati anche
disastrosi!!!
Vettori di puntatori
• I vettori possono contenere puntatori - vettori di stringhe
char *suit[4] = {"Hearts","Diamonds","Clubs","Spades"};
– Stringa: puntatore al primo carattere
– char * - ciascun elemento di suit è un puntatore a char
– Le stringhe non sono contenute realmente nel vettore - nel vettore ci
sono solo i puntatori alle stringhe
suit[0]
’H’
’e’
’a’
’r’
’t’
’s’
’\0’
suit[1]
’D’
’i’
’a’
’m’
’o’
’n’
’d’
suit[2]
’C’
’l’
’u’
’b’
’s’
’\0’
suit[3]
’S’
’p’
’a’
’d’
’e’
’s’
’s’
’\0’
’\0’
• il vettore suit ha dimensione fissa, ma le stringhe possono essere di
dimensione qualsiasi
(altrimenti matrice con dim fissata → SPRECO di MEMORIA)
Studio di un caso: simulatore di un
mescolatore e distributore di carte
• Mescolatore di carte
– Usa un vettore di puntatori a stringhe
– Usa un vettore bidimensionale (seme, valore)
0
Hearts
Diamonds
Clubs
Spades
1
2
3
4
5
6
7
8
9
10
11
0
1
2
3
deck[2][12] rappresenta il King di Clubs
Clubs
King
– Nel vettore saranno inseriti i numeri 1-52 che
rappresentano l’ordine in cui le carte saranno
distribuite
12
…continua
• Pseudocodice - livello Top: Mischiare e distribuire 52 carte
Primo raffinamento
Inizializzare i vettori
dei semi, dei valori,
la matrice delle carte
Secondo raffinamento
Per ciascuna delle 52 carte
Inserire il numero di
estrazione in un elemento
libero della matrice delle
carte scelto a caso
Mischiare le carte
Terzo raffinamento
Scegliere a caso un elemento
della matrice di carte
Finchè l’elemento è già stato
scelto precedentementte
Scegliere a caso un
elemento
Inserire il numero di
estrazione nell’elemento
della matrice di carte
Per ciascuna delle 52 carte
Distribuire le 52 carte
Trovare il numero di
estrazione nella matrice
delle carte e visualizzare
il valore e il seme della
carta
Per ciascun elemento della
matrice di carte
Se l’elemento contiene il
numero di estrazione
Visualizzare il valore e
il seme della carta
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
Card shuffling dealing program */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void shuffle( int [][ 13 ] );
void deal( const int [][ 13 ], const char *[], const char *[] );
int main()
{
const char *suit[ 4 ] =
{ "Hearts", "Diamonds", "Clubs", "Spades" };
const char *face[ 13 ] =
{ "Ace", "Deuce", "Three", "Four",
"Five", "Six", "Seven", "Eight",
"Nine", "Ten", "Jack", "Queen", "King" };
int deck[ 4 ][ 13 ] = { 0 };
srand( time( 0 ) );
shuffle( deck );
deal( deck, face, suit );
return 0;
}
void shuffle( int wDeck[][ 13 ] )
{
int row, column, card;
for ( card = 1; card <= 52; card++ ) {
33
do {
34
row = rand() % 4;
35
column = rand() % 13;
36
I numeri 1-52 sono messi a
caso nel vettore deck .
} while( wDeck[ row ][ column ] != 0 );
37
38
39
wDeck[ row ][ column ] = card;
}
40 }
41
42 void deal( const int wDeck[][ 13 ], const char *wFace[],
43
const char *wSuit[] )
44 {
45
int card, row, column;
46
47
for ( card = 1; card <= 52; card++ )
48
49
for ( row = 0; row <= 3; row++ )
50
51
for ( column = 0; column <= 12; column++ )
52
53
if ( wDeck[ row ][ column ] == card )
54
printf( "%5s of %-8s%c",
55
wFace[ column ], wSuit[ row ],
56
card % 2 == 0 ? '\n' : '\t' );
57 }
Ricerca in deck il numero
card , poi visualizza face
e suit.
Esecuzione
Esecuzione
Six
Six
Ace
Ace
Ace
Ace
Queen
Queen
Ten
Ten
Ten
Ten
Ten
Ten
Four
Four
Six
Six
Eight
Eight
Nine
Nine
Deuce
Deuce
Five
Five
Deuce
Deuce
Five
Five
King
King
Deuce
Deuce
Ace
Ace
Three
Three
Nine
Nine
Four
Four
Eight
Eight
Jack
Jack
Five
Five
Four
Four
Jack
Jack
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
Clubs
Clubs
Spades
Spades
Hearts
Hearts
Clubs
Clubs
Hearts
Hearts
Spades
Spades
Diamonds
Diamonds
Diamonds
Diamonds
Diamonds
Diamonds
Hearts
Hearts
Hearts
Hearts
Spades
Spades
Clubs
Clubs
Diamonds
Diamonds
Spades
Spades
Diamonds
Diamonds
Hearts
Hearts
Clubs
Clubs
Clubs
Clubs
Clubs
Clubs
Hearts
Hearts
Diamonds
Diamonds
Diamonds
Diamonds
Hearts
Hearts
Clubs
Clubs
Clubs
Clubs
Seven
Seven
Ace
Ace
Queen
Queen
Seven
Seven
Deuce
Deuce
Three
Three
Four
Four
Ten
Ten
Six
Six
Three
Three
Three
Three
Six
Six
Eight
Eight
Eight
Eight
King
King
Jack
Jack
Queen
Queen
King
King
King
King
Nine
Nine
Queen
Queen
Nine
Nine
Seven
Seven
Five
Five
Jack
Jack
Seven
Seven
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
of
Diamonds
Diamonds
Diamonds
Diamonds
Diamonds
Diamonds
Hearts
Hearts
Clubs
Clubs
Spades
Spades
Spades
Spades
Clubs
Clubs
Spades
Spades
Diamonds
Diamonds
Hearts
Hearts
Hearts
Hearts
Clubs
Clubs
Spades
Spades
Clubs
Clubs
Spades
Spades
Hearts
Hearts
Spades
Spades
Hearts
Hearts
Spades
Spades
Spades
Spades
Diamonds
Diamonds
Clubs
Clubs
Diamonds
Diamonds
Hearts
Hearts
Spades
Spades
Puntatori a funzioni
• Puntatore a una funzione
– Contiene il suo indirizzo in memoria
– Analogamente al nome di un vettore, che è
l’indirizzo del primo elemento, il nome di una
funzione è in realtà l’indirizzo di memoria dal quale
inizia il codice che definisce la funzione stessa
• I puntatori a funzioni possono essere
– passati e restituiti dalle funzioni
– memorizzati in vettori
– assegnati ad altri puntatori a funzioni
…continua
• Esempio: bubblesort
– La funzione bubble riceve come argomento un puntatore a
una funzione ausiliaria (ascending o descending)
• bubble chiama una delle funzioni ausiliarie
• così si può scegliere fra ordinamento ascendente o
discendente
– Nell’intestazione della funzione bubble compare il parametro:
int ( *compare )( int, int )
• questo avverte bubble che riceverà come parametro un
puntatore a una funzione che accetta due argomenti interi
e che restituisce un risultato dello stesso tipo
– Se non avessimo incluso le parentesi, al dichiarazione
sarebbe stata:
int *compare( int, int )
• che dichiara una funzione che riceve due interi e restituisce
un puntatore dello stesso tipo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
Multipurpose sorting program using function pointers */
#include <stdio.h>
#define SIZE 10
void bubble( int [], const int, int (*)( int, int ) );
int ascending( int, int );
int descending( int, int );
Si noti il parametro
puntatore a funzione
int main()
{
int order,
counter,
a[ SIZE ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 };
printf( "Enter 1 to sort in ascending order,\n"
"Enter 2 to sort in descending order: " );
scanf( "%d", &order );
printf( "\nData items in original order\n" );
for ( counter = 0; counter < SIZE; counter++ )
printf( "%5d", a[ counter ] );
if ( order
bubble(
printf(
}
else {
bubble(
printf(
}
== 1 ) {
a, SIZE, ascending );
"\nData items in ascending order\n" );
a, SIZE, descending );
"\nData items in descending order\n" );
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
for ( counter = 0; counter < SIZE; counter++ )
printf( "%5d", a[ counter ] );
printf( "\n" );
return 0;
}
void bubble( int work[], const int size,
int (*compare)( int, int ) )
{
int pass, count;
ascending e descending
restituiscono true o false.
bubble chiama swap se la
funzione restituisce true.
void swap( int *, int * );
for ( pass = 1; pass < size; pass++ )
for ( count = 0; count < size - 1; count++ )
if ( (*compare)( work[ count ], work[ count + 1 ] ) )
swap( &work[ count ], &work[ count + 1 ] );
}
void swap( int *element1Ptr, int *element2Ptr )
{
int temp;
temp = *element1Ptr;
*element1Ptr = *element2Ptr;
*element2Ptr = temp;
}
Si noti come i puntatori a
funzioni siano chiamati usando
l’operatore di deriferimento.
L’operatore * non è
necessario, ma evidenzia che
compare è un puntatore a
funzione e non una funzione.
65 int ascending( int a, int b )
66 {
67
return b < a;
/* swap if b is less than a */
68 }
69
70 int descending( int a, int b )
71 {
72
return b > a;
/* swap if b is greater than a */
73 }
Enter
Enter 11 to
to sort
sort in
in ascending
ascending order,
order,
Enter
Enter 22 to
to sort
sort in
in descending
descending order:
order: 11
Data
Data items
items in
in original
original order
order
22
66
44
88 10
12
10 12 89
89 68
68
Data
items
in
ascending
order
Data items in ascending order
22
44
66
88 10
10 12
12 37
37 45
45
45
45
37
37
68
68
89
89
Enter
Enter 11 to
to sort
sort in
in ascending
ascending order,
order,
Enter
Enter 22 to
to sort
sort in
in descending
descending order:
order: 22
Data
Data items
items in
in
22
66
44
Data
Data items
items in
in
89
89 68
68 45
45
original
original order
order
88 10
12
10 12 89
89 68
68
descending
order
descending order
37
88
66
37 12
12 10
10
45
45
37
37
44
22
Array come parametro di funzione
•
•
In realtà viene passato il puntatore all’array (che è
un singolo valore numerico)
Esistono due modi per passare un array ad una
funzione:
1. Si passa l’array come primo parametro e la
dimensione dell’array come secondo parametro
2. Si passa il puntatore all’array
•
(parametro.cpp)
•
Le modifiche apportate dalle funzioni sono
permanenti
{
int v[4]={1,2,3,4};
{
int v[4]={1,2,3,4};
Funz(v,4);
Funz(&v);
}
}
int Funz(int w, int dim) int Funz(int *w)
Passaggio di parametri per indirizzo
• Passa per valore l’indirizzo della variabile che si
vuole leggere o modificare
• Rende nota la locazione della variabile alla funzione
• Quindi modificabile dalle istruzioni della funzione
Bubble Sort usando la chiamata per
riferimento
• Realizziamo bubblesort usando puntatori
– Scambiare due elementi
– la funzione swap deve ricevere gli indirizzi degli
elementi del vettore (usando &)
• Gli elementi di un vettore hanno per default la
chiamata per valore
– Usando i puntatori e l’operatote *, swap può
scambiare gli elementi del vettore
• Psuedocodice
Inizializzare il vettore
visualizzare i dati nell’ordine originale
Chiamare la funzione bubblesort
visualizzare il vettore ordinato
Definire bubblesort
…continua
• sizeof
– Restituisce la dimensione in byte dell’operando
– Per i vettori: dimensione di 1 elemento * numero di
elementi
– se sizeof(int) = 4 bytes, allora
int myArray[10];
printf( "%d", sizeof( myArray ) );
visualizzerà 40
• sizeof può essere usato con
– Nomi di variabili
– Nomi di tipo
– Valori costanti
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
This program puts values into an array, sorts the values into
ascending order, and prints the resulting array. */
#include <stdio.h>
#define SIZE 10
void bubbleSort( int *, const int );
int main()
{
int a[ SIZE ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 };
int i;
printf( "Data items in original order\n" );
for ( i = 0; i < SIZE; i++ )
printf( "%4d", a[ i ] );
bubbleSort( a, SIZE );
/* sort the array */
printf( "\nData items in ascending order\n" );
for ( i = 0; i < SIZE; i++ )
printf( "%4d", a[ i ] );
printf( "\n" );
return 0;
}
void bubbleSort( int *array, const int size )
{
void swap( int *, int * );
33
int pass, j;
34
for ( pass = 0; pass < size - 1; pass++ )
35
36
for ( j = 0; j < size - 1; j++ )
37
38
if ( array[ j ] > array[ j + 1 ] )
39
swap( &array[ j ], &array[ j + 1 ] );
40 }
41
42 void swap( int *element1Ptr, int *element2Ptr )
43 {
44
int hold = *element1Ptr;
45
*element1Ptr = *element2Ptr;
46
*element2Ptr = hold;
47 }
Esecuzione
Esecuzione
Data
Data
22
Data
Data
22
items
items
66
items
items
44
in
in
44
in
in
66
original
original order
order
88 10
10 12
12 89
89 68
68
ascending
order
ascending order
88 10
10 12
12 37
37 45
45
45
45
37
37
Introduzione al C
Le stringhe
Array di caratteri
•Non esiste in C un tipo di dato predefinito
per le stringhe
•Le stringhe sono rappresentate come array
di caratteri.
•Questo approccio alle stringhe consente di
avere una potenza e flessibilità maggiori
rispetto ai linguaggi che utilizzano un tipo
speciale per le stringhe.
44
Stringhe
•Una stringa è un array di caratteri che finisce con un
terminatore NULL.
•Un NULL viene specificato come il carattere speciale ‘\0’
e corrisponde nella codifica ASCII al carattere che ha
valore zero. Pertanto,
•se ad esempio vogliamo dichiarare un array stringa che
contenga la stringa di caratteri “ciao”:
char stringa[5];
(Questo per lasciare spazio al terminature NULL alla fine
della stringa).
45
…continua
Una costante stringa è una lista di caratteri racchiusi
fra doppi apici:
“ciao”
ciao” è una costante stringa
“” prende il nome di stringa nulla perchè contiene
soltanto il terminatore NULL e nessun altro carattere.
Nelle costanti stringa non è necessario aggiungere
manualmente il terminatore NULL alla fine della
stringa, il compilatore lo fa automaticamente, perciò
la stringa “ciao” in memoria apparirà così:
c
i
a
o ‘\0’
46
Inizializzazione di un array di
caratteri
Gli array di caratteri permettono di utilizzare
un’inizializzazione abbreviata che assume
questa forma:
char nome_array[dimensione]=
nome_array[dimensione]=“
stringa”;
]=“stringa”
char str[5]=“
str[5]=“ciao”
ciao”;
Al posto della forma classica per gli array
interi.
tipo nome_variabile
nome_variabile[]={e1,
[]={e1,…
[]={e1,…,en};
char str[5]={‘
str[5]={‘c’,’i’,’a’,’o’,’\0’};
47
Array come parametro di
funzione
• Che cosa stampa?
#include <stdio.h
<stdio.h>
stdio.h>
void func(char a[]);
int main()
{
char a[]= “12345”
12345”;
func(a);
func(a);
return 0;
}
void func(char a[])
{
printf(“
printf(“%u\
%u\n”, sizeof a);
}
48
#include <stdio.h
<stdio.h>
stdio.h>
/*corretto*/
void func(
func(char a[], size_t s)
{
printf("%u
printf("%u\
("%u\n", s);
}
int main()
main()
{
char a[]="12345";
func(a,
func(a, sizeof a);
return 0;
49
}
50
Lettura di una stringa da
tastiera: gets()
• char *gets(
*gets(char *str);
*str);
• Legge i caratteri da stdio finché non incontra \n
o EOF
• \n è rimpiazzato da \0
• Restituisce
– un puntatore alla stringa
– il puntatore nullo se si è verificato qualche errore
oppure se ha incontrato \n o EOF prima che sia stato
inserito qualche carattere
• Il programmatore deve allocare spazio di
memoria sufficiente a contenere l’input per
prevenire errori di sovrascrittura! (input_ge.cpp)51
fgets()
È simile a gets() ma più flessibile perchè permette di
specificare 1)l’input stream e 2)il numero massimo di
caratteri da inserire
Questa funzione prende in input tre argomenti:
char *fgets(char
*fgets(char *str,int n, FILE *fp
*fp);
fp);
legge al massimo n-1 caratteri dallo standard input (la
tastiera)
la lettura da tastiera termina quando viene incontrato
un carattere \n o EOF in input oppure n-1 caratteri, ma
l’esecuzione non ritorna da fgets() finchè non è
premuto INVIO
viene aggiunto il terminatore NULL alla fine della
52
stringa (vedi stringa2.c)
Lettura di una stringa da tastiera
Utilizzo della funzione scanf().
Problemi:
scanf() smette di leggere una stringa non
appena incontra un carattere di spaziatura (space,
tab o newline), ma restituisce l’input solo dopo
l’invio.
scanf() non vieta di inserire una stringa di
caratteri più lunga delle dimensioni dell’array che
la può contenere.
Ritorna il numero di immissioni effettuate con
successo.
53
#include <stdio.h
<stdio.h>
stdio.h>
int main()
main()
{
char s[80];
int iNum;
iNum;
int count;
count;
puts("Inserire
puts("Inserire una stringa di caratteri e poi un
intero \ decimale\
decimale\n");
count=scanf("%s
count=scanf("%s\
("%s\t %d",s,&iNum
%d",s,&iNum);
&iNum);
printf("Il
printf("Il numero di valori inseriti e'
%d\
%d\n",count
n",count);
count);
printf("La
printf("La stringa inserita e' \"%s\
"%s\"\n",s);
printf("Il
printf("Il numero inserito e' %d\
%d\n",iNum
n",iNum);
iNum);
}
54
Si può specificare con %ns il numero
massimo di caratteri accettati
Ma, i caratteri extra rimangono nel buffer
di IO
• Esempi:
– Ancora sull’inserimento di stringhe di caratteri
con scanf()
scanf() : input_sc.cpp
– Come eliminare i caratteri extra dal buffer:
extra_ca.cpp
55
Quiz
• Quando il compilatore incontra un singolo
carattere tra apici singoli, come viene
interpretato?
• Per memorizzare una stringa di n caratteri
quanti elementi deve avere l’array?
• Quanti bytes sono allocati per memorizzare le
seguenti variabili?
–
–
–
–
char *str1={ “String 1” };
char *str2[]={ “String 2” };
char string3;
char str4[20]={ “String 4” };
56
• Usando la dichiarazione:
• char *string=“
*string=“A string!
string!”;
• che valore hanno le seguenti:
– string[0]
– *string
– string[9]
– string[33]
– string+8
– string
57