560 likes | 749 Vues
Array e puntatori in C. Sommario. Gli array ed 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. 2. Gli array ed i puntatori. 3.
E N D
Sommario • Gli array ed 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 2
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.) Gli array 4
La dichiarazione di array 1 • La sintassi della dichiarazione di arrayè: • 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 Specificatore di tipo Nome dell’array Dimensione dell’array [ ] = { } Valori iniziali 5
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 dichiarazione di array 2 /* I seguenti sono riferimenti a elementi * dell’array; i valori 0,1,2,… * specificano gli elementi cui accedere */ daily_temp[0] 2; daily_temp[1] 5; daily_temp[2] 3; … … … /* La seguente è una dichiarazione; * il valore 365 specifica il numero * di elementi dell’array */ int daily_temp[365]; 6
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, sum0; int daily_temp[DAYS_IN_YEAR]; /* si assegnano i valori a daily_temp[] */ for (j0; j<DAYS_IN_YEAR; j) sum daily_temp[j]; printf(“La temperatura media dell’anno è %d.\n”, sum/DAYS_IN_YEAR); exit(0); } 7
Esempio ar[2] e ar[4] sono indefiniti: il contenuto delle posizioni di memoria è quello rimasto da esecuzioni precedenti (garbage) La memorizzazione di array 4 byte 0FFC intar[5]; /* dichiarazione */ ar[0] 15; ar[1] 17; ar[3] 3; ar[0] 15 1000 ar[1] 1004 17 ar[2] 1008 non definito ar[3] 100C 3 ar[4] 1010 non definito 1014 1018 8
La presenza di valori indefiniti in alcuni elementi dell’array può provocare errori difficili da rilevare Occorre inizializzare l’intero vettore Dichiarare l’arraystatic (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 L’inizializzazione di array 1 9
Esempio Nota:il valore floatingpoint 3.5 viene convertito nel valore intero 3 ar[0] br[0] ar[1] br[1] ar[2] br[2] ar[3] br[3] ar[4] br[4] L’inizializzazione di array 2 4 byte 0FFC staticintar[5]; staticintbr[5]{1,2,3.5,4,5}; 0 15 1000 1004 17 0 1008 0 non definito 100C 0 1010 0 1014 1 2 1018 3 101C 1020 4 1024 5 1028 10
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 staticintcr[5] {1,2,3}; produce l’inizializzazione cr[0] 1 cr[1] 2 cr[2] 3 cr[3] 0 cr[4] 0 L’inizializzazione di array 3 11
Se vengono specificati i valori iniziali, può essere omessa la dimensione dell’array: il compilatore calcola automatica-mente il numero degli elementi sulla base del numero dei valori iniziali specificati Esempio: staticchar 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’ L’inizializzazione di array 4 12
Poiché in formato elettronico viene memorizzato e trasmesso ogni tipo di informazione (anche personale, quindi coperta da 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 Crittografia 1 13
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 Crittografia 2 14
Se il sistema di cifra, ocifrario, è ben congegnato, l’operazione didecifrazioneo 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, lachiave del cifrario Occorre notare la distinzione tra decifrazione edecrittazione, l’operazione illegittima in cui non ci si può avvalere della chiave Crittografia 3 15
Ciao 6D\5 Crittografia 4 /* Dato un carattere, ne fornisce un valore codificato */ define ILLEGAL_VAL 1 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 */ } 48=‘0’ 111=‘o’ 16
Il codiceShiftCyphercon K3 era utilizzato da Giulio Cesare Esercizio:Con K11, 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 wewillmeet 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 Crittografia 5 17
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_DIM1)) && (found 1)); c) { for(k (c1); ((k < MAX_DIM) && (found 1)); k) { if (v[c] v[k]) found 1; /* ricerca con successo */ } } return found; } 18
Esempio 2 /* Inverte gli elementi di un vettore */ #include <stdio.h> int main() { /*dichiarazioni*/ int a[10], b[10]; int i, dim10; /*input elementi*/ for(i0;i<dim;i) { printf(“inserire l’elemento a[%d]: ”,i); scanf(“%d”, &a[i]); } /*stampa vettore*/ printf(“\nIl vettore è:\n\n”); for(i0;i<dim;i) printf(“a[%d] %d\n”, i, a[i]); printf(“\n”); /*inversione vettore*/ for(i0;i<dim;i) b[i]a[dim1i]; /*stampa vettore invertito*/ printf(“Il vettore invertito è:\n\n”); for(i0;i<dim;i) printf(“b[%d] %d\n”, i, b[i]); return 0; } 19
Il C permette l’utilizzo degli operatori additivi in concomitanza con i puntatori Se p è un puntatore, l’espressione p3 è lecita ed individua il terzo oggetto che segue quello puntato da p Poiché p contiene un indirizzo, operazioni aritmetiche su pforniscono 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 p3 identifica l’indirizzo 1018 (8 byte per gli interi lunghi); se p è un puntatore a char, p3rappresenta l’indirizzo 1003 L’aritmetica dei puntatori 1 20
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 È lecito anche sottrarre un valore intero da un puntatore: il risultato è un puntatore &a[3]&a[0] 3 &a[0]&a[3] 3 L’aritmetica dei puntatori 2 21
Esempio L’aritmetica dei puntatori 3 long *p1, *p2, k; int j; char *p3; p1&k; p2p14; /* OK */ jp2p1; /* OK a j viene assegnato 4 */ jp1p2; /* OK a j viene assegnato 4 */ p1p22; /* OK tipi dei puntatori compatibili */ p3p11; /* NO tipi diversi di puntatori */ jp1p3; /* NO tipi diversi di puntatori */ 22
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; p0; /* 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 L’aritmetica dei puntatori 4 23
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 Il passaggio di puntatori come argomenti di funzione 1 24
Il passaggio di puntatori come argomenti di funzione 2 s[0]1 s[1]2 s[2]3 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); } 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 25
È possibile accedere agli elementi di un arrayattraverso… …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 Inoltre, un nome di array non seguito da un indice viene interpretato come un puntatore all’elemento iniziale dell’array L’accesso agli elementi di array mediante puntatori 1 Se p&ar[0]*(pe) è equivalente a ar[e] ar è equivalente a &ar[0] 26
Combinando le due relazioni, si ottiene la regola generale 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 diindirizzo base In considerazione del meccanismo di scaling, lo spostamento determina il numero di elementi da oltrepassare Esempio: L’accesso agli elementi di array mediante puntatori 2 ar[n] equivale a *(arn) 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 ar[2] *(ar2) 27
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; par; /* OK equivale a p&ar[0] */ arp; /* NO assegnamento su un indirizzo di array */ ∥ /* NO assegnamento su un indirizzo di puntatore */ ar; /* NO: non è possibile incrementare un indirizzo di array */ ar[1]*(p5); /* OK ar[1] è una variabile */ p; /* OK è possibile incrementare una variabile puntatore */ 28
func(ar) float*ar; { … …… } func(ar) floatar[]; { … …… } Non è specificata la dimensione perché non si alloca memoria 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 Nella funzione chiamata è necessario dichiarare l’argomento come un puntatore all’elemento iniziale di un array main() { externfloatfunc(); float x; staticfloatfarray[5]; x func(farray) /* equivalente a: func(&farray[0]) */ … …… 29
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 Il passaggio di array come argomenti di funzione 2 30
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 Il passaggio di array come argomenti di funzione 3 include <stdio.h> include <stdlib.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]; printf(“La dimensione di f_array è: %d\n”,sizeof(f_array)); print_size(f_array); exit(0); } Sulla macchina di riferimento, l’esecuzione del programma fornisce: La dimensione di f_array è: 40 La dimensione di arg è: 4 31
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: Uscita dal limite superiore di un array Essendo arun array di 10 elementi, quelli cui è possibile accedere in modo corretto hanno indice da 0 a 9: il ciclo forcontiene un errore offbyone Probabilmente verrebbe azzerata la variabile j il ciclo diventa infinito main() { int ar[10], j; for(j0; j<10; j) ar[j] 0; } 32
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 Gli algoritmi di ordinamento 1 33
Gli algoritmi di ordinamento 2 • Esistono due categorie di algoritmi di ordinamento: la classificazione è fatta in base alla complessità di calcoloe 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 34
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(nlog2n): sono più complessi, fanno spesso uso diricorsione; la convenienza del loro utilizzo si rileva quando il numero n di informazioni da ordinare è molto elevato 35
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 Bubblesort 1 36
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ù n1 passaggi Al kesimo passaggio vengono effettuati nk confronti (con eventuali scambi): almeno k1 elementi sono già ordinati Complessivamente, vengono effettuati n(n1)/2 confronti La complessità computazionale del Bubblesort è O(n2) Bubblesort 2 37
Bubblesort 3 define FALSE 0 define TRUE 1 include <stdio.h> void bubble_sort(list, list_size) int list[], list_size; { int j, temp, sortedFALSE; while(!sorted) { sorted TRUE; /* assume che list sia ordinato */ for(j0; j<list_size1; j) { if(list[j]>list[j1]) { /* almeno un elemento non è in ordine */ sorted FALSE; temp list[j]; list[j] list[j1]; list[j1] temp; } } /* fine del ciclo for */ } /* fine del ciclo while */ } Nota Nel caso migliore, quando il vettore è già ordinato, si effettua un solo passaggio, con n1 confronti e nessuno scambio La complessità scende a O(n) 38
Le stringhe 39
Unastringaè un array di caratteri terminato dal carattere nullo, corrispondente alla sequenza di escape\0 (con valore numerico associato zero) Unastringa costante(oletterale) è 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 Definizione 40
Dichiarazione e inizializzazione 1 • Per memorizzare una stringa occorre dichiarare un array di char, che può essere inizializzato con una stringa costante staticchar 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 • staticchar str[3]“quattro”; /* SCORRETTO */ • staticchar str1[3]“tre”; /* CORRETTO */ • I compilatori ANSI, generalmente, consentono di specificare una dimensione di array che non includa il carattere terminatore 41
È possibile inizializzare un puntatore a charcon una stringa costante: char*ptr = “altro testo”; si crea un array di caratteri, inizializzato ad “altro testo”, ri-servando 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 ‘a’ 1006 0FFF str ‘l’ 1007 ‘t’ 1000 ‘t’ 1008 ‘e’ 1001 ‘r’ 1009 ‘s’ 1002 ‘o’ 100A ‘t’ 1003 ‘ ’ 100B ‘o’ 1004 ‘t’ 100C ‘\0’ 1005 ‘e’ 100D ‘s’ 100E ‘t’ 100F ‘o’ 1010 ‘\0’ 1011 Dichiarazione e inizializzazione 2 ptr 1006 2000 42
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 */ *(ptr15) ‘B’; /* OK */ ptr1 “OK”; ptr1[5] ‘C’; /* opinabile a causa dell’assegnamento precedente */ *ptr2 “not OK”;/* conflitto di tipi */ ptr2 “OK”; /* opinabile perché non c’è inizializzazione */ exit(0); } 43
Occorre notare la differenza fra stringhe costanti e costanti di tipo carattere: È possibile assegnare una costante carattere all’indirizzo contenuto in un puntatore a char; è invece scorretto effettuare la stessa operazione relativamente ad una stringa Stringhe e caratteri 1 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”; char *p1; *p1 ‘a’; /* OK */ *p1 “a”; /* not OK */ char *p2; p2 ‘a’; /* not OK */ p2 “a”; /* OK */ Le stringhe sono interpretate come puntatori a carattere 44
Le inizializzazioni e gli assegnamenti non sono simmetrici; è infatti possibile scrivere char*p “string”; ma non… *p “string”; Nota:vale per inizializzazioni ed assegnamenti di tutti i tipi di dati float f; float*pf&f; /*OK */ *pf&f; /* SCORRETTO */ Stringhe e caratteri 2 Puntatore a carattere Carattere Puntatore a float Float 45
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 \0in 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) Lettura e scrittura di stringhe 1 46
Esempio: Scrivere un programma che legge una stringa dalla periferica d’ingresso di default e la stampa dieci volte Lettura e scrittura di stringhe 2 include <stdio.h> include <stdlib.h> define MAX_CHAR 80 main() { char str[MAX_CHAR]; int i; printf(“Introdurre una stringa:”); scanf(“%s”, str); for (i0; i<10; i) printf(“%s\n”, str); exit(0); } È possibile utilizzare il nome dell’array come argomento per le funzioni di I/O, in quanto puntatore all’inizio dell’array 47
Poiché nell’espressione *stri due operatori hanno la stessa precedenza ed associatività de-stra, l’espressione viene analiz-zata dal compilatore nel modo seguente: Valutazione dell’operatore di incre-mento postfisso; il compilatore passa str all’operatore successivo e lo incrementa solo al termine della valutazione dell’espressione Le funzioni di libreria per le stringhe strlen() • La funzione strlen(), restituisce il numero di caratteri che compongono una stringa (escluso il carattere nullo) int strlen(str) char *str; { int i; for (i0; *str; i) ; /* istruzione vuota */ return i; } • Valutazione dell’operatore*, applicato astr • Completamento dell’espressione, con l’incremento di str 48
L’operatore di incremento postfisso è obbligatorio: un incremento prefisso non produrrebbe un risultato corretto, dato che il primo elemento non verrebbe copiato 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 *s2vale zero (per il carattere di terminazione), si ha l’uscita dal ciclo void strcpy(s1, s2) char *s1, *s2; { while(*s2 *s1) ; /*istruzione vuota */ } 49
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 runtime restituisce 0 o 1, come valore di errore (per strstr(), 0 corrisponde all’occorrenza della seconda stringa all’inizio della prima) 50