Programmare in C – Stringhe

Le stringhe in C sono assimilabili a puntatori a lunghe catene di dati. Pertanto, se si è in grado di utilizzare con dimestichezza i puntatori, le stringhe in C saranno molto semplici da utilizzare.

Una stringa in C è semplicemente un array di caratteri. La linea qui sotto dichiara un array che può contenere fino a 99 caratteri.

char str[100];

La gestione dei caratteri è tale per cui str[0] è il primo carattere della stringa, str[1] è il secondo carattere e così via. Ma perchè non si possono contenere 100 caratteri effettivi? Perchè il linguaggio C usa il terminatore di stringa per segnalare il termine di qualsiasi stringa. Il terminatore di stringa è realizzato con il valore ASCII 0 (il carattere null), che può anche essere rappresentato come '\0'.

Il concetto di terminatore di stringa è veramente molto diverso dal modo in cui gli altri linguaggi gestiscono le stringhe. Per esempio, in Pascal, ogni stringa è un array di caratteri con la lunghezza in byte che tiene conto del numero di caratteri memorizzati nell'array. Questa struttura dà al Pascal un vantaggio definitivo quando si deve calcolare la lunghezza di una stringa. Il Pascal può semplicemente restituire la lunghezza in byte, mentre il C deve contare i caratteri finchè non trova il '\0'. C è pertanto più lento del Pascal in alcuni casi, ma in altri è molto più veloce, come avremo modo di vedere in seguito.

poiché il C non fornisce un supporto per le stringhe insito nel linguaggio, tutte le funzioni di gestione delle stringhe sono implementate in librerie. Le operazioni di I/O su stringa (gets, puts, e così via) sono implementate in e un set di semplici funzioni di manipolazione sono implementate in (in alcuni sistemi strings.h)

Il fatto che le stringhe non siano native in C implica creare dei roundabout per realizzare il codice. Per esempio, supponiamo che si voglia assegnare una stringa ad un'altra stringa, ossia si voglia copiare il contenuto di una stringa in un'altra stringa. In C, come abbiamo visto nella scorsa lezione, non si può semplicemente assegnare un array ad un altro. Si deve effettuare infatti la copia elemento per elemento. La libreria delle stringhe string.h contiene una funzione chiamata strcpy per realizzare questo compito. Ecco infine uno spezzone di codice molto frequente nei programmi C:

char s[100];
strcpy(s, "hello");

Al termine dell'esecuzione delle due linee, il contenuto di s è quello nel diagramma:

La parte alta del diagramma mostra l'array con i suoi caratteri. La parte bassa del diagramma mostra il codice ASCII equivalente per i caratteri ed è proprio il modo in cui il C pensa e tratta la stringa (ossia come un array di bytes che contengono valori interi).

Il seguente codice mostra come usare strcpy in C:

#include <string.h>
int main()
{
    char s1[100],s2[100];
    strcpy(s1,"hello"); /* copia "hello" in s1 */
    strcpy(s2,s1);      /* copia s1 in s2 */
    return 0;
}

Strcpy è usata ogni qualvolta che una stringa viene inizializzata in C. Si usa la funzione strcmp contenuta nella libreria per confrontare due stringhe. Restituisce un intero che indica il risultato del confronto: 0 significa che le due stringhe sono uguali, un valore negativo significa che s1 è minore di s2 e un valore positivo significa che s1 è più grande di s2.

#include <stdio.h>
#include <string.h>
int main()
{
    char s1[100],s2[100];
     gets(s1);
    gets(s2);
    if (strcmp(s1,s2)==0)
        printf("equal\n");
    else if (strcmp(s1,s2)<0)
        printf("s1 less than s2\n");
    else
        printf("s1 greater than s2\n");
    return 0;
}

Altre funzioni comuni nella libreria stringa includono strlen, che restituisce la lunghezza di una stringa, e strcat che concatena due stringhe. Le altre funzioni disponibili nella libreria possono essere studiate e consultante facendo ricorso ai manuali.

Per essere in grado di realizzare funzioni sulle stringhe, faremo un po' di pratica analizzando due esempi: le funzioni strlen e strcpy. Qui di seguito la versione Pascal di strlen (lunghezza di una stringa)

int strlen(char s[])
{
    int x;
    x=0;
    while (s[x] != '\0')
         x=x+1;
    return(x);
}

La maggior parte dei programmatori C non amano questo approccio perchè sembra non efficiente. Utilizzano, invece, un approccio basato sui puntatori:

int strlen(char *s)
{
    int x=0;
    while (*s != '\0')
    {
        x++;
        s++;
    }
    return(x);
}

Si può abbreviare questo codice nel seguente:

int strlen(char *s)
{
    int x=0;
    while (*s++)
        x++;
    return(x);
}

Un programmatore C esperto può rendere ancora più breve questo codice.

Quando abbiamo compilato questi tre pezzi di codice su di un MicroVAX con gcc, non utilizzando alcuna ottimizzazione, e l'abbiamo eseguito 20000 volte su una stringa di 120 caratteri. Il primo codice ha impiegato una media di 12,3 secondi, il secondo 12.3 secondi e il terzo 12.9 secondi. Cosa significa? Che si dovrebbe scrivere il codice nella maniera più chiara più possibile da capire. I puntatori di solito permettono di realizzare un codice più veloce, ma in questo caso non è proprio così.

Facciamo la stessa cosa con la funzione strcpy

strcpy(char s1[],char s2[])
{
    int x;
    for (x=0; x<=strlen(s2); x++)
        s1[x]=s2[x];
}

Da notare che <= è importante nel ciclo for perchè il codice possa così copiare '\0'. Bisogna sempre assicurarsi di aver copiato anche il terminatore di stringa. La maggior parte dei bug si verifica perchè ci si dimentica del terminatore di stinga. Noterete, inoltre, che questo codice è veramente poco efficiente, perchè la funzione strlen viene chiamata ogni volta che viene eseguito il ciclo. Per risolvere questo problema, si potrebbe usare il codice seguente:

strcpy(char s1[],char s2[])
{
    int x,len;
     len=strlen(s2);
    for (x=0; x<=len; x++)
        s1[x]=s2[x];
}

La versione a puntatori è simile:

strcpy(char *s1,char *s2)
{
    while (*s2 != '\0')
    {
        *s1 = *s2;
        s1++;
        s2++;
    }
}

Che può essere compresso ulteriormente:

strcpy(char *s1,char *s2)
{
    while (*s2)
        *s1++ = *s2++;
}

La prima versione di strcpy impiega 415 secondi per copiare una stringa di 120 caratteri 10000 volte. La seconda versione impiega 14.5 secondi, la terza 9.7 secondi. In questo caso, i puntatori forniscono un'ottima possibilità di miglioramento delle performance.

Il prototipo della funzione strcpy nella libreria sulle stringhe indica che è realizzata in modo da restituire un puntatore ad una stringa
char *strcpy(char *s1,char *s2)

La maggior parte delle funzioni stringa restituisce un puntatore ad una stringa come risultato, e strcpy ritorna il valore di s1 come risultato.

Utilizzare i puntatori nell'elaborazione di stringe può a volte tradursi in un notevole miglioramento di velocità di esecuzione. Si può tenerne conto e pensare un po' prima di buttarsi nello scrivere codice. Per esempio, supponiamo di voler rimuovere gli spazi bianchi in una stringa. Si potrebbe pensare di spostare i caratteri della stringa sui caratteri bianchi per rimuoverli. Ma in C, e con i puntatori, si può evitare di effettuare lo spostamento.

#include <stdio.h>
#include <string.h>

int main()
{
    char s[100],*p;
     gets(s);
    p=s;
    while (*p==' ')
        p++;
    printf("%s\n",p);
    return 0;
}

E' sicuramente una tecnica molto più veloce, specialmente per stringhe lunghe. Scoprirete molte altre cose sulle stringhe man mano che farete pratica.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend