AngoLinux
Convenzioni di scrittura di programmi C e Assembler
- Si ringraziano i Proff. Dario Ghirimoldi e Paolo Pelizzoni per la
stesura e formattazione degli esempi -
Un programma può essere scritto in molti modi. Possono essere usati algoritmi
diversi per risolvere lo stesso problema, alcuni migliori ed altri peggiori.
Oltre agli algoritmi, anche la presentazione del programma può essere molto
diversa.
Lo stesso programma, presentato in forma diversa, può risultare più leggibile e
chiaro o meno.
In una realtà produttiva, come anche la scuola, si deve sempre tener presente che
un programma deve essere elaborato da più persone. Inoltre ogni programma ha un ciclo
di vita, per cui in periodi diversi e da persone diverse dovrà essere letto, compreso e
modificato.
Per questo motivo è indispensabile definire uno stile standard di codifica che
deve essere seguito in maniera assoluta, per garantire una trasferibilità ed una
manutenibilità nel tempo.
Quando si intraprende un nuovo progetto, normalmente, il team dei programmatori ed analisti si
accorda su di uno stile comune. Se però un programmatore si aggiunge in un secondo tempo,
deve adeguarsi allo stile già consolidato, pena grosse difficoltà di scambio con
il resto del team.
Se il team è ben organizzato le convenzioni di stile vengono anche formalizzate in un
documento.
Nel nostro caso questo è il documento di definizione dello stile.
Stile e regole per la scrittura di programma C e C++
NOTA 1: queste indicazioni non possono considerarsi complete. Verranno
di mano in mano aggiornate, man mano che nuove convenzioni verranno codificate o che si
vedrà che alcune indicazioni sono diventate obsolete o sono poco chiare.
NOTA 2: questi stili e procedure sono generali ed vanno applicati per tutta la durata
del corso. Inizialmente gli studenti conoscono solo parte del linguaggio C, quindi alcuni dei
punti, che si riferiscono a funzioni, parametri, prototipi o linguaggio C++ non saranno
applicabili. Nel momento in cui la teoria avrà introdotto le conoscenze necessarie,
ogni punto diventerà obbligatorio.
- Ogni programma va prima progettato e poi realizzato. Prima di cominciare a
scrivere definizioni o codice occorre aver deciso con precisione cosa si deve fare. Meglio se
questo processo decisionale è scritto. In particolare, se la stesura di un programma
prenderà più lezioni di laboratorio la stesura del progetto è indispensabile.
Il posto più opportuno per scriverlo è il commento iniziale del programma.
Se l'analisi viene redatta invece in un documento estrerno, sarà comunque importante
lasciare qui un sunto dell'analisi stessa ed un riferimento al documento esterno.
- Ove non sia esplicitamente richiesto o non si sia deciso di utilizzare delle classi,
non compilare come C++, ma come C standard.
- Ogni programma deve cominciare con un commento. Questo commento iniziale deve
contenere:
- Nome, cognome di tutti i componenti del gruppo
- Data di inizio della stesura del programma
- Nome del file, per garantire chiarezza nella stampa
- Testo del problema, scritto in maniera più chiara e descrittiva possibile
- Assunzioni aggiuntive o limitazioni del programma
- Eventuali note sugli algoritmi usati
in paritcolare, le note sugli algoritmi sono fondamentali per garantire la coerenza nella
stesura del programma che verrà scritto da più persone e nell'arco di più
lezioni di laboratorio.
Questo commento iniziale va scritto prima di cominciare a scrivere codice o dichiarazioni.
NOTA: negli esempi che seguono, il testo del problema non è riportato nel
commento iniziale per concisione, visto che compare nella documento subito prima del sorgente.
- Ogni sorgente dovrà contenere nell'ordine:
- Commento iniziale
- Istruzioni #include per tutti i file da includere
(attenzione! includere solo i file necessarie)
- Diciarazioni di constanti, strutture e tipi enumerativi
- Definizione di variabili
- Prototipi delle funzioni
- Definizione delle funzioni
La funzione main va messa come prima funzione [Esempio].
- I nomi delle variabili, strutture, costanti e funzioni devono essere
significativi.
- Sono da evitare nomi troppo lunghi. È opportuno cercare in linea di massima di
contenere la lunghezza dei nomi sotto i 16 caratteri.
- Se un nome deriva dalla concatenazione di più parole è preferibile
evidenziare con iniziali maiuscole le parole seguenti alla prima, piuttosto che
usare il carattere _, che aumenta la lunghezza del nome, per separarle.
- I nomi delle costanti vanno scritti tutti in maiuscolo (in questo caso usare gli _ per separare le parole, se il nome è composto)
- I commenti sulla stessa riga delle costanti vanno fatti con /* ... */ e
non con // .... perchè alcuni compilatori sostituiscono l'intera riga della costante,
compreso il commento, generando errori difficili da rintracciare.
- Se una costante non è costituita solamente da un valore semplice, ma da
un'espressione (come ad esempio #define TOTALE (A+B)) ricordarsi di mettere
tra parentesi l'espressione, come nell'esempio. A TOTALE viene sostituito
il testo che segue. Se noi, per esempio, adesso calcolassimo x=TOTALE*2,
con le parentesi risulta correttamente x=(A+B)*2, ma senza sarebbe diventato
x=A+B*2, che viene calcolato come x=A+(B*2), quindi errato.
- I nomi di strutture, variabili, funzioni o altro vanno scritti tutti in
minuscolo, salvo per le iniziali, come indicato precedentemente.
- Nelle dichiarazioni di tipi (struct, union,
enum) o nelle inizializzazioni di
variabili all'interno delle definizioni la { che apre il blocco va posta
di seguito al nome del tipo all'uguale dell'inizializzazione, sulla stessa
riga.
Nel caso delle struct o union, i vari campi andranno su
righe separate, indentati rispetto alla dichiarazione, mentre la graffa di
chiusura andrà allineata con l'inizio della dichiarazione, su di una riga a
sè stante.
Per gli enum e le inizializzazioni, se trovano posto sulla stessa
riga della dichiarazione stessa, andranno chiusi con la graffa sulla stessa riga,
altrimenti andranno indentati e su righe successive, con la graffa su una riga a sè stante, allineata con la definizione [Esempio].
- Le variabili globali vanno usate solo se strettamente indispensabile.
- Per ogni costante, ogni struttura, ogni campo di struttura ed ogni variabile locale occorre indicare, con un commento, che funzione ha.
- Nelle funzioni[Esempio]
o nei costrutti
if/else [Esempio]
for [Esempio]
while [Esempio]
switch [Esempio]
la { di apertura del blocco va di fianco al costrutto stesso, separata o meno da uno spazo.
La } di chiusura del blocco va allineata esattamente sotto la prima lettera del costtrutto.
- Nel costrutto do ... while la { di apertura va posta
sulla stessa riga del do, quindi si va a capo; la } di
chiusura va allineata sotto la d di do, seguita dal
while.
- Le dichiarazioni di variabili locali vanno allineate esattamente come le altre istruzioni del blocco
separate da una riga vuota dalle istruzioni che seguoono.
- Anche se il C++ lo consente, le variabili locali non vanno definite nel punto del
primo utilizzo (ad esempio la variabile del for definita nel
for stesso) ma all'inizio del blocco in cui sono utilizzate.
- Tutte le istruzioni contenute in un blocco vanno indentate rispetto
all'alineamento del blocco stesso (che sia la { di apertura o il
do). Per evitare di avere sorgenti che eccedano la larghezza dello schermo
è preferibile indentare i un paio di spazi, invece che di un tab.
- Nell'espressione ternaria (<condizione> ? <valore per vero> :
<valore per falso>) la condizione va sempre messa tra parentesi.
- Ove possibile l'epsressione ternaria va messa su di una sola riga. Se è
necessario, si deve andare a capo dopo il ? e/o dopo il :.
- Se l'espressione ternaria è annidata (<valore per
vero> o <valore per falso> sono a loro volta espressioni
ternarie) le espressioni ternarie interne vanno messe tra parentesi.
- Nell'espressione ternaria (<condizione> ? <valore per vero> :
<valore per falso>) la condizione va sempre messa tra parentesi.
- Se l'espressione ternaria è annidata (<valore per
vero> o <valore per falso> sono a loro volta espressioni
ternarie) le espressioni ternarie interne vanno messe tra parentesi.
- Separare le funzioni tra loro con una linea vuota e possibilmente un commento sullo scopo della funzione e dei parametri.
- Usare linee vuote all'interno delle funzioni solo quando strettamente necessario a rendere più chiara la
comprensione e non mettere mai due righe vuote consecutive.
- Una funzione deve eseguire un compito. Non è opportuno scrivere
funzioni che contengano il codice per fare molte cose assieme. Piuttosto, scrivere le funzione
per i singoli compiti ed una ulteriore funzione che svolga il compito complesso, invocando le
funzioni precedenti.
- Le funzioni comunicano con il resto del programma tramite i parametri ed il
valore di ritorno. In particolare i parametri vanno normalmente dal programma alla
funzione ed il valore di ritorno dalla funzione al programma. Se l'interazione
non è questa, occorre documentarlo molto bene. In particolare:
- Se una funzione modifica delle variabili globali
- Se una funzione ha dei parametri passati per riferimento che vengono modificati
Attenzione che i vettori e le strutture sono sempre passati per
riferimento alle funzioni, quindi occorre documentare molto bene il loro uso.
Sarebbe auspicabile usare la parola chiave const per i parametri
(strutture o vettori) in ingresso.
- Se si usano i prototipi per le funzioni, indicare sempre i nomi dei parametri,
oltre ai tipi. I tipi servono al compilatore, i nomi al programmatore. Inoltre,
commentare ogni prototipo indicando il compito della funzione ed i parametri
usati. Questo commento deve essere più sintetico di quello prima della
definizione della funzione. Si deve puntare a descrivere più la funzione che i
parametri. Meglio se il commento è breve e stà sulla stessa riga
del prototipo.
- Prima di ogni funzione, eccetto che per la main, è necessario fare un
commento, molto evidente (per individuare alla svelta le funzioni, quando le si cerca),
che contenga:
- Scopo della funzione
- Significato e valori leciti dei parametri, uno per uno
- valori di ritorno nei vari casi
- Eventuali parametri passati per riferimento modificati all'interno della funzione
- Eventuali effetti collaterali sulle variabili globali
- Occorre mettere commenti su quasi tutte le istruzioni. I commenti devono
essere significativi. Ad esempio la riga:
non ha un commento significativo, mentre la riga:
x++; // Sposta l'oggetto a destra
lo ha.
111
- I commenti devono essre sintetici. Le istruzioni devono comunque essrere la
cosa che spicca maggiormente nella lettura; il compito dei commenti è di renderle di
più comprensbili, non di sommergerle.
- I commenti devono essere aggiornati. Se modifichiamo l'uso di una variabile o
l'algoritmo di una funzione e non modifichiamo il commento che lo spiega, invece di rendere
più facile la comprensione il commento la ostacolerà.
- I commenti vanno separati di almeno uno spazio od una tabulazione
dall'istruzione che descrivono.
Se il commento, inserito dopo l'istruzione, andrà troppo a destra, lo
metterò sulla riga precedente, allineato con l'istruzione.
Cercherò per quanto possibile di incolonnare tutti i commenti in coda alle
istruzioni.
- È opportuno separare con una linea vuota i gruppi funzionali che
possono essere individuati in una funzione.
- Quando l'algoritmo utilizzato in una funzione non è evidente è
necessario documentarlo molto bene con un commento sufficientemente esteso.
Appendice sull'Assembler
- Ogni programma assembler deve cominciare con lo stesso commento dei programmi C/C++
- Le label devono essere allineate al bordo sinistro
- Le istruzioni devono essere indentate esattamente di un tabulatore
- I parametri delle istruzioni devono essere separate dall'opcode di
un tabulatore. Ad esempio:
main:
mov sp,#8fh ; Inizializza lo stach per le subroutine
- Prima di ogni subroutine vagrave posto un commento. Questo commento deve contenere:
- Un commento molto evidente, contenente il nome della funzione
- Una descrizione del funzionamento della subroutine, contenete anche eventuali
note sull'algoritmo usato
- un elenco dei parametri di ingresso (input:) che elenchi tutti i
dati usati dalla funzione, indicando in che registri/variabili globali vanno posti
- Un elenco dei valori ritornati (output:) che indichi che cosa
viene ritornato ed in quali registri/variabili globali saranno lasciati
- Un elenco dei registri/variabili globali modificate dalla subroutine
(modifica:), ed il cui valore non è stato ripristinato prima di terminare
la subroutine.
Tutte queste informazione devono comparire sempre nel commento prima di ogni
subroutine.
Le parole input:, modifica: e output: devono
comparire necessariamente nel commento, all'inizio di una riga.
[Home Page dell'IS "Fermi"]
[Indice Angolinux]
[Precedente]
[Successivo]
© Ing. Stefano Salvi - Released under GPL licence