Nessun titolo diapositiva

Download Report

Transcript Nessun titolo diapositiva

Il linguaggio C
Gli array e i puntatori
Dichiarazione e memorizzazione di array
L’inizializzazione di array
L’aritmetica dei puntatori
Il passaggio di puntatori come argomenti di funzione
Gli algoritmi di ordinamento
Le stringhe
Fondamenti di Informatica I  a.a. 2008-09
1
Gli array e i puntatori
Fondamenti di Informatica I  a.a. 2008-09
2
Gli array
Nel linguaggio C, un array è un insieme di variabili dello
stesso tipo memorizzate consecutivamente
Ogni variabile dell’array, detta elemento, può essere
acceduta mediante il nome dell’array ed un indice (un
espressione), con il primo elemento individuato dall’indice di
valore 0
Esempio: Memorizzare la temperatura media registrata per
ogni giorno dell’anno
Si dichiara un array di 365 elementi (piuttosto che 365
variabili!), identificati con un solo nome, e che occupano
posizioni di memoria consecutive
Nota: Gli array contengono informazione correlata (le
temperature di un anno, i voti di esame di uno studente,
etc.)
Fondamenti di Informatica I  a.a. 2008-09
3
La dichiarazione di array  1
La sintassi della dichiarazione di un array è:
Specificatore
di tipo
Nome
dell’array
Dimensione
dell’array
[
]
=
{
Valori iniziali
}
La dichiarazione viene effettuata inserendo una coppia di
parentesi quadre dopo il nome dell’array
La dimensione dell’array viene specificata inserendo il
numero di elementi all’interno delle parentesi quadre
Fondamenti di Informatica I  a.a. 2008-09
4
La dichiarazione di array  2
Il riferimento ad un elemento dell’array è invece l’operazione
di accesso al dato elemento ed avviene utilizzando il nome
dell’array seguito dall’indice dell’elemento (racchiuso fra
parentesi quadre)
Le istruzioni di dichiarazione di un array e di riferimento ad
un elemento dell’array sono molto simili nella forma, ma
molto diverse nel significato
/* La seguente è una dichiarazione;
* il valore 365 specifica il numero
* di elementi dell’array
*/
int daily_temp[365];
Fondamenti di Informatica I  a.a. 2008-09
/* I seguenti sono riferimenti a elementi
* dell’array; i valori 0,1,2,… specificano
* gli elementi a cui accedere
*/
daily_temp[0]  2;
daily_temp[1]  5;
daily_temp[2]  3;
………
5
La dichiarazione di array  3
Esempio: Calcolo della temperatura media annua
include <stdio.h>
include <stdlib.h>
define DAYS_IN_YEAR 365
main()
{
int j, sum0;
int daily_temp[DAYS_IN_YEAR];
/* si assegnano i valori a daily_temp[] */
for (j0; j<DAYS_IN_YEAR; j)
sum  daily_temp[j];
printf(“La temperatura media dell’anno è %d.\n”, sum/DAYS_IN_YEAR);
exit(0);
}
Fondamenti di Informatica I  a.a. 2008-09
6
La modalità di memorizzazione
degli array
Esempio
4 byte
int ar[5]; /* dichiarazione */
ar[0]  15;
ar[1]  17;
ar[3]  3;
0FFC
1000
1004
1008
100C
1010
1014
1018
15
17
non definito
3
non definito
ar[0]
ar[1]
ar[2]
ar[3]
ar[4]
ar[2] e ar[4] sono indefiniti: il contenuto delle posizioni di
memoria è quello rimasto da esecuzioni precedenti
(garbage)
Fondamenti di Informatica I  a.a. 2008-09
7
L’inizializzazione di array  1
La presenza di valori indefiniti in alcuni elementi dell’array
può provocare errori difficili da rilevare
 Occorre inizializzare l’intero vettore
Dichiarare l’array static (vettore a durata fissa): gli elementi del
vettore, non inizializzati esplicitamente, vengono posti a zero
Valori diversi possono essere specificati, facendoli seguire alla
dichiarazione dell’array, racchiusi fra parentesi graffe: tali valori
devono essere espressioni costanti che possano essere
convertite automaticamente nel tipo dell’array
Fondamenti di Informatica I  a.a. 2008-09
8
L’inizializzazione di array  2
4 byte
Esempio
static int ar[5];
static int br[5]  {1,2,3.5,4,5};
Nota: il valore floatingpoint
3.5 è convertito nel valore
intero 3
Fondamenti di Informatica I  a.a. 2008-09
0FFC
1000
1004
1008
100C
1010
1014
1018
101C
1020
1024
1028
0
15
17
0
non definito
0
0
0
1
2
3
4
5
ar[0]
ar[1]
ar[2]
ar[3]
ar[4]
br[0]
br[1]
br[2]
br[3]
br[4]
9
L’inizializzazione di array  3
Specificare un numero maggiore di valori di inizializzazione,
rispetto agli elementi dell’array, costituisce un errore
segnalato dal compilatore
Se vengono specificati meno valori rispetto alla dimensione,
gli elementi rimanenti vengono inizializzati a zero
Esempio: La dichiarazione
static int cr[5]  {1,2,3};
produce l’inizializzazione
cr[0]  1
cr[1]  2
cr[2]  3
cr[3]  0
cr[4]  0
Fondamenti di Informatica I  a.a. 2008-09
10
L’inizializzazione di array  4
Se vengono specificati i valori iniziali, può essere omessa la
dimensione dell’array: il compilatore calcola automaticamente
il numero degli elementi sulla base del numero dei valori
iniziali specificati
Esempio:
static char dr[]  {‘a’, ‘b’, ‘c’, ‘d’};
comporta la creazione di un array di quattro elementi, di tipo
char, caratterizzati dai valori iniziali
dr[0]  ‘a’
dr[1]  ‘b’
dr[2]  ‘c’
dr[3]  ‘d’
Fondamenti di Informatica I  a.a. 2008-09
11
Crittografia  1
Poiché in formato elettronico viene memorizzato e trasmesso
ogni tipo di informazione (anche personale, quindi coperta
dalla privacy), viene posta attenzione alla sicurezza rispetto
ad accessi indesiderati
I file, nei sistemi multiutente, sono provvisti di vari livelli di
protezione, il primo dei quali è costituito dall’autenticazione al
sistema tramite password
Oltre ai tipi di protezione standard, il contenuto di un file
riservato può essere codificato, utilizzando tecniche
crittografiche che producono, a partire dal testo in chiaro, il
cosiddetto crittogramma, o testo cifrato
Il legittimo proprietario del file (e chi da lui autorizzato) è
l’unico depositario della chiave di decodifica ed è l’unico in
grado di operare l’inversione da testo cifrato a testo in chiaro
Fondamenti di Informatica I  a.a. 2008-09
12
Crittografia  2
Inoltre, negli attuali sistemi informativi distribuiti, e più in
generale nel settore delle telecomunicazioni, la crittografia ha
assunto rilievo ed interesse crescenti nelle infrastrutture di
sicurezza
Fondamenti di Informatica I  a.a. 2008-09
13
Crittografia  3
Se il sistema di cifra, o cifrario, è ben congegnato,
l’operazione di decifrazione o decifratura deve risultare
semplice al legittimo proprietario (o al destinatario del
messaggio), ma di complessità proibitiva alla “spia”
 possibile in quanto gli utenti “legittimi” possiedono
un’informazione che deve rimanere inaccessibile alla spia, la
chiave del cifrario
Occorre notare la distinzione tra decifrazione e decrittazione,
l’operazione illegittima in cui non ci si può avvalere della
chiave
Fondamenti di Informatica I  a.a. 2008-09
14
Crittografia  4
/* Dato un carattere, ne fornisce un valore codificato */
define ILLEGAL_VAL 1
48=‘0’  111=‘o’
char encode(ch)
char ch;
{
static unsigned char encoder[128]  {127, 124, 121, 118, 115, 112,
109, 106, 103, 100, 97, 94, 91, 88, 85, 82, 79, 76, 73, 70, 67, 64, 61,
58, 55, 52, 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4,
1, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 87, 84,
81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45, 42, 39, 36, 33, 30,
27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 125, 122, 119, 116, 113, 110, 107,
104, 101, 98, 95, 92, 89, 86, 83, 80, 77, 74, 71, 68, 65, 62, 59, 56, 53,
50, 47, 44, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2}
/* Controlla la presenza di caratteri illeciti */
if (ch > 127)
return ILLEGAL_VAL;
else
return encoder[ch]; /* Fornisce il carattere codificato */
}
Fondamenti
di Informatica I  a.a. 2008-09
Ciao
6G\2
15
Crittografia  5
Il codice Shift Cypher con K3 era utilizzato da Giulio Cesare
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
0 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
Esercizio: Con K11, si somma 11 al valore corrispondente alla
lettera e, nel caso che la somma superi 25, si divide il numero per
26 e si considera il resto della divisione intera
we will meet at midnight
22 4 22 8 11 11 12 4 4 19 0 19 12 8 3 13 8 6 7 19

7 15 7 19 22 22 23 15 15 4 11 4 23 19 14 24 19 17 18 4
hphtwwxppelextoytrse
Fondamenti di Informatica I  a.a. 2008-09
16
Esempio  1
/* Dato un vettore, stabilisce se vi sono almeno due elementi uguali tra loro
*/
#define MAX_DIM 256
typedef int[MAX_DIM] VECTOR;
int TwinSearch (v) /* ricerca degli elementi gemelli */
VECTOR v;
{
int c  0;
int k  0;
int found  1; /* ricerca senza successo ritorna 1 */
for(c  0; ((c < (MAX_DIM1)) && (found  1)); c)
{
for(k  (c1); ((k < MAX_DIM) && (found  1)); k)
{
if (v[c]  v[k])
found  1; /* ricerca con successo */
}
}
}
return found;
Fondamenti di Informatica I  a.a. 2008-09
17
Esempio  2
/* Inverte gli elementi di un vettore */
#include <stdio.h>
/*inversione vettore*/
for(i0;i<dim;i)
b[i]a[dim1i];
int main()
{
/*dichiarazioni*/
int a[10], b[10];
int i, dim10;
/*input coefficienti*/
for(i0;i<dim;i)
{
printf(“inserire il coefficiente a[%d]: ”, i);
scanf(“%d”, &a[i]);
}
/*stampa vettore*/
printf(“\nIl vettore e’:\n\n”);
for(i0;i<dim;i)
printf(“a[%d]  %d\n”, i, a[i]);
printf(“\n”);
Fondamenti di Informatica I  a.a. 2008-09
/*stampa vettore invertito*/
printf(“Il vettore invertito e’:\n\n”);
for(i0;i<dim;i)
printf(“b[%d]  %d\n”, i, b[i]);
return 0;
}
18
L’aritmetica dei puntatori  1
Il C permette l’utilizzo degli operatori additivi in
concomitanza con i puntatori
Se p è un puntatore, l’espressione “p3” è lecita ed
individua il terzo oggetto che segue quello puntato da p
Poiché p contiene un indirizzo, operazioni aritmetiche su p
forniscono nuovi indirizzi
Il compilatore non opera semplicemente una somma tra 3 e
p, ma moltiplica 3 per la dimensione dell’oggetto puntato da
p: effettua cioè uno scaling
Esempio: Se l’indirizzo contenuto in p è 1000 e p è un
puntatore a long int, allora p3 identifica l’indirizzo 1018 (8
byte per gli interi lunghi); se p è un puntatore a char, p3
rappresenta l’indirizzo 1003
Fondamenti di Informatica I  a.a. 2008-09
19
L’aritmetica dei puntatori  2
Nell’ipotesi di puntatori che si riferiscono allo stesso tipo di
dato, è lecito sottrarre il valore di un puntatore da un altro:
l’operazione fornisce un valore intero che rappresenta il
numero di oggetti compresi fra i due puntatori
Se il primo puntatore è relativo a un indirizzo inferiore al
secondo, il risultato è un intero negativo
&a[3]  &a[0]  3
&a[0]  &a[3]  3
È lecito anche sottrarre un valore intero da un puntatore: il
risultato è un puntatore
Fondamenti di Informatica I  a.a. 2008-09
20
L’aritmetica dei puntatori  3
Esempio
long *p1, *p2, k;
int j;
char *p3;
p1  &k;
p2  p1 4; /* OK */
j  p2p1;
/* OK a j viene assegnato 4 */
j  p1p2;
/* OK a j viene assegnato 4 */
p1  p22; /* OK i tipi dei puntatori sono compatibili */
p3  p11; /* NO i tipi dei puntatori sono diversi */
j  p1p3;
/* NO i tipi dei puntatori sono diversi */
Fondamenti di Informatica I  a.a. 2008-09
21
L’aritmetica dei puntatori  4
Il linguaggio C prevede la definizione di puntatori nulli,
ovvero di puntatori che non puntano ad alcun oggetto valido
Un puntatore nullo è un qualsiasi puntatore a cui sia
assegnato il valore zero
char *p;
p0; /* rende p un puntatore nullo */
In questo caso il compilatore non effettua la conversione
esplicita dell’espressione intera nel tipo del puntatore
La definizione del puntatore nullo è utile all’interno di
istruzioni di controllo: il puntatore nullo è l’unico puntatore
cui è associato il valore FALSE; tutti i puntatori validi valgono
TRUE
Fondamenti di Informatica I  a.a. 2008-09
22
Il passaggio di puntatori come
argomenti di funzione  1
Il compilatore segnala i tentativi di utilizzo congiunto di
puntatori di tipi diversi
Un’eccezione alla regola è costituita dall’uso di puntatori
come argomenti di funzione: in mancanza di un prototipo, il
compilatore non effettua controlli per verificare la
corrispondenza di tipo fra parametro attuale e parametro
formale
 si possono produrre risultati inconsistenti nel caso di
parametri di tipo disomogeneo
Il prototipo di una funzione è una dichiarazione di funzione
antecedente alla sua definizione: permette al compilatore di
compiere il controllo sui tipi degli argomenti che vengono
passati alla funzione
Fondamenti di Informatica I  a.a. 2008-09
23
Il passaggio di puntatori come
argomenti di funzione  2
include <stdio.h>
include <stdlib.h>
void clr(p)
int *p;
{
*p  0 /* Memorizza 0 alla locazione p */
}
main()
{
static short s[3]  {1,2,3};
clr(&s[1]); /* Azzera l’elemento 1 di s[] */
printf(“s[0]%d\ns[1]%d\ns[2]%d\n”, s[0],s[1],s[2]);
exit(0);
s[0]1
s[1]2
s[2]3
p è un puntatore a int 
vengono azzerati 4 byte,
quindi sia s[1] che s[2]
s[0]1
s[1]0
s[2]0
}
Fondamenti di Informatica I  a.a. 2008-09
24
L’accesso agli elementi di array
mediante puntatori  1
È possibile accedere agli elementi di un array attraverso…
…l’uso del nome dell’array con il relativo indice
…l’uso dei puntatori
Infatti vale la regola che…
Aggiungere un intero ad un puntatore all’inizio di un array, ed
accedere all’indirizzo puntato dall’espressione, equivale ad
utilizzare l’intero come indice dell’array
Se p  &ar[0]  *(pe) è equivalente a ar[e]
Inoltre, un nome di array non seguito da un indice viene
interpretato come un puntatore all’elemento iniziale dell’array
ar è equivalente a &ar[0]
Fondamenti di Informatica I  a.a. 2008-09
25
L’accesso agli elementi di array
mediante puntatori  2
Combinando le due relazioni, si ottiene la regola generale
ar[n] equivale a *(arn)
Un nome di array viene trasformato dal compilatore C in un
puntatore all’elemento iniziale dell’array e quindi gli indici
vengono interpretati come spostamenti dalla posizione di
indirizzo base
In considerazione del meccanismo di scaling, lo spostamento
determina il numero di elementi da oltrepassare
Esempio:
ar[2]
*(ar2)
Fondamenti di Informatica I  a.a. 2008-09
Sono equivalenti: in entrambi i casi, ar è un puntatore
all’elemento iniziale dell’array e 2 è un fattore di
spostamento che richiede al compilatore di aggiungere
due al valore del puntatore
26
L’accesso agli elementi di array
mediante puntatori  3
Tuttavia…
…i valori delle variabili puntatore possono essere modificati
…i nomi di array non sono variabili, ma riferimenti a indirizzi
delle variabili array e, come tali, non possono essere modificati
Un nome di array non associato ad un indice o ad un
operatore “accesso all’indirizzo di” (*) non può apparire alla
sinistra di un operatore di assegnamento
float ar[7], *p;
par; /* OK equivale a p&ar[0] */
arp; /* NO: non è possibile operare assegnamenti su un indirizzo di array */
&par; /* NO: non è possibile operare assegnamenti su un indirizzo di puntatore */
ar;
/* NO: non è possibile incrementare un indirizzo di array */
ar[1]*(p5); /* OK ar[1] è una variabile */
p;
/* OK è possibile incrementare una variabile puntatore */
Fondamenti di Informatica I  a.a. 2008-09
27
Il passaggio di array come
argomenti di funzione  1
In C, un nome di array, utilizzato come argomento di funzione,
viene interpretato come indirizzo del primo elemento dell’array
Esempio:
main()
{
extern float func();
float x;
static float farray[5];
x  func(farray) /* equivalente a: func(&farray[0]) */
………
Nella funzione chiamata è necessario dichiarare l’argomento come
un puntatore all’elemento iniziale di un array
func(ar)
float *ar;
{
………
}
Fondamenti di Informatica I  a.a. 2008-09
func(ar)
float ar[];
{
………
}
Non è specificata la
dimensione perché non
si alloca memoria
28
Il passaggio di array come
argomenti di funzione  2
Anche nel secondo caso, ciò che viene passato è un
puntatore al primo elemento dell’array ed il compilatore è in
grado di convertire automaticamente ar in un puntatore a
float
In termini di leggibilità, la seconda versione è preferibile,
poiché evidenzia che l’oggetto passato è l’indirizzo di base di
un array e non un generico puntatore a una variabile float
(scalare o composta?)
La dichiarazione della dimensione dell’array nella definizione
dell’argomento è comunque corretta: il compilatore può
usare l’informazione sulla dimensione per effettuare controlli
sui valori limite
Fondamenti di Informatica I  a.a. 2008-09
29
Il passaggio di array come
argomenti di funzione  3
Non è possibile ottenere la dimensione di un array all’interno
di una funzione cui viene passato come argomento, ma solo
laddove l’array è effettivamente dichiarato
include <stdio.h>
include <stdio.h>
void print_size(arg)
float arg[];
{
printf(“La dimensione di arg è: %d\n”, sizeof(arg));
}
main()
{
void print_size();
static float f_array[10];
}
Sulla macchina di riferimento,
l’esecuzione del programma
fornisce:
La dimensione di f_array è: 40
La dimensione di arg è: 4
printf(“La dimensione di f_array è: %d\n”, sizeof(f_array));
print_size(f_array);
exit(0);
Fondamenti di Informatica I  a.a. 2008-09
30
Uscita dal limite superiore di un array
Il compilatore, di solito, non controlla che l’accesso agli
elementi di un array venga effettuato rispettandone i limiti
dimensionali
 È possibile accedere per errore ad elementi per i quali
non è stata allocata memoria (aree di memoria riservate ad
altre variabili, riservate ad altri processi, etc.)
Esempio:
Essendo ar un array di 10 elementi,
main()
{
int ar[10], j;
for (j0; j<10; j)
ar[j]  0;
}
Fondamenti di Informatica I  a.a. 2008-09
quelli cui è possibile accedere in modo
corretto hanno indice da 0 a 9: il ciclo
for contiene un errore offbyone
Probabilmente verrebbe azzerata la
variabile j  il ciclo diventa infinito
31
Gli algoritmi di ordinamento  1
L’ordinamento di una sequenza di informazioni consiste
nel disporre le stesse informazioni in modo da rispettare
una qualche relazione d’ordine; ad esempio, una
relazione d’ordine “minore o uguale” dispone le
informazioni in modo “non decrescente”
L’ordinamento è un’operazione molto importante perché
permette di ridurre notevolmente i tempi di ricerca di
un’informazione, nell’ambito di una sequenza di
informazioni
Nel caso in cui tale sequenza risulta ordinata, secondo
una qualche relazione d’ordine, è infatti possibile
sfruttare la stessa relazione d’ordine per effettuare la
ricerca
Fondamenti di Informatica I  a.a. 2008-09
32
Gli algoritmi di ordinamento  2
Esistono due categorie di algoritmi di ordinamento: la
classificazione è fatta in base alla complessità di calcolo
e alla semplicità algoritmica
La complessità di calcolo si riferisce al numero di
operazioni necessarie all’ordinamento; tali operazioni
sono essenzialmente confronti e scambi tra gli elementi
dell’insieme da ordinare
La semplicità algoritmica si riferisce alla lunghezza e
alla comprensibilità del codice
Fondamenti di Informatica I  a.a. 2008-09
33
Gli algoritmi di ordinamento  3
Algoritmi semplici di ordinamento
Algoritmi che presentano complessità O(n2), dove n è il
numero di informazioni da ordinare: sono caratterizzati
da poche e semplici istruzioni, dunque si realizzano con
poche linee di codice
Algoritmi evoluti di ordinamento
Algoritmi che presentano complessità computazionale
O(nlog2n): sono più complessi, fanno spesso uso di
ricorsione; la convenienza del loro utilizzo si rileva
quando il numero n di informazioni da ordinare è molto
elevato
Fondamenti di Informatica I  a.a. 2008-09
34
Bubblesort  1
La strategia Bubblesort (ordinamento a bolla) prevede il
confronto dei primi due elementi di un array, e lo
scambio, se il primo è maggiore del secondo
Dopo il primo confronto, si effettua un confronto fra il
secondo ed il terzo elemento (con eventuale scambio),
fra il terzo ed il quarto, etc.
Gli elementi “pesanti” (grandi) tendono a scendere
verso il fondo del vettore, mentre quelli “leggeri” (più
piccoli) salgono (come bolle) in superficie
Fondamenti di Informatica I  a.a. 2008-09
35
Bubblesort  2
Il confronto fra tutte le coppie di elementi adiacenti viene
detto passaggio
Se, durante il primo passaggio, è stato effettuato almeno uno
scambio, occorre procedere ad un ulteriore passaggio
Ad ogni passaggio, almeno un elemento assume la sua
posizione definitiva (l’elemento più grande del sottoinsieme
attualmente disordinato)
Devono essere effettuati al più n1 passaggi
Al kesimo passaggio vengono effettuati nk confronti (con
eventuali scambi): almeno k1 elementi sono già ordinati
Complessivamente, vengono effettuati n(n1)/2 confronti
 La complessità computazionale del Bubblesort è O(n2)
Fondamenti di Informatica I  a.a. 2008-09
36
Bubblesort  3
define FALSE 0
define TRUE 1
include <stdio.h>
void bubble_sort(list, list_size)
int list[], list_size;
{
int j, temp, sortedFALSE;
while (!sorted)
{
sorted  TRUE; /* assume che list sia ordinato */
for (j0; j<list_size1; j)
{
if (list[j]>list[j1])
{ /* almeno un elemento non è in ordine */
sorted  FALSE;
temp  list[j];
list[j]  list[j1];
list[j1]  temp;
}
} /* fine del ciclo for */
} /* fine del ciclo while */
}
Fondamenti di Informatica I  a.a. 2008-09
Nota
Nel caso migliore,
quando il vettore è
già
ordinato,
si
effettua un solo
passaggio, con n1
confronti e nessuno
scambio
 La complessità
scende a O(n)
37
Le stringhe
Fondamenti di Informatica I  a.a. 2008-09
38
Definizione
Una stringa è un array di caratteri terminato dal
carattere nullo, corrispondente alla sequenza di escape
\0 (con valore numerico associato zero)
Una stringa costante (o letterale) è una serie di
caratteri racchiusi fra doppi apici: tale stringa è di tipo
array di caratteri, con ogni carattere che occupa un
byte
Ad ogni stringa viene aggiunto automaticamente dal
compilatore un carattere nullo, ad indicarne la fine
Fondamenti di Informatica I  a.a. 2008-09
39
Dichiarazione e inizializzazione  1
Per memorizzare una stringa occorre dichiarare un array di char,
che può essere inizializzato con una stringa costante
static char str[]“testo”;
L’array ha dimensione maggiore di uno rispetto alla lunghezza
della stringa, per consentire la memorizzazione del carattere nullo
di terminazione (str ha lunghezza 6 byte)
Il compilatore segnala un errore se si dichiara la lunghezza della
stringa n, e si inizializza con una stringa costante di lunghezza >n
static char str[3]“quattro”; /* SCORRETTO */
static char str1[3]“tre”;
/* CORRETTO */
I compilatori ANSI, generalmente, consentono di specificare una
dimensione di array che non includa il carattere terminatore
Fondamenti di Informatica I  a.a. 2008-09
40
Dichiarazione e inizializzazione  2
È possibile inizializzare un puntatore a
char con una stringa costante:
char *ptr = “altro testo”;
si crea un array di caratteri,
inizializzato ad “altro testo”, riservando
però memoria anche per il puntatore
Nel caso dell’array, tutti i successivi accessi
utilizzano
il
nome
dell’array
come
riferimento per l’indirizzo dell’elemento
iniziale dell’array: tale indirizzo non può
essere modificato
Il puntatore è una variabile e può essere
modificato: l’indirizzo relativo alla prima
inizializzazione viene perso
2000
Fondamenti di Informatica I  a.a. 2008-09
str
0FFF
1006
‘a’
1000
‘t’
1007
‘l’
1001
‘e’
1008
‘t’
1002
‘s’
1009
‘r’
1003
‘t’
100A
‘o’
1004
‘o’
100B
‘’
1005
‘\0’
100C
‘t’
100D
‘e’
100E
‘s’
100F
‘t’
1010
‘o’
1011
‘\0’
ptr
1006
41
Gli assegnamenti a stringhe
Un puntatore a char può essere inizializzato con una stringa
costante, perché una stringa è un array di char
Una stringa costante viene interpretata come un puntatore al primo
carattere della stringa
include <stdlib.h>
main()
{
char array[10];
char *ptr1  “10 spazi”;
char *ptr2;
array  “not OK”; /* non è possibile assegnare un indirizzo */
array[5]  ‘A’;
/* OK */
*(ptr15)  ‘B’; /* OK */
ptr1  “OK”;
ptr1[5]  ‘C’;
/* opinabile a causa dell’assegnamento precedente */
*ptr2  “not OK”;/* conflitto di tipi */
ptr2  “OK”;
exit(0);
}
Fondamenti di Informatica I  a.a. 2008-09
42
Stringhe e caratteri  1
Occorre notare la differenza fra stringhe costanti e costanti di
tipo carattere:
char ch  ‘a’; /* Per ‘a’ è riservato un byte */
/* Vengono riservati due byte per “a”, oltre allo
* spazio necessario alla memorizzazione di ps
*/
char *ps  “a”;
È possibile assegnare una costante carattere all’indirizzo
contenuto in un puntatore a char; è invece scorretto
effettuare la stessa operazione relativamente ad una stringa
char *p1;
*p1  ‘a’; /* OK */
*p1  “a”; /* not OK */
Fondamenti di Informatica I  a.a. 2008-09
char *p2;
p2  ‘a’; /* not OK */
p2  “a”;/* OK */
Le stringhe sono interpretate
come puntatori a carattere
43
Stringhe e caratteri  2
Le inizializzazioni e gli assegnamenti non sono simmetrici; è
infatti possibile scrivere
Puntatore a carattere
char *p  “string”;
Carattere
ma non…
*p  “string”;
Nota: vale per inizializzazioni ed assegnamenti di tutti i tipi di
dati
Puntatore a float
float f;
float *pf  &f; /*OK */
*pf  &f; /* SCORRETTO */
Float
Fondamenti di Informatica I  a.a. 2008-09
44
Lettura e scrittura di stringhe  1
Le stringhe possono essere lette e scritte utilizzando le
funzioni scanf() e printf(), con lo specificatore di formato %s
L’argomento della funzione scanf() deve essere un puntatore
ad un array di caratteri di dimensioni sufficienti a contenere
la stringa in ingresso, che si intende terminata da un
qualsiasi carattere di spaziatura
La funzione scanf(), dopo aver letto il dato in ingresso,
aggiunge automaticamente il carattere \0 in fondo alla
stringa
L’argomento della funzione printf() deve essere un puntatore
ad un array di caratteri terminato dal carattere nullo (che
non viene stampato)
Fondamenti di Informatica I  a.a. 2008-09
45
Lettura e scrittura di stringhe  2
Esempio: Scrivere un programma che legge una stringa dalla
periferica d’ingresso di default e la stampa dieci volte
include <stdio.h>
include <stdlib.h>
define MAX_CHAR 80
main()
{
char str[MAX_CHAR];
int i;
È possibile utilizzare il nome
dell’array come argomento per
le funzioni di I/O, in quanto
puntatore all’inizio dell’array
printf(“Introdurre una stringa:”);
scanf(“%s”, str);
for (i0; i<10; i)
printf(“%s\n”, str);
exit(0);
}
Fondamenti di Informatica I  a.a. 2008-09
46
Le funzioni di libreria per le stringhe:
strlen()
La funzione strlen(), restituisce il numero di caratteri che
compongono una stringa (escluso il carattere nullo)
Poiché nell’espressione *str i
due operatori hanno la stessa
precedenza
ed
associatività
destra,
l’espressione
viene
analizzata dal compilatore nel
modo seguente:
Valutazione
dell’operatore
di
incremento postfisso; il compilatore
passa str all’operatore successivo e lo
incrementa solo al termine della
valutazione dell’espressione
int strlen(str)
char *str;
{
int i;
for (i0; *str; i)
; /* istruzione vuota */
return i;
}
Valutazione dell’operatore *, applicato a str
Completamento dell’espressione, con l’incremento di str
Fondamenti di Informatica I  a.a. 2008-09
47
Le funzioni di libreria per le stringhe:
strcpy()
La funzione strcpy() copia una stringa in un’altra
Il risultato dell’assegnamento
costituisce la condizione di
test per il ciclo while
Se *s2 vale zero (per il
carattere di terminazione), si
ha l’uscita dal ciclo
void strcpy(s1, s2)
char *s1, *s2;
{
while (*s2  *s1)
; /*istruzione vuota */
}
L’operatore di incremento postfisso è obbligatorio: un
incremento prefisso non produrrebbe un risultato corretto,
dato che il primo elemento non verrebbe copiato
Fondamenti di Informatica I  a.a. 2008-09
48
Le funzioni di libreria per le stringhe:
strstr()  1
La funzione strstr() effettua la ricerca di una sottostringa
all’interno di una stringa, operazione detta comunemente
pattern matching
La funzione prevede come argomenti due puntatori a
stringhe di caratteri ed effettua la ricerca di un’occorrenza
della seconda stringa nella prima:
se esiste un’occorrenza, viene restituita la posizione d’inizio
nell’array
altrimenti, viene restituito 1
Nota: la maggior parte delle funzioni della libreria di runtime
restituisce 0 o 1, come valore di errore (per strstr(), 0
corrisponde all’occorrenza della seconda stringa all’inizio della
prima)
Fondamenti di Informatica I  a.a. 2008-09
49
Le funzioni di libreria per le stringhe:
strstr()  2
/* Restituisce la posizione di str2 in str1; restituisce 1 se non esiste occorrenza */
int strstr(str1, str2)
char *str1, *str2;
{
char *p, *q, *substr;
/* Itera su ogni carattere di str1 */
for (substrstr1; *substr; substr)
{
p  substr;
q  str2;
/* Controlla se l’occorrenza di str2 corrisponde alla posizione corrente */
while (*q)
if (*q !*p)
goto no_match; /* serve per uscire dal while, ma restare nel for */
/* Si giunge qui solo se si è trovata un’occorrenza di str2 */
return substrstr1;
/* Si giunge qui se non è stata riscontrata un’occorrenza di str2 (nel ciclo while) */
no_match: ;
}
/* Si giunge qui se non vi sono occorrenze di str2 in str1 */
return 1;
}
Fondamenti di Informatica I  a.a. 2008-09
50
Le funzioni di libreria in string.h
strcpy()
Copia una stringa in un array
strncpy()
Copia una parte di una stringa in un array
strcat()
Concatena due stringhe
strncat()
Concatena parte di una stringa ad un’altra
strcmp()
Confronta due stringhe
strncmp()
Confronta due stringhe per una lunghezza data
strchr()
Cerca la prima occorrenza di un carattere specificato in una stringa
strcoll()
Confronta due stringhe sulla base di una sequenza di confronto definita
strcspn()
Calcola la lunghezza di una stringa che non contiene i caratteri specificati
strerror()
Fa corrispondere ad un numero di errore un messaggio di errore testuale
strlen()
Calcola la lunghezza di una stringa
strpbrk()
Cerca la prima occorrenza di uno tra i caratteri specificati all’interno di una stringa
strrchr()
Cerca l’ultima occorrenza di un carattere in una stringa
strspn()
Calcola la lunghezza di una stringa che contenga caratteri specificati
strstr()
Cerca la prima occorrenza di una stringa in un’altra
strtok()
Divide una stringa in una sequenza di simboli
strxfrm()
Trasforma una stringa in modo che sia utilizzabile come argomento per strcmp()
Fondamenti di Informatica I  a.a. 2008-09
51
Esempio: Calcolo dell’età
/* Esempio di conversione da stringa ad intero */
include <stdio.h>
include <stdlib.h>
main()
{
char anno_nascita[5], anno_corrente[5];
int anni;
printf(“Inserire l’anno di nascita: ”);
scanf(“%s”, anno_nascita);
printf(“Inserire l’anno corrente: ”);
scanf(“%s”, anno_corrente);
/* atoi() converte una stringa in un intero */
anni  atoi(anno_corrente)  atoi(anno_nascita);
printf(“Età: %d\n”, anni);
exit(0);
}
Fondamenti di Informatica I  a.a. 2008-09
52
Esempio: Parole nella stringa
/* Conta il numero di parole in una stringa */
include <ctype.h>
int word_count(s)
char *s;
{
int count0;
while (*s !`\0´)
{
while (isspace(*s))
s;
if (*s !`\0´)
{
count;
while (!isspace(*s)
s;
}
}
return count;
}
Fondamenti di Informatica I  a.a. 2008-09
/* salta la spaziatura */
/* trovata una parola */
&& *s !`\0´)
/* salta la parola */
53
Esempio: Parole palindrome
/* Letta in input una stringa, verifica se è palindroma */
include <stdio.h>
include <stdlib.h>
include <string.h>
main()
{
char parola[32], i0, n;
printf(“Inserisci una parola (lunga al max 31 caratteri): ”);
scanf(“%s”, parola);
n  strlen(parola);
while ((i  n/2) && (parola[i]   parola[n1i]))
i;
if (i  n/2)
printf(“La parola %s è palindroma.\n”, parola);
else
printf(“La parola %s non è palindroma.\n”, parola);
exit(0);
}
Fondamenti di Informatica I  a.a. 2008-09
54
Esempio: Da minuscole a maiuscole
/* Letta in input una stringa alfabetica, la riscrive utilizzando solo lettere maiuscole*/
include <stdlib.h>
include <stdio.h>
include <string.h>
main()
{
char s[100], t[100];
int i;
printf(“Inserisci una stringa: ”);
scanf(“%s”, s);
for (i0; istrlen(s); i)
{
if (s[i]  97 && s[i]  122)
t[i]  s[i]  32;
else
t[i]  s[i];
}
printf(“Stringa maiuscola: %s\n”, t);
}
exit(0);
Fondamenti di Informatica I  a.a. 2008-09
55