View Full Version : Regole di Buona Programmazione C
Predator Hunter
12-03-2007, 15:48
Ciao a tutti
volendo esaminare un programma a oggetti e decidere se è ben fatto ho vari parametri su cui applicare metriche... coesione, accoppiamento, modularità, incapsulamento ecc
volendo fare la stessa cosa su un programma C, per esempio, cosa dovrei andare a guardare? prescindendo dalle regole tipo indentare, commentare, nomi intelligenti di variabili mi servirebbe qualcosa da cui poter estrapolare qualche metrica o, addirittura, qualche metrica già pronta.
Sapete darmi un :help: ?
thank you!
Potresti spiegarti meglio?
Nel senso...cosa ti interessa sapere di preciso? Quali sono alcune buone norme di programmazione pulita e leggibile?
mapomapo
12-03-2007, 16:46
innanzitutto vedrei i prototipi delle funzioni...un'occhiata veloce al main per vedere se è "snello" e subito un salto alle definizioni delle funz....immediatamente un esame delle variabili...vedere se sono giusti i passaggi (riferimento o valore)..vedere se sono superflui alcuni puntatori...
per il resto non credo si possa veder molto..
Vito
Predator Hunter
12-03-2007, 16:47
io ti dò il codice sorgente di un sw
e tu da quello devi vedere se è stato ben scritto. però non a livello di indentazione ma a livello di progettazione
per esempio in un OO vai a vedere le classi come sono fatte, l'incapsulamento, la modularità e tutti i vari (conosciutissimi) parametri di buona programmazione OO.
per un non OO come il C però mi trovo in difficoltà
:mc:
grazie per l'interessamento
Predator Hunter
12-03-2007, 16:47
innanzitutto vedrei i prototipi delle funzioni...un'occhiata veloce al main per vedere se è "snello" e subito un salto alle definizioni delle funz....immediatamente un esame delle variabili...vedere se sono giusti i passaggi (riferimento o valore)..vedere se sono superflui alcuni puntatori...
per il resto non credo si possa veder molto..
Vito
ti ringrazio, qualche pista me l'hai data :)
Sono proprio tordo!
Beh sì, come detto da mapomapo, controlla bene il passaggio parametri alle funzioni, ma verifica anche la correttezza del tipo di ritorno.
Aggiungerei di fare molta attenzione ai puntatori e all'uso che ne fai. Se utilizzi la new per allocare variabili dinamicamente sullo heap, ricordati che poi dovrai disalloccarle! Non è per nulla buona cosa lasciare spazzatura!
Sempre riguardo ai puntatori (e anche a ciò che fai restituire dalle funzioni), attento ai puntatori penzolanti, ovvero che puntano ad aree di memoria che vengono disallocate.
Attento anche a rispettare i limiti degli eventuali array.
In genere è da evitarsi l'utilizzo dei break o diciture simili, che possono rendere il codice più imprevedibile, incontrollabile e meno leggibile.
Di buona norma viene anche insegnato l'utilizzo di variabili di appoggio per controllare meglio alcune situazioni come per esempio l'uscita da un ciclo...anche se a volte, a mio parere, può andare un po' a scapito dell'ottimizzazione.
Altra buona cosa è appunto l'ottimizzazione del codice, ma questo è un passo in più che viene dopo (o con) la correttezza. In ogni caso, al di là dei commenti, è importante che il codice sia leggibile! L'ottimizzaione può aiutare in questo, ma a volte può anche rendere più difficoltoso il tutto.
Se mi viene in mente altro te lo dico... spero intanto di non averti detto banalità.
Ciaooo
vedere se sono giusti i passaggi (riferimento o valore).. piccola nota: il passaggio per riferimento in C non esiste, esiste per valore o per puntatore (a volte si chiama "per riferimento" quello per puntatore).
per il resto non credo si possa veder molto.. io cercherei sostanzialmente di vedere se c'è codice inutile: variabili mai utilizzate, variabili scritte ma mai lette, tipi inutilizzati, costanti inutilizzate se è supportata la keyword const, macro inutilizzate, prototipi di funzioni senza la corrispondente implementazione (occhio alle librerie esterne ovviamente), funzioni mai chiamate, code paths irraggiungibili, funzioni chiamate solamente in code paths irraggiungibili, ed opzionalmente funzioni chiamate una volta sola (se sono chiamate una volta sola che ci sta a fare quel codice in una funzione a se stante? però certi programmatori preferiscono isolare certi pezzi di codice, quindi questa la metterei come opzionale). probabilmente si può fare anche di meglio, al momento non mi viene in mente null'altro di relativamente semplice, ma volendo complicare (di molto) le cose potresti definire per ciascuna funzione esterna nota (per esempio per ciascuna API Win32) un pattern che definisca quali valori vengono ritornati in quali casi e con quali input, così da poter predire meglio certi code paths basati sui valori ritornati da tali funzioni.
piccola nota: il passaggio per riferimento in C non esiste, esiste per valore o per puntatore (a volte si chiama "per riferimento" quello per puntatore).
Veramente il passaggio "per puntatore" si è sempre chiamato per "riferimento", dai tempi immemori della nascita del C. Tant'è che ottenere un valore da un puntatore si chiama "dereferencing", inoltre un puntatore null'altro è che un riferimento vero e proprio. Passaggio per riferimento e per puntatore sono la stessa identica cosa. "Per riferimento" si riferisce al concetto, "per puntatore" si riferisce all'implementazione, ma entrambe le notazioni definiscono sempre lo stesso concetto.
Veramente il passaggio "per puntatore" si è sempre chiamato per "riferimento", dai tempi immemori della nascita del C. Tant'è che ottenere un valore da un puntatore si chiama "dereferencing", inoltre un puntatore null'altro è che un riferimento vero e proprio. Passaggio per riferimento e per puntatore sono la stessa identica cosa. "Per riferimento" si riferisce al concetto, "per puntatore" si riferisce all'implementazione, ma entrambe le notazioni definiscono sempre lo stesso concetto. non mi piace come terminologia, e non credevo che fosse così diffusa: crea confusione quando si viene a parlare di C++ e del passaggio "by reference" (quello con la &). l'uso di "reference" e "dereference" relativo ai puntatori lo conoscevo solo come verbi.
altra cosa che mi è venuta in mente per testare la qualità di un sorgente: controllare che la convenzione di scrittura sia coerente. controllare che le parentesi graffe abbiano sempre la stessa posizione e indentazione rispetto al codice precedente e successivo, controllare che nei nomi delle variabili l'iniziale sia sempre o maiuscola o minuscola, controllare opzionalmente l'uso di prefissi ungheresi (dato che si parla di C...), controllare che i nomi di macro siano tutti maiuscoli, controllare l'uso coerente delle andature a capo (in particolare controllare che un ; di fine istruzione sia sempre seguito da un fine riga), eccetera eccetera.
Predator Hunter
13-03-2007, 08:11
Grazie a tutti!!
avete confermato alcune mie idee e me ne avete date altre (preziose)
grazie 1000 davvero.
Per quanto in teoria il passaggio perpuntatore e riferimento sia qlc di diverso, a quanto ne so io, si tende a dare lo stesso significato ai due (per puntatore)
grazie ancora!!
beh, veramente non è così. il passaggio per riferimento non esiste in C, esiste solo il passaggio per valore: che poi sia di un int o di un indirizzo (ossia, un puntatore) cambia solo l'effetto, ma il passaggio è esclusivamente per valore. leggi il K&R, ora non ce l'ho sottomano, però è espressamente scritto così dai creatori del C. Ciao.
#include <stdio.h>
void swap(int * x, int * y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main(void)
{
int i = 3, j = 5;
printf("Prima di swap(): i = %d, j = %d\n", i, j);
swap(&i, &j);
printf("Dopo di swap(): i = %d, j = %d\n", i, j);
return 0;
}
Io questo lo chiamo passaggio per riferimento :rolleyes:
Non so cosa abbiano scritto K&R, comunque il loro "non esiste" significa che il C non ha una sintassi "dedicata" per il passaggio per riferimento (come ad esempio il pascal che distingue fra function e procedure) e che quindi si deve "realizzare" mediante i puntatori.
Chiamalo come ti pare, il passaggio per riferimento in C è possibile ergo esiste. Il loro "non esiste" probabilmente si riferisce a questioni implementative, che però a noi in questo caso non interessano (in fondo passare per valore un puntatore implica la non esistenza del riferimento ma la possibilità di ottenerlo in ogni caso).
In parole povere, meno teoria e piu' pratica :p
#include <stdio.h>
void swap(int * x, int * y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main(void)
{
int i = 3, j = 5;
printf("Prima di swap(): i = %d, j = %d\n", i, j);
swap(&i, &j);
printf("Dopo di swap(): i = %d, j = %d\n", i, j);
return 0;
}
Io questo lo chiamo passaggio per riferimento :rolleyes:
Non so cosa abbiano scritto K&R, comunque il loro "non esiste" significa che il C non ha una sintassi "dedicata" per il passaggio per riferimento (come ad esempio il pascal che distingue fra function e procedure) e che quindi si deve "realizzare" mediante i puntatori.
Chiamalo come ti pare, il passaggio per riferimento in C è possibile ergo esiste. Il loro "non esiste" probabilmente si riferisce a questioni implementative, che però a noi in questo caso non interessano (in fondo passare per valore un puntatore implica la non esistenza del riferimento ma la possibilità di ottenerlo in ogni caso).
In parole povere, meno teoria e piu' pratica :p
e poi quando vedi il passaggio di parametro con & in c++ come lo chiami?
passaggio per riferimento_v2.0?
e cmq tu passi il valore del puntatore, non un riferimento al puntatore. Non ci sono cazzi fra teoria e pratica.
trallallero
13-03-2007, 13:04
e poi quando vedi il passaggio di parametro con & in c++ come lo chiami?
passaggio per riferimento_v2.0?
e cmq tu passi il valore del puntatore, non un riferimento al puntatore. Non ci sono cazzi fra teoria e pratica.
vabbé, ma per distinguere questo
funzione( x );
dda questo ?
funzione( &x );
come fai ? io son d'accordo sul chiamarlo passaggio per indirizzo o per puntatore e non per riferimento per non confonderlo col C++, ma dire sempre per valore crea confusione.
EDIT: e comunque anche in C++, quando passi per riferimento, viene passato un valore. Che sia un indirizzo o un dato sempre valore é ;)
Ma non penso che l'autore del 3d voleva discutere di questo
vabbé, ma per distinguere questo
funzione( x );
dda questo ?
funzione( &x );
come fai ? allo stesso modo in cui distingui tra questi due:
void funzione(int x);
void funzione(float x); sono semplicemente tipi diversi.
EDIT: e comunque anche in C++, quando passi per riferimento, viene passato un valore. Che sia un indirizzo o un dato sempre valore é ;) sintatticamente no.
Prima di tutto premetto che quando ho seguito il corso di Programmazione 1, ci è stato detto chiaramente "studieremo la parte C di C++", infatti spesso e volentieri ci è stata negata la possibilità di sfruttare alcune caratteristiche del c++. Tuttavia abbiamo studiato sia il passaggio per valore che quello per riferimento. (mi domando allora se sia realmente possibile o no utilizzare entrambi su c)
Bene, veniamo ai due tipi di passaggio parametri...
Passaggio per Valore:
1) Il passaggio per valore consiste nel passare alla funzione un valore o anche una espressione qualunque che ritorni un valore. La funzione riceverà appunto un valore attraverso il suo parametro formale, ma le modifiche apportate su di esso non avranno alcuna ripercussione all'esterno della funzione e muoriranno all'uscita da essa, perché fatte su una variabile locale alla funzione. L'unica speranza di trasmettere qualcosa all'esterno è sfruttare il return.
int fun(int x) {
x=5;
return (x+2);
}
main() {
...
int y=10;
k = fun(y);
...
}
Alla fine del programma, y=10 e k=7, mentre x sarà morta all'uscita dalla funzione fun.
2) Sempre riguardo al passaggio per valore, è possibile passare per valore puntatori a variabili, attraverso dei quali la funzione potrà cambiare i valori delle variabili puntate. Ovviamente i parametri passati alla funzione dovranno rispettare i tipi, ovvero fornire un indirizzo!
void scambia(int *a, int *b) {
int t;
t = *a; //dereferenzio per prendere il valore di a
*a = *b; //scambio i valori delle due variabili utilizzando la dereferenziazione dei puntatori
*b=t;
}
main() {
...
int x=10, y=20;
scambia(&x, &y); //devo passare l'indirizzo delle due variabili!!!
...
}
Dopo aver chiamato la funzione scambia, le due variabili x e y avranno valori invertiti, ovvero x=20 e y=10.
Da notare che dentro scambia, a e b sono due variabili locali della funzione, definite come puntatori ad interi che ricevono l'indirizzo di un intero.
Passaggio per Riferimento:
Lo stesso effetto che si ha sfruttando il passaggio per valore di un puntatore, lo si può ottenere con il passaggio di parametri per riferimento per avere side-effect sul chiamante della funzione. Ogni modifica fatta sui parametri formali della funzione,a vrà ripercussione sui parametri attuali passati alla funzione.
void scambia(int &a, int &b) {
int t;
t = a;
a = b;
b=t;
}
main() {
...
int x=10, y=20;
scambia(x, y);
...
}
Da notare che a e b sono detti "alias" in quanto corrispondono esattamente alle variabili utilizzate durante il passaggio parametri alla funzione.
Osservazioni
Sebbene il passaggio per riferimento e quello per valore di puntatore, permettano di esercitare modifiche sulle variabili passate dal chiamante, sono in realtà due approcci molto diversi!
Tutto sta nel capire che una variabile è composta di due parti: l-valore ed r-valore. L' l-valore corrisponde all'indirizzo in memoria della variabile, ovvero ciò che ci permette di raggiungerla. L' r-valore è invece il contenuto vero e proprio della variabile.
int x=10; // r-valore=10, l-valore= un qualche indirizzo in memoria (0x****)
Quando si parla di puntatori, ecco che un puntatore è una variabile che ovviamente ha il suo l-valore, ma in cui l' r-valore contenuto in esso corrisponde ad un indirizzo in memoria di un'altra variabile, perciò un l-valore!!!
Concludendo
La differenza tra passaggio parametri per valore o per riferimento sta in fatti nel passare alla funzione un r-valore oppure un l-valore.
Nel caso del passaggio di un valore, la cosa è scontata. Nel caso del passaggio del valore di un puntatore, non si fa che passare appunto l' r-valore della variabile puntatore, che corrisponde poi ad un indirizzo in memoria.
Nel caso invece di passaggio per riferimento, si passa invece un l-valore.
Spero di essere stato esauriente... e scusate per la lunghezza :rolleyes:
trallallero
13-03-2007, 13:36
allo stesso modo in cui distingui tra questi due:
void funzione(int x);
void funzione(float x); sono semplicemente tipi diversi.
beh si, é vero ma fa molto piú phigo chiamarlo per indirizzo dai :cool:
sintatticamente no.
ma non stavate parlando di sintassi, é ovvio che quella é diversa
EDIT: e comunque anche in C++, quando passi per riferimento, viene passato un valore. Che sia un indirizzo o un dato sempre valore é ;)
Ma non penso che l'autore del 3d voleva discutere di questo
se passi per riferimento in c++, non passi il valore ma la variabile. Ha tutto un altro significato. Passi il contenitore del valore, non il valore.
e cmq tu passi il valore del puntatore, non un riferimento al puntatore. Non ci sono cazzi fra teoria e pratica.
A questo punto ti chiedo che differenza c'è fra un puntatore e un riferimento. Un puntatore non è altro che un indirizzo di memoria, ovvero un riferimento ad un blocco di memoria che contiene un dato. Un "riferimento a un puntatore" non è altro che un puntatore a puntatore, ovvero un'indirettezza multipla pari a due. Sono curioso :)
A questo punto ti chiedo che differenza c'è fra un puntatore e un riferimento. sintatticamente sono semplicemente due cose diverse. sintatticamente in C un riferimento non esiste neanche. ciò che tu riesci a trovare tra le due cose è solo una (corretta) analogia concettuale/funzionale. ma fermo rimane che parlare di riferimento in C crea confusione quando si introduce il discorso dei references in C++, e per questo sarebbe più corretto evitare le analogie concettuali e (giusto per rimanere sempre nell'ambito della più pratica e meno teoria) parlare solo in termini sintattici.
Prima di tutto premetto che quando ho seguito il corso di Programmazione 1, ci è stato detto chiaramente "studieremo la parte C di C++", infatti spesso e volentieri ci è stata negata la possibilità di sfruttare alcune caratteristiche del c++. Tuttavia abbiamo studiato sia il passaggio per valore che quello per riferimento. (mi domando allora se sia realmente possibile o no utilizzare entrambi su c) tutto questo non ha alcun barlume di senso, te ne rendi conto?? :Prrr:
A questo punto ti chiedo che differenza c'è fra un puntatore e un riferimento. Un puntatore non è altro che un indirizzo di memoria, ovvero un riferimento ad un blocco di memoria che contiene un dato. Un "riferimento a un puntatore" non è altro che un puntatore a puntatore, ovvero un'indirettezza multipla pari a due. Sono curioso :)
un puntatore è un puntatore, e lo maneggi come tale. Un riferimento a una variabile è un riferimento a una variabile e la maneggi come tale. Se per caso esistesse una variabile che contiene un valore non tramite un puntatore in qualche buffa architettura hw il passaggio per riferimento continuerebbe ad andare. E un astrazione in più.
tutto questo non ha alcun barlume di senso, te ne rendi conto?? :Prrr:
Peché non ha senso??????
Mi pare che la frase si capisca. Il C++ è una evoluzione del C...probabilmente volevano semplicemente dirci che avremmo studiato C e basta. Poi qualcuno qui scrive che in C non è possibile fare passaggio per riferimento come lo si fa in C++, allora rimango un po' perplesso perché noi studiando C abbiamo studiato anche quello...
A questo punto ti chiedo che differenza c'è fra un puntatore e un riferimento. Un puntatore non è altro che un indirizzo di memoria, ovvero un riferimento ad un blocco di memoria che contiene un dato. Un "riferimento a un puntatore" non è altro che un puntatore a puntatore, ovvero un'indirettezza multipla pari a due. Sono curioso :)
un puntatore è un puntatore, e lo maneggi come tale. Un riferimento a una variabile è un riferimento a una variabile e la maneggi come tale. Se per caso esistesse una variabile che contiene un valore non tramite un puntatore in qualche buffa architettura hw il passaggio per riferimento continuerebbe ad andare. E un astrazione in più.
Se andate a leggere il messaggio che avevo scritto io per cercare di fare chiarezza sulla questione passaggio parametri, allora può essere che trovare la soluzione che cercate... Ma a questo punto mi domando se sono stato poco chiaro.
Quando di parla di un riferimento si parla di un l-valore specifico, ovvero dell'indirizzo in memoria di una certa variabile.
Quando si parla di puntatore si parla di una variabile puntatore, ovvero di una variabile utilizzata per puntare ad altre variabili, più precisamente all'indirizzo in memoria di queste variabili. Infatti nello specifico un puntatore è una variabile che giustamente possiede il suo l-valore e il suo r-valore, ed in cui il suo r-valore contiene l' l-valore della variabile puntata.
Volendo si può dire che il puntatore contiene il riferimento ad una certa variabile. Non per niente quando si usa l'asterisco (*) prima del puntatore, si fa una cosa che si chiama dereferenziazione, ovvero si segue "la freccia" che porta fino alla variabile puntata.
Puntatori e riferimenti sono due cose diverse.
Peché non ha senso??????
Mi pare che la frase si capisca. Il C++ è una evoluzione del C...probabilmente volevano semplicemente dirci che avremmo studiato C e basta. la programmazione procedurale in C++ contiene alcune lievi differenze rispetto a quella del C sia a livello sintattico che a livello di tecnica (vedi giustappunto i passaggi per riferimento, che in C non esistono). la dicitura "sottoinsieme C del C++" è semplicemente senza senso, perché il C++ non è un sovrainsieme del C, anche se esiste un vastissimo sottoinsieme comune. al limite però si può accettare un po' alla lontana quella dicitura per intendere giustappunto questo sottinsieme comune, ma ciò che non ha veramente senso è dire che il corso prevede l'insegnamento del "sottinsieme C del C++" anziché dire che il corso prevede semplicemente l'insegnamento del C!! :rotfl: :rotfl:
vorrei tanto sapere cosa ti hanno spiegato spacciandolo per passaggio "per riferimento"... :rolleyes: :rolleyes: :rolleyes:
Poi qualcuno qui scrive che in C non è possibile fare passaggio per riferimento come lo si fa in C++, allora rimango un po' perplesso perché noi studiando C abbiamo studiato anche quello... in C il passaggio per riferimento non esiste a livello sintattico, ma lo si può simulare utilizzando puntatori. in C++ invece esiste una vera e propria specificità sintattica che si chiama reference e costituisce un livello di indirezione concettualmente (e anche funzionalmente) analogo a quello dato da un puntatore. l'unica differenza tra un puntatore e un reference è che il reference punta sempre a qualcosa, mentre il puntatore può anche risultare "dangling" o NULL.
vorrei tanto sapere cosa ti hanno spiegato spacciandolo per passaggio "per riferimento"... :rolleyes: :rolleyes: :rolleyes:
La scritta "studieremo la parte C del C++" è nella prima pagina delle slides forniteci dal prof che ha tenuto il corso :D ....:doh: In ogni caso, che si tratti o no di una gaf, la competenza del docente in questo campo è a dir poco elevata!
Ciò che abbiamo studiato in termini di passaggio parametri è proprio ciò che ho cercato di riassumere qualche messaggio fa...leggetelo!
Le definizioni di R ed L -Valore aiutano molto a comprendere i suddetti meccanismi, oltre a definire appunto la differenza tra puntatore e riferimento. Ecco appunto perché un puntatore può essere nullo o penzolante (dangling), mentre invece un reference non può esserlo.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.