Buona Programmazione in C

Transcript

Buona Programmazione in C
Buona Programmazione in C
Più lo sviluppo di un prodotto
software prosegue e più le
modifiche che devono
essere apportate divengono
complicate e dispendiose;
portando, così, ad un
aumento difficilmente
accettabile dei suoi costi di
manutenzione.
1
Buona Programmazione in C
Soluzione:
Utilizzare durante la stesura del codice le
indicazioni ed i piccoli accorgimenti
presentati di seguito per ottenere codice
C più robusto e manutenibile.
2
Buona Programmazione in C
• Note relative al “contenuto” del codice
• Note relative alla “forma” del codice
• Note relative agli errori più comuni
3
Buona Programmazione in C
Nomenclatura di variabili, costanti, funzioni
Utilizzare nomi significativi:
int a,b,c;
Non indicative del contenuto
int iPesoConfezione, iTara; Facilitano la comprensione
del codice
Per i nomi composti da più parole si devono utilizzare lettere maiuscole o il
carattere ‘_’ per facilitarne la lettura:
int peso_confezione;
Evitare di utilizzare caratteri ‘1’ troppo vicini a caratteri ‘l’ senza eventuali
caratteri di separazione, durante la lettura del codice la variabile ‘alltest’
(tutti i test) può essere facilmente confusa con ‘al1test’ (al primo test)
Il C è case sensitive, ma è buona norma non utilizzare variabili con lo
stesso nome che differiscono solo per alcune lettere minuscole o maiuscole
int variabileuno, Variabileuno, variabileUno;
4
Buona Programmazione in C
Definizione di costanti
I nomi delle costanti devono essere scritti tutti in maiuscolo, utilizzando ‘_’
nel caso di nomi composti
La definizione delle costanti deve racchiudere il valore tra due parentesi:
#define PI (3.14159)
#define PI_INCREMENTATO (PI + 5.0)
la riga di codice originale:
risultato = PI_INCREMENTATO * 2.0;
a seguito del preprocessamento diventa:
risultato = ((3.14159) + 5.0) * 2.0; 16.28318
Corretto
e non:
13.14159
Errato
che, a causa della precedenza degli operatori nel calcolo dell’espressione,
fornisce un risultato non atteso.
risultato = 3.14159 + 5.0 * 2.0;
5
Buona Programmazione in C
Creazione di funzioni
Cercare di riunire il più possibile il codice in funzioni:
- in questo modo il flusso dei dati nel programma risulta essere più
chiaro e leggibile
- modificare una funzione permette di ottenere il cambiamento in tutti i
punti in cui essa viene chiamata, con una fatica decisamente ridotta
rispetto all’implementazione della modifica in ogni punto necessario (con
il rischio, inoltre, di dimenticarsi uno o più punti)
- chiamare una funzione al posto di copiare pedestremente il codice
porta benefici anche alla dimensione del programma
Esistono direttive per cui è consigliato creare funzioni con non più di
qualche decina di righe di codice al loro interno.
6
Buona Programmazione in C
Inizializzazione
Inizializzare sempre le variabili prima di utilizzarle.
I vari compilatori C tendono ad inizializzare il contenuto delle variabili con
valori indicativi, non sempre con i valori che il programmatore si aspetta.
Un errore dovuto ad una variabile non inizializzata si verifica SOLO
durante l’esecuzione del codice, il che, certamente, non facilita il
programmatore nella soluzione del problema.
Inoltre, per rendere il codice portabile su varie piattaforme hardware, o
semplicemente per poter rigenerare il codice con una versione differente
di compilatore è importante inizializzare sempre le variabili prima di
utilizzarle.
7
Buona Programmazione in C
Puntatori e memoria dinamica
L’utilizzo dei puntatori è indispensabile nel caso in cui si debbano
scambiare grosse moli di dati tra due funzioni o nel caso in cui si debba
gestire, ad esempio, una lista dinamica. Si deve, però, porre molta
attenzione alla differenza tra l’indicatore ‘&’ ovvero l’indirizzo del
puntatore e ‘*’ ovvero il contenuto del puntatore:
int a[10];
int *pa;
int x;
dopo l’assegnamento
pa = &a[0];
si ha che pa punta all’elemento 0 di a (pa contiene l’indirizzo di a[0])
assegnando poi
x = *pa;
ottengo una copia del contenuto di a[0] in x.
8
Buona Programmazione in C
Puntatori e memoria dinamica
Quando viene utilizzata l’allocazione dinamica della memoria tramite
l’istruzione malloc (che fornisce memoria non inizializzata) o l’struzione
calloc (che fornisce memoria inizializzata a 0) occorre sempre liberare la
memoria alla fine del suo utilizzo tramite l’istruzione free.
Se questa operazione non viene fatta correttamente il programma
potrebbe, in poco tempo, occupare tutta la memoria disponibile del
sistema impedendone il suo funzionamento.
9
Buona Programmazione in C
Altre indicazioni
Testare sempre il valore di ritorno di una funzione anche se sembra
superfluo, a volte il risultato assume quel valore che proprio non ci si
aspettava…(e perciò non gestito…)
Nel caso di variabili reali (float e double) evitare di fare confronti di
uguaglianza, ma utilizzare dei range di validità. Le approssimazioni fatte
dalla macchina su cui si sta eseguendo il codice fanno sì che alcuni
confronti, formalmente corretti, diano esito negativo
Raggruppare più variabili con utilizzo omogeneo in strutture (ad esempio
la struttura impiegato che racchiude tutti i dati di un dipendente), in
questo modo è possibile manipolare i dati in modo più intuitivo, ad
esempio passando o facendo restituire ad una funzione un’intera
struttura, anziché un singolo valore
Fare particolare attenzione all’uso dell’operatore di assegnamento ‘=‘ al
posto dell’operatore di confronto ‘==‘ nei costrutti condizionali
10
Buona Programmazione in C
Stile, indentazione e commenti
Applicare lo stesso stile a tutto il progetto:
- un programma diviene maggiormente manutenibile se i nomi
assegnati alle variabili ed alle funzioni seguono sempre la stessa
regola (tutto in italiano, i nomi composti identificati dalle lettere
maiuscole interne, quantità e posizione dei commenti, …)
Utilizzo dell’indentazione:
- alcune raccomandazioni (ad esempio quella NASA) indicano che 4
spazi, e non una tabulazione, sia l’indentazione ottimale.
La tabulazione, infatti, da editor ad editor può cambiare, rendendo,
a volte, il codice illeggibile. Una errata indentazione può portare ad
un errore di valutazione dell’esecuzione del codice, ad esempio:
11
y = 1.0;
if (a < b)
x = 2.0;
else
x = 3.0;
y = 2.0 * x;
L’ultima riga, in realtà, viene
sempre eseguita e non solo
se a non è minore di b!
Buona Programmazione in C
Stile, indentazione e commenti (2)
Utilizzare molti commenti all’interno del codice: un programma risulta
spesso illeggibile anche dal proprio creatore già dopo qualche mese, se il
tempo aumenta o se la manutenzione del software deve essere condotta da
altre persone, un codice ben commentato può essere l’unica alternativa ad
un forte dispendio di energie e costi
Inserire, all’inizio del file contenente il codice, un’intestazione in cui si
indica lo scopo delle funzioni presenti in esso, la data di creazione, l’autore e
la lista delle modifiche effettuate (segnalando data, autore, descrizione della
modifica e del suo impatto sul resto del codice, motivo della modifica, …)
Inserire prima di ogni funzione un’intestazione in cui si descrive il suo
funzionamento, i parametri passati (con eventuali range di validità), il valore
restituito dalla funzione (se presente) e la lista delle modifiche effettuate su di
essa (come indicato nel punto precedente)
12
Se le modifiche sono ben documentate è facile risalire alla causa di
eventuali malfunzionamenti presentatisi a seguito di alcuni cambiamenti
Buona Programmazione in C
Stile, indentazione e commenti (3)
E’ opportuno commentare la fine del blocco di istruzioni con il nome della
struttura di controllo di cui fa parte o con il nome della funzione di cui
costituisce il corpo. In questo modo anche per più strutture annidiate è
sempre possibile verificare correttamente la posizione in cui si sta inserendo
il codice e verificare l’indentazione che si sta impostando
Mettere la corretta spaziatura all’interno del codice facilita la lettura e la
comprensione del programma:
a=funzione(b+1.0)*2.0-c;//commento
è sicuramente meno chiara di:
a = funzione(b + 1.0) * 2.0 - c;
13
// commento
Utilizzare sempre le parentesi tonde all’interno di espressioni matematiche
complesse:
- a volte una svista sulla priorità di esecuzione degli operatori
matematici può richiedere molto tempo per essere risolta. Effettuare
modifiche alla formula risulta, inoltre, enormemente facilitato se è di
immediata identificazione il punto di intervento
Buona Programmazione in C
Notazione ungherese
Notazione Ungherese:
int iLunghezza;
Unsigned int uiComando;
Utile ma senza esagerare !:
14
ad esempio mpWdnPwdd indica:
mp map
wd window
n number
p pointer
d descriptor
ovvero:
un vettore di puntatori a descrittori di finestre, indicizzato sul numero di
finestre.
A cui deve essere ancora aggiunto il nome della variabile, questo è,
infatti, solo un preambolo.
Buona Programmazione in C
Altre indicazioni
Andare a capo dopo ogni carattere di fine istruzione ‘;’ rende il codice più
leggibile:
a=5;b=7;c=10;
è sicuramente corretto, ma all’interno di una pagina di codice ritrovare a
colpo d’occhio il valore di inizializzazione di b potrebbe risultare un po’
complicato…
Utilizzare gli enum in modo da poter inserire nel codice delle stringhe
esplicative al posto di semplici valori numerici:
enum listaColori {ROSSO = 1, GIALLO, VERDE};
typedef enum listaColori coloriSemaforo;
coloriSemaforo statoSemaforo;
statoSemaforo = GIALLO;
15
Buona Programmazione in C
Errori più comuni
Al termine di ogni istruzione deve essere presente il ‘;’:
- questa dimenticanza non sempre viene segnalata dal compilatore
alla riga corretta (a volte persino con messaggi fuorvianti), infatti, il
problema può palesarsi anche alcune righe dopo l’effettiva
dimenticanza, il programmatore rischia, perciò, di perdere tempo
utile alla ricerca di un problema che in realtà non esiste
Utilizzo di un assegnamento ‘=‘ al posto di un confronto ‘==‘:
- questo errore è ancora più subdolo del precedente, in quanto non
segnalato dal compilatore (è, difatti, formalmente valido), ma
genera, durante l’esecuzione, dei comportamenti non corretti.
16
Errori nella gestione degli indici degli array:
- un array di dimensione 10 avrà un indice che parte da 0 ed
arriva a 9. Se il codice che incrementa l’indice è contenuto in un
ciclo, la possibilità che il controllo sul suo valore non sia corretto è
alta; portando, durante l’esecuzione, anche ad errori gravi come i
tentativi di scrittura in zone di memoria protetta
Buona Programmazione in C
Errori più comuni (2)
Dimenticanza delle parentesi tonde ‘()’ dopo una funzione che non
necessita parametri:
- questo tipo di errore è maggiormente frequente in sviluppatori
all’inizio della loro carriera oppure in sviluppatori provenienti da altri
linguaggi che prevedono una sintassi meno rigorosa
Essendo il C case sensitive può capitare, se il codice è stato scritto male,
che più variabili abbiano nome simile, questo porta sicuramente a confusione
sia durante lo sviluppo (è facile utilizzare erroneamente una variabile al posto
di un’altra), sia in fase di manutenzione e soluzione di bachi (verificare che la
variabile utilizzata in quella istruzione è proprio quella e non l’altra con nome
molto simile obbliga il manutentore ad effettuare ricerche approfondite e
dispendiose in termini di tempo prima di arrivare alla comprensione
dell’algoritmo implementato)
17
Buona Programmazione in C
Conclusione
Non esistono delle regole generali riconosciute da tutti, ma ogni grande
realtà crea un proprio decalogo della buona programmazione, in cui specifica
la propria visione sulla gestione dei puntatori, la possibilità o meno di gestire
memoria dinamica, come e dove mettere i commenti ed altro ancora.
In ogni caso valgono alcune regole fondamentali:
18
1.
scrivere il codice in modo chiaro, con nomi di variabili e di funzioni il più
possibile autoesplicativi
2.
usare l’indentazione per rendere il codice facilmente comprensibile
anche ad una semplice occhiata
3.
commentare tutti i punti in cui anche il programmatore stesso, a distanza
di qualche tempo, potrebbe avere difficoltà a seguire il flusso dei dati
Buona Programmazione in C
Conclusione (2)
19
4.
rendere il flusso delle operazioni da eseguire il più semplice e lineare
possibile, utilizzando le chiamate a funzioni come punto di partenza per
eventuali approfondimenti sulle operazioni eseguite e non come
passaggio obbligatorio da effettuare per una comprensione, anche solo
sommaria, dell’algoritmo
5.
utilizzare costrutti complessi solo se necessario, l’uso di due funzioni
semplici al posto di una più complessa non sempre penalizza le
prestazioni del programma, certamente, però, facilita la comprensione
del codice favorendone la sua manutenzione
Buona Programmazione in C
Esempio di funzione
La seguente funzione viene presenta nel modo peggiore possibile,
difficilmente si riesce ad intuire lo scopo di tale funzione…
long funz(int a){long b=1;while(a>1){b*=a--;}return b;}
20
Buona Programmazione in C
Esempio di funzione
Se applichiamo la corretta indentazione ed associamo alla funzione un nome
significativo possiamo ottenere qualche cosa che è sicuramente utilizzabile,
ma difficilmente manutenibile…
/* Fattoriale: ritorna il fattoriale di "a" */
long Fattoriale (int a)
{
long b = 1;
while (a > 1)
{
b *= a--;
}
return b;
}
21
Buona Programmazione in C
Esempio di funzione
Modificando il nome delle variabili ed aggiungendo alcune righe di commento
si ottiene una versione praticamente corretta, ma se, ad esempio, la si vuole
inserire in una libreria di continuo utilizzo è opportuno perdere ancora un po’
di tempo per darne una versione definitiva e maggiormente commentata…
long Fattoriale (int n)
{
/* Definisco ed inizializzo la variabile */
long lFattoriale = 1L;
while (n > 1)
{
/* Calcolo il fattoriale come prodotto dei
valori compresi fra n ed 1 */
lFattoriale *= n--;
}
return lFattoriale;
}
22
Buona Programmazione in C
Esempio di funzione
23
/***********************************************
* FUNZIONE: long Fattoriale (int n)
*
*
*
* DESCRIZIONE: Ritorna il fattoriale di "n"
*
*
*
* ARGOMENTI:
*
*
int n: valore di cui calcolare
*
*
il fattoriale
*
*
*
* RITORNO: Ritorna il valore del fattoriale
*
*
*
* MODIFICHE:
*
*
2005/02/24 - RR - Prima versione *
*
*
***********************************************/
long Fattoriale (int n)
{
/* Definisco ed inizializzo la variabile */
long lFattoriale = 1L;
while (n > 1)
{
/* Calcolo il fattoriale come prodotto dei
valori compresi fra n ed 1 */
lFattoriale *= n--;
}
return lFattoriale;
}
Buona Programmazione in C
Esempio di funzione
Il cuore della funzione di calcolo del fattoriale può essere scritta in modo
esteso per aumentarne la comprensibilità da parte di programmatori non
esperti senza inficiare pesantemente sulle prestazioni
lFattoriale *= n--;
Può essere scritto in modo esteso come:
lFattoriale = lFattoriale * n;
n = n –1;
In cui risulta molto più chiaro il susseguirsi delle operazioni effettuate, cioè
prima la moltiplicazione e poi il decremento della variabile n
24
Buona Programmazione in C
Esempio di funzione
Esistono delle gare internazionali di scrittura di brutto codice (IOCCC), il
codice sorgente fornito deve essere poco comprensibile, ma funzionante,
come il seguente roemer.c:
25
Buona Programmazione in C
Bibliografia
“Tecniche di scrittura del codice e regole per la buona programmazione” in
http://msdn.microsoft.com/library/ita/default.asp?url=/library/ITA/vsent7/html/v
xconCodingTechniquesProgrammingPractices.asp
“C Style Guide” in
http://sel.gsfc.nasa.gov/website/documents/online-doc/94-003.pdf
“C programming practical 3” in
http://schmidt.ucg.ie/cs102/prac3.pdf
“C programming” in
http://www.colorado.edu/ASEN/SrProjects/Class%20Presentations/C%20Pro
gramming.pdf
“Good programming practice” in
http://www.sussex.ac.uk/Units/physics/teaching/wfl/C/Intro_C_good_program
ming_practise.pdf
26
Buona Programmazione in C
Bibliografia (2)
“Norme di codifica” in
http://www.di.unipi.it/~ambriola/9900/is/lezioni/is-pi-1999-00-09.pdf
“Come scrivere programmi incomprensibili” in
http://www.soft-land.org/cgi-bin/doc.pl?doc=documenti/pessimo
“roemer.c” in
http://www0.us.ioccc.org/years.html
“Optimize Maintenance/Asset Management Policies” in
http://www.mt-online.com/articles/0604wireman.cfm
27