This is an old revision of the document!
Indicații generale teme
Codul vostru trebuie nu numai să “meargă”, ci și să fie ușor de citit/parcurs și fără potențiale erori (neverificate de sistemul de evaluare automată). Aveți mai jos câteva indicații pentru teme, împreună cu depunctările aferente pentru nerespectarea lor.
Recomandări
-
-
Dacă nu sunteți siguri pe modul vostru de indentare, folosiți
indent sau
astyle.
Pentru cei care folosesc vim, puteți porni de la acest
vimrc
Creați un README relevant care să nu reproducă enunțul sau comentariile din cod și care să ajute la înțelegerea soluției alese.
Folosiți lista de discuții dacă aveți întrebări referitoare la coding style sau best practices.
Testați-vă tema dincolo de testele automate. O aplicație reușită nu se rezumă doar la trecerea unui subset de teste.
-
Găsiți
aici o serie de framework-uri de unit testing.
Listă depunctări
Temele care nu se compilează sau nu rulează, indiferent de motive, vor primi punctaj 0; folosiți sistemul
vmchecker pentru a verifica rularea/compilarea corespunzătoare a temelor
Funcționalitate
-0.2: accesarea unor zone de memorie nevalide (care nu rezultă în mesaj de eroare de tipul “Segmentation fault”): buffer overflow, lucru neadecvat cu funcții pe șiruri; pentru depistarea accesărilor invalide puteți folosi
valgrind;
-0.2: nu se eliberează resursele după utilizarea acestora: nu se eliberează memoria alocată; leak-uri de memorie; recomandăm folosirea utilitarului
valgrind pentru a depista leak-urile de memorie din program;
-0.2: nu se eliberează resursele după utilizarea acestora: nu se închid descriptorii de fișiere (Linux), respectiv handler-ele (Windows)
-0.2: nu sunt verificate valorile de retur ale funcțiilor; recomandăm folosirea macro-ului DIE, prezent în scheletul de laborator;
-0.1: nu sunt întoarse coduri de eroare relevante;
-0.1: folosire alocare dinamică pentru o dimensiune statică (cunoscută la compilare);
-0.1: folosire buffere statice (dimensiune fixă) acolo unde este nevoie de alocare dinamică (acolo unde dimensiunea se cunoaște doar la runtime);
-0.1: mult cod duplicat;
Structura codului
-0.5: definiții de funcții non-statice în headere;
-0.5: includere a unei surse (exemplu: #include “file.c”
); se includ doar fișiere header (exemplu: #include “file.h”
);
-0.1: funcții nepublice (interne modulului) nedefinite folosind identificatorul static; variabile globale nepublice (interne modulului) nedefinite folosind identificatorul static;
Portabilitate
Build
-0.2: warning-uri de compilare; trebuie folosit, în cadrul compilării, flag-ul -Wall
pe Linux și flag-ul /W3
, cel puțin, pe Windows;
-0.1: fișier Makefile necorespunzător: o singură regulă, nu există target de clean, nu se folosesc dependențe;
Mentenabilitate
-0.1: folosire de valori hard-coded, în loc să se definească macrodefiniții;
-0.1: funcții kilometrice (> 150 de linii); dezavantajele unei funcții prea lungi: este dificil de a o citi și înțelege, este predispusă la a conține mai multe defecte, indică un design precar al programului;
-0.2: funcții “și mai kilometrice” (> 300 de linii);
-0.1: denumire neadecvată a funcțiilor sau variabilelor (do_stuff
, my_var
) ;
Coding style
Cleanup
-0.1: linii de cod comentate în surse; pentru debug recomandăm folosirea unui macro – puteți urmări
exemplul de aici;
-0.1: includerea de fișiere binare sau irelevante în arhivă;
-0.2: cod inutil; prezența unor funcții sau bucăți de cod care nu sunt folosite sau sunt inutile în cadrul programului (de exemplu, se alocă un element și apoi nu se folosește sau se execută cod după apelul exec
);
Documentație
-0.1: surse nesemnate;
-0.1: comentarii inadecvate: comentarii nerelevante sau absente;
-0.1: comentarii cu C99 (//
in loc de /* */
)
-0.2: README necorespunzător: nu se descrie soluția, se indică doar comentarii din sursă, copy paste la enunț sau la comentarii.
Cele mai frecvente greșeli întâlnite în teme
* Neverificarea valorilor de retur a apelurilor de sistem și implicit netratarea erorilor. Aici sunt două variante:
-
dacă eroarea nu este “neapărat” fatală și permite continuarea rulării programului trebuie acumulate undeva erorile și se recomandă folosirea unor
debug prints
Se preferă abordarea din primul bullet. Dacă un apel low-level de sistem pică este indicată de cele mai multe ori o problemă mai adâncă.
* Alocarea dinamică atunci când se poate folosi cea statică (mai puțin heap consumat și mai puțini ciclii!)
decât char *cmd = malloc(256 * sizeof(char));
/* Undeva într-un fișier .h */
#define MAX_CMD_BUF_SIZE 256
(...)
/* Undeva în sursele .c unde avem nevoie de buffer */
char cmd[MAX_CMD_BUF_SIZE];
* Neînchiderea/necurățarea resurselor folosite:
Memorie alocată dinamic (cu malloc/calloc
) neeliberată folosind free
- se poate verifica utilizând valgrind
Fișiere deschise cu open
care nu mai sunt închise folosind close
- se poate folosi opțiunea –track-fds
a valgrind
Procese pornite cu fork
după care nu se face wait/waitpid
- rezultă în apariția unor procese zombie
* Implementare nemodulară, într-un singur fișier. Exemplu: avem de implementat un hashtable cu bucket-urile fiind liste înlănțuite
semnăturile funcțiile, macro-uri, definiții pentru structuri utile lucrului cu liste se pun intr-un header list.h
implementările funcțiilor de mai sus se fac în sursa list.c
semnăturile funcțiile, macro-uri, funcție de hash, definiții pentru structuri utile lucrului cu hashtable se pun intr-un header hashtable.h
implementările funcțiilor de mai sus se fac în sursa hashtable.c
lucrul cu apeluri de sistem (e.g. wrappere) sau macro-uri de assert se pot pune într-un util.h
și util.c
Nu aruncați toată implementarea temei într-un singur fișier
.c
chiar dacă merge. Face imposibil de urmărit codul și pentru voi dar și pentru
asistenți sau pentru cei care peste 5 ani vor citi codul vostru. Țineți minte următorul motto:
Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.
Sursa: http://wiki.c2.com/?CodeForTheMaintainer