Gli errori comuni nella programmazione C

Dicembre 2016

Il linguaggio C, inventato nel 1972 da Ken Thompson et Dennis Ritchie è uno dei primi linguaggi di programmazione di sistema ed è ancora oggi uno dei linguaggi più utilizzati, spesso insegnato nei corsi preparatori post diploma, poiché reputato semplice grazie alla sua sintassi.

Se creare un programma funzionale in C risulta semplice, se si ha una buona conoscenza dei puntatori, più difficile risulta creare un programma robusto. Le due ragioni principali, all'origine di alcuni programmi vulnerabili sono anche da ricercare nella scarsa competenza del programmatore e nella sua necessita di completare il programma il prima possibile (rispettandone i tempi di consegna). Le conseguenze derivate che costituiscono un programma vulnerabile sono disastrose ed è per questo che verranno qui presentati alcuni esempi degli errori più frequenti in C.


Confusione tra C e C++

Il linguaggio C e C++ sono due linguaggi distinti e, contrariamente a quanto si possa pensare, le istruzioni di C non sono valide per C++.

Cattivo prototipo del main()

I prototipi spesso visualizzati sono: main (), void main (), void main (void).

I due soli prototipi ammessi main () sono:

int main (void)
int main (int argc, char **argv) / int main (int argc, char *argv[]).

Fine del programma

Corollario dell’errore precedente, il main () deve terminare per:


return EXIT_SUCCESS; o return 0; /* EXIT_SUCCESS==0*/
return EXIT_FAILURE;


Nota Bene: l’uso di EXIT_SUCCESS ou EXIT_FAILURE necessita di #define <stdlib.h>

char *buffer; scanf("%s", buffer);

Il codice del titolo causerà sicuramente l'errore Segmentation Fault. La prima istruzione crea una variabile che conterrà un indirizzo puntato su un altro. Il problema è da riportare all'indirizzo impostato l'indirizzo e la zona tratteggiata dovrebbe essere grande abbastanza per contenere i dati. Quindi, utilizzare uno delle due linee seguenti:

char *buffer = malloc(254*sizeof *buffer);
char buffer[256]; /*se non si desidera fare un'allocazione dinamica*/

Confusione tra operatore di confronto e operatore di assegnamento

if (a = 5)
In questo caso viene assegnato il valore 5 alla variabile "a", l'operatore di assegnamento è =, invece l’operatore di confronto è doppiamente uguale ==, si dovrebbe scrivere if (a == 5)

Errore nel cast type di funzione senza argomento

int view ()
La definizione del prototipo è incompleta. Per definire un prototipo senza argomenti, bisogna specificare il parametro void (vuoto) che indica che la funzione accetta 0 argomenti:

type function (void)

Errore nel ricopiare le stringhe

char buffer[256];
buffer = "ciao";

Per copiare una stringa in un array, si usa la funzione strcpy (), o meglio strncpy (). Per esempio:

strcpy (buffer, "ciao");

Usare una funzione senza controllare la dimensione

char buffer[256];
gets(buffer) o scanf("%s", buffer);


Da non usare, poiché se l’utente inserisce una stringa molto grande, ciò causerà nella migliore delle ipotesi un blocco del programma. Meglio utilizzare:

fgets (buffer, sizeof buffer, stdin);
, si nota che fgets (), se c'è molta memoria, memorizzerà il Carriage return '\n' (a capo) nel buffer.

Cast inutile di malloc ()

char *p = (char *) malloc(...);

Nelle prime versioni di C (K & R C), era il modo giusto da intraprendere, perché malloc ritornava un char*. In C++ questo è giusto (anche se vi è l'istruzione 'new' che è più appropriata). Ma oggi, malloc() restituisce un void * che verrà promosso automaticamente al tipo ad hoc. Così, il Cast è del tutto inutile, e peggio ancora, non è consigliabile perché in alcune versioni di compilatore, se il programmatore dimentica l'include <stdlib.h> questo non verrà segnalato dal compilatore. Così, il modo corretto è:

 char * p = malloc (...); 


Nota Bene: questo appunto è da applicarsi a tutte le funzioni ritornano un void *, come calloc(), realloc(), ecc.

L’utilizzo dei float

float a;
Certamente, questo non risulta essere un errore, ma bisogna privilegiare sempre il doppio, tranne se la dimensione di memoria ha la sua importanza. Si preferirà dunque:
double a

l’utilizzo di %lf dans printf()

Esempio d’errore: double a;
printf ("%lf", a);


Un errore frequente consiste di pensare che %lf equivale a double. Se è vero, in un scanf(), NON lo è in printf (). Bisogna scrivere:

printf ("%f", a);

Mescolare dichiarazioni e istruzioni

printf ("piccolo errore");
int b;


Non si mixano le dichiarazioni e le istruzioni, ma si mettono le dichiarazioni della variabile al primo posto, seguite poi dalle istruzioni. In modo da ottenere:

int b;
printf ("piccolo errore");

L’utilizzo di una variabile nel printf()

Esempio d’errore: fgets (variabile, sizeof variabile, stdin);
print(variabile);
Questa introduce una vulnerabilità nel proprio codice, da sostituire con:

printf("%s", variabile);

Potrebbe anche interessarti :
Il documento intitolato « Gli errori comuni nella programmazione C » da CCM (it.ccm.net) è reso disponibile sotto i termini della licenza Creative Commons. È possibile copiare, modificare delle copie di questa pagina, nelle condizioni previste dalla licenza, finché questa nota appaia chiaramente.