In questo articolo spiegherò come creare una struttura con relative funzioni associate per permettere di utilizzare una stringa che incrementi la sua dimensione appena lo spazio allocato non basta più per conservare i caratteri, utilizzando soltanto le caratteristiche del linguaggio c (niente classi quindi).
Una stringa del genere è molto utile nei casi dobbiamo lavorare con stringhe di cui non si conosce a priori la lunghezza (per es. una stringa letta da un file xml): possiamo quindi leggere i caratteri ad uno ad uno fino a quando non arriviamo alla fine della stringa, senza preoccuparci dell’allocazione della memoria.
Innanzitutto analizziamo la struttura dati che definisce la stringa autoincrementante:

// Struct containing the growing string
typedef struct
{
	// stringa
	char * text;
	// lunghezza della stringa (comprensiva dello zero terminatore)
	int length;
	// capacità della stringa
	int capacity;
} growing_string;

La variabile text sarà il posto dove effettivamente andremo ad allocare la stringa, length indicherà la lunghezza della stringa corrente mentre capacity la memoria allocata per la stringa.
Per capire meglio la differenza tra length e capacity guardiamo la prossima immagine:

Capacity-Length

Capacity rappresenta quindi lo spazio allocato in memoria per la stringa, mentre length rappresenta il numero di caratteri effettivamente occupati (comprensivo di zero terminatore). Si capisce facilmente che se si aggiungono abbastastanza caratteri da fare in modo che length sia maggiore di capacity, allora bisogna riallocare lo spazio di memoria in modo da far entrare più caratteri.

Per creare la stringa utilizzeremo il metodo:

// Crea un nuovo growing_string. Ritorna 0 se l'operazione è andata a buon fine
int new_growing_string(growing_string * str);

Che al suo interno allocherà lo spazio per la stringa così:

// alloca lo spazio per la stringa
str->text = (char *) malloc(sizeof(char) * DEFAULT_INITIAL_SIZE);

DEFAULT_INITIAL_SIZE è un #define che serve a dichiarare la dimensione iniziale della stringa quando questa viene allocata. Un altro #define è DEFAULT_GROW, che invece indica di quanti caratteri bisogna incrementare la stringa quando lo spazio già allocato in memoria non basta più.
Il metodo che si preoccupa di aggiungere i caratteri alla stringa è il seguente:

// Aggiunge un carattere alla stringa. Ritorna 0 se l'operazione è andata a buon fine
int add_char(growing_string * str, char c);

Questo metodo alloca nuovo spazio di memoria quando c’è bisogno, e aggiunge il carattere alla fine della stringa:

int add_char(growing_string * str, char c)
{
	// se ho superato la capacità della stringa
	if (str->length >= str->capacity)
	{
		//  incrementa la dimensione della stringa
	}
	// aggiungo il carattere
	str->text[str->length - 1] = (int)c;
	str->text[str->length++] = ‘\0′;
	return 0;
}

Come si vede, se la lunghezza non ha ancora superato la capacità, allora verrà immediatamente aggiunto il carattere c alla fine della stringa. Nel caso in cui la lunghezza ha superato la capacità, viene invece eseguito il codice all’interno delle graffe, che riporto di seguito:

char * backup = str->text;
int i;
// creo spazio per la nuova stringa più lunga
char * tmp = (char *) malloc(sizeof(char) * str->length + DEFAULT_GROW);
if (tmp == NULL)
	return 1;
str->text = tmp;
str->capacity = str->length + DEFAULT_GROW;
// copio la vecchia stringa
for (i = 0; i length; i++)
{
	str->text[i] = backup[i];
}
// libero la memoria della vecchia stringa
free(backup);

Viene quindi inizialmente salvato il puntatore alla vecchia stringa, assegnato uno spazio di memoria più grande alla nuova stringa, incrementata la capacità e copiata la vecchia stringa nella nuova. Alla fine viene ovviamente liberata la memoria della vecchia stringa.
Altri metodi della “classe” sono i seguenti:

// libera lo spazio allocato per la stringa
void delete_growing_string(growing_string * str)
// aggiunge nella stringa il contenuto della variabile text
int add_string(growing_string * str, char * text)

Di seguito un esempio di utilizzo, che stampa tutti i caratteri ASCII dal 32esimo al 127esimo:

int main()
{
	growing_string str;
	new_growing_string(&str);
	for (i = 32; i < 128; i++)
	{
		add_char(&str, (char)i);
	}
	printf("%s", str.text);
	delete_growing_string(&str);
	return (0);
}

Nei sorgenti troverete, oltre il file growing_string.h e growing_string.c, un main dove viene mostrato un esempio di utilizzo della struttura growing_string.

Download Growing String. Downloads: 74


Se sei interessato a questo post, potresti anche provare a leggere:

    No related posts