Errori Comuni

Ovvero: quello che avreste dovuto sapere (ma che non sapevate) e non avete mai osato chiedere…

 

Errore

Restituire, tramite una funzione, una stringa allocata come variabile locale (allocata nello stack).

Soluzione

Purtroppo, in C, una stringa è solamente un povero array di caratteri. Purtroppo, in C, gli array sono sostanzialmente puntatori costanti ad aree di memoria allocate sullo stack se gli array sono dichiarati come comuni variabili. Purtroppo, come tutte le variabili allocate sullo stack, anche gli array vengono distrutti una volta usciti dallo scope in cui sono stati dichiarati, pertanto una funzione scritta come:

 

char* getString()

{

     char str[20] = “Una stringa”;

     return str;

}

 

Ed utilizzata come:

 

{

     char *stringa;

     stringa = getString();

     printf(“%s”, stringa);

}

 

Non darà luogo a clamorosi errori di compilazione ma solo ad un semplice warning del tipo “attenzione, si sta restituendo l’indirizzo di una variabile locale…”. Peccato che, una volta terminata l’esecuzione della funzione getString, l’area di memoria riservata per l’array di caratteri (char str[20]) viene deallocata ma viene comunque correttamente restituito un puntatore a quell’area di memoria. Tale area di memoria viene poi impropriamente utilizzata successivamente per stampare un dato… che probabilmente sarà ancora lui, ma che sicuramente cambierà molto in fretta, ad esempio alla successiva allocazione di una variabile sullo stack.

Per fare le cose in modo corretto, occorre allocare dentro la getString la memoria in modo dinamico in modo da poter restituire l’indirizzo ad un’area di memoria che al termine della getString non venga deallocata. Rovescio della medaglia, una volta che si termina di utilizzare l’area di memoria allocata dalla getString, occorre deallocarla.

 

char* getString()

{

     char *str = (char*)malloc(20 * sizeof(char));

     strcpy(str, “Una stringa”);

     return str;

}

 

L’utilizzo…

 

{

char *stringa;

stringa = getString();

printf(“%s”, stringa);

free(stringa);
}

 

Errore

Copiare le stringhe con l’operatore di assegnamento; confrontare le stringhe con l’operatore di confronto.

Soluzione

Le stringhe sono array. Gli array sono puntatori costanti. Pretendere di copiare un array in un altro utilizzando l’operatore di assegnamento è pretendere di violare il fatto che gli array sono puntatori costanti. In questo caso viene segnalato un bell’errore. Al più è possibile fare in modo che un puntatore a caratteri punti alla stessa area di memoria dell’array: è stato creato un alias della stessa stringa (array) ma non una stringa diversa. Per avere due stringhe diverse l’una copia dell’altra, occorre utilizzare la funzione strcpy che si trova nella libreria string.h. Vedere le slide per il suo utilizzo.

Ovviamente, per ciò che è stato detto sopra, non è nemmeno possibile confrontare le stringhe (array) con gli operatori di confronto: si ottiene solamente di confrontare gli indirizzi di memoria dove sono allocate le stringhe e non il loro valore “lessicografico”. Per confrontare correttamente due stringhe, utilizzare la funzione strcmp che si trova nella libreria string.h. Vedere le slide per il suo utilizzo.

 

Errore

Incapsulare in una struttura una stringa allocata dinamicamente sperando che copiando la struttura venga copiata anche la stringa:

 

typedef struct

{

char *s;
} myStringType;

 

Soluzione

Quando questa struttura viene copiata, viene copiato solamente il puntatore e non viene fatta una copia dell’area di memoria puntata. Tale area è allocata in memoria dinamica e non sullo stack: al termine della copia entrambe le strutture conterranno un puntatore che punterà alla stessa area di memoria… che non è la stessa cosa che fare una copia. Se la struttura contenesse un array allora il discorso sarebbe diverso: in questo caso la struttura contiene direttamente l’area di memoria cui si riferisce l’array quindi copiare la struttura significa copiare anche l’array. La soluzione è: evitare di fare una cosa del genere poiché è del tutto inutile.

 

Errore

Nella lettura di file di testo, non verificare la terminazione del file.

Soluzione

In un file di testo, la verifica di terminazione del file deve essere fatta in modo opportuno a seconda del metodo di lettura che si sceglie. Se si utilizza la fscanf, occorre verificare che il valore di ritorno sia pari al numero di campi che si desiderano leggere (numero di formati %qualcosa presenti nella stringa di formato) e se è diverso dal valore atteso, valutare la funzione feof(fileHandler) sul file che si sta utilizzando per verificare che non si sia giunti al termine del file (tale funzione restituisce true se è stata raggiunta la fine del file). Se si utilizza la fgetc(), verificare che il carattere restituito sia diverso dal carattere EOF che rappresenta proprio la fine del file.

 

Errore

Non badare ai warnings

Soluzione

I warnings nei file che si compilano (che non contengono errori gravi) non vengono mostrati da visual studio durante un normale Build incrementale (i file già compilati e non modificati vengono solamente ri-linkati, quindi i warnings relativi ad essi non vengono visualizzati) – è possibile far ricomparire i warnings forzando la ricompilazione di tutto il progetto (Rebuild All).

 

Errore

Tutti i warnings sono uguali…

Soluzione

Non è vero! Ci sono warnings che il compilatore segnala come tali ma sono gravi come errori. Ad esempio, usare una variabile come r-value senza averla inizializzata è un errore ma è segnalato come warning anche se pregiudica il funzionamento del programma. Definire una variabile e non usarla è segnalato come warning ma non pregiudica il buon funzionamento del programma.

 

Errore

Allocare la memoria… poi non deallocarla (memory leak!).

Soluzione

Quando si allocano porzioni di memoria dinamica, è necessario ricordarsi di deallocare la memoria una volta terminato il suo utilizzo. In caso contrario è probabile l’applicazione che si sta scrivendo funzioni correttamente nel breve ma che sia estremamente instabile nel lungo termine in quanto occupa memoria senza mai liberarla. In pratica è sempre bene che malloc e free viaggino in coppia (anche se a distanza…).

 

Errore

Compilare solo cinque minuti prima della consegna. E’ praticamente impossibile consegnare un programma che sia compilabile e che funzioni.

Soluzione

Ogni volta che si scrivono poche righe di codice “consistenti”, compilare per verificare che l’applicazione si compili senza errori e senza warnings. È un’operazione che porta via pochi secondi ma che risulta essere determinante per un soddisfacente risultato finale.

 

Errore

Eseguire solo cinque minuti prima della consegna. E’ praticamente impossibile consegnare un programma che funzioni.

Soluzione

Anche in mancanza di errori e warnings è assai probabile inserire nel programma errori algoritmici che possono portare come minimo al non corretto funzionamento del programma e che, normalmente, portano anche instabilità del programma stesso (chiusure inattese). Per evitare ciò, ogni volta che si completa la codifica di una funzionalità o un algoritmo, è bene testare (almeno brevemente, ma sarebbe meglio in modo estensivo…) ciò che si è implementato per verificarne il corretto funzionamento. In generale, nei nostri semplici programmi, occorrono pochi minuti per la verifica del corretto funzionamento.

 

Errore

Non leggere tutto il testo del compito almeno una volta prima di cominciare a pensare e pretendere di aver capito il contesto del compito.

Soluzione

Leggere tutto il testo del compito almeno una volta prima di cominciare a pensare: a volte i suggerimenti sono in fondo al testo…

 

Errore

Sottovalutare l’esame di Laboratorio.

Soluzione

Prepararsi meglio. L’esame di laboratorio (e anche quello di fondamenti) è un esame come gli altri e va preparato con lo stesso impegno con cui si preparano esami che tipicamente si ritengono più impegnativi come le analisi o le fisiche… col vantaggio che la preparazione dell’esame di laboratorio può essere anche più divertente!

 

Errore

Copiare dal vicino ed essere beccati.

Soluzione

Non copiare.