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