Le Stringhe

Lo scopo della lezione di oggi e' prendere confidenza con la gestione delle stringhe.

 



Osservazione sulle stringhe

Per memorizzare singoli caratteri si usano variabili di tipo char. Le sequenze di caratteri, chiamate stringhe dagli informatici, possono quindi essere memorizzate come array di caratteri. Per agevolare il trattamento delle stringhe, il C mette a disposizione la sintassi coi doppi apici (che abbiamo già utilizzato con printf e scanf).

Vediamo alcuni esempi di dichiarazione di stringhe:

// dichiara un vettore di 100 caratteri, senza inizializzarlo
char a[100];

// dichiara e inizializza un vettore di 5 caratteri
char vocali[] = { 'a' , 'e' , 'i' , 'o' , 'u' };

// dichiara e inizializza un vettore di 5 caratteri
// (tramite i corrispondenti codici ASCII)

char vocali_da_ASCII[] = { 97 , 101 , 105 , 111 , 117};

// dichiara e inizializza una stringa
char saluto[] = "Ciao mondo!";

La sintassi dell'ultima istruzione definisce un array di lunghezza N+1, dove N è la lunghezza della stringa: l'ultima casella dell'array contiene il carattere speciale '\0'. Nell'esempio abbiamo un array di 12 posizioni.

C i a o   m o n d o ! \0 carattere
0 1 2 3 4 5 6 7 8 9 10 11 posizione


Notare la differenza tra l'uso di apici singoli e quello di apici doppi:


char x = 'a'; // ok

char y = "b"; // NO!!!

char z[] = "c"; // ok

char w[] = 'd'; // NO!!!


Sappiamo che per stampare un array è importante conoscerne la lunghezza, altrimenti potremmo accedere a zone di memoria che non erano state allocate per l'array. Lo stesso ragionamento vale per le stringhe, che sono array particolari. Come esempio è istruttivo provare a compilare ed eseguire il seguente programma:


/*
Stampa i caratteri di una stringa ed i corrispondenti codici ASCII
*/


#include <stdio.h>
#include <stdlib.h>

#define LUNGHEZZA_MAX 20

int main () {

int i;
char stringa1[] = "Prova 1";
char stringa2[LUNGHEZZA_MAX] = "Prova 2";
char stringa3[] = "Prova 3";

for (i=0 ; i<LUNGHEZZA_MAX ; i++)
printf("%c (ASCII %d)\n",stringa1[i],stringa1[i]);

printf("===========\n");

for (i=0 ; i<LUNGHEZZA_MAX ; i++)
printf("%c (ASCII %d)\n",stringa2[i],stringa2[i]);

printf("===========\n");

for (i=0 ; i<LUNGHEZZA_MAX ; i++)
printf("%c (ASCII %d)\n",stringa3[i],stringa3[i]);

return EXIT_SUCCESS;
}


Notare che una variabile di tipo carattere viene stampata come carattere se invocata con l'opzione %c, e viene stampata come numero intero (corrispondente al suo codice ASCII) se invocata con l'opzione %d.

Se mandate in esecuzione il programma dovreste notare qualcosa di particolarmente strano... che evidenzia il modo in cui sono allocate le variabili nella memoria.

Provate a rispondere alle seguenti domande:

  1. L'output è quello che vi aspettavate?
  2. Quali stranezze notate nell'output del programma?
  3. Come dovremmo correggere il programma?
  4. Come possiamo scorrere una stringa senza doverne conoscere / calcolare la lunghezza?


Per stampare una stringa possiamo utilizzare direttamente
printf, senza doverci preoccupare di scorrerla carattere per carattere:


char nome[] = "Mario Rossi";

printf("ciao %s, come stai?\n",nome);
Analogamente, con scanf possiamo leggere da tastiera direttamente una stringa, senza doverci preoccupare di leggerla un carattere per volta:

char nome[51]; 

printf("Come ti chiami? (max 50 caratteri)\n");
scanf("%s",nome);

Notare che questa volta non serve il simbolo & davanti a nome.

Attenzione: Se si immette un numero maggiore di caratteri si rischia di sovrascrivere delle zone di memoria con altri dati.

Nota: In questo modo però la stringa viene letta solo fino al primo carattere di separazione (spazio, tabulazione, new line). Per evitarlo possiamo fare un ciclo di lettura che esamina un carattere alla volta e termina quando si digita un carattere speciale (es. il tasto ESC).

int i=0;
char stringa[LUNGHEZZA_MAX+1];

scanf("%c",&stringa[i]); // leggo il primo carattere
while ( i<LUNGHEZZA_MAX && // se c'è spazio e
stringa[i]!=27 && // ... l'ultimo carattere letto non è ESC e
stringa[i]!='\0') { // ... l'ultimo carattere letto non è '\0', allora...
i++;
scanf("%c",&stringa[i]); // leggo il prossimo carattere
}
stringa[i]='\0'; // metto '\0' come ultimo carattere

Invece della primitiva scanf di lettura formattata, possiamo anche usare la funzione getchar():

int i=0;
char stringa[LUNGHEZZA_MAX+1];

stringa[i]=getchar(); // leggo il primo carattere
while ( i<LUNGHEZZA_MAX && // se c'è spazio e
stringa[i]!=27 && // ... l'ultimo carattere letto non è ESC e
stringa[i]!=EOF && // ... l'ultimo carattere letto non è EOF e
stringa[i]!='\0') { // ... l'ultimo carattere letto non è '\0', allora...
i++;
stringa[i]=getchar(); // ... leggo il prossimo carattere
}
stringa[i]='\0'; // metto '\0' come ultimo carattere


Esercitazione non guidata

<>Risolvere il seguente esercizio e spedire il solo file sorgente (suffisso .c) all'indirizzo paolo.santi@iit.cnr.it come attachment di un messaggio di posta elettronica con subject: Esercitazione Lezione 6, indicando il vostro nome, cognome e gruppo di appartenenza.


Esercizio:

Scrivere un programma mergeStringhe.c che legga due stringhe di lunghezza inferiore a 100 e costruisca una nuova stringa formata dai caratteri alternati delle due stringhe lette. La stringa risultante deve essere stampata sia in formato carattere che in formato ASCII (sequenza di numeri interi). In quest'ultimo caso, i codici ASCII corrispondenti ai vari caratteri devono essere opportunamente spaziati (altrimenti non si distinguono).

Nota: Se una stringa ha lunghezza maggiore dell'altra, alternare i caratteri finché possibile e poi copiare quello che rimane della stringa più lunga.



Torna alla HomePage del corso