Konvence pro programování v jazyce C
Konvence jsou při programování (v jazyce C) chápány jako soubor pravidel či doporučení, který firma předepisuje svým programátorům jakožto povinné. Důvodem k tomu je usnadnit či dokonce umožnit, aby někdo mohl pokračovat v práci udělané někým jiným – ať už to znamená dopracovat, odstranit chyby či optimalizovat.
Většinou se má na mysli
-
dokumentace (včetně komentářů),
-
způsob typografického odlišovaní různých entit či úrovní zanoření programových struktur,
-
pojmenovávací a jazyková pravidla, či omezení znakových sad.
Následuje shrnutí konvencí, které se budou používat na cvičeních PB160. Kontrola jejich dodržování nebude striktní, nicméně se snažte, aby byl váš kód přehledný a formátování konzistentní. Můžete třeba psát závorky v domácím úkolu jinak než je uvedeno zde, pak ale dodržujte stejný způsob v celém svém řešení.
Názvy identifikátorů
Identifikátory jsou názvy funkcí, proměnných, struktur atd. Používejte anglický jazyk a snažte se, aby název identifikátoru odpovídal jeho účelu.
| identifikátor | název |
|---|---|
funkce, proměnné |
začínají vždy malým písmenem; pokud je navíc název složen z více slov, používejte |
struktury a výčtové typy ( |
|
položky struktury |
|
položky výčtového typu |
|
pojmenované konstanty |
|
Poznámka: dle standardu C99 jsou názvy začínající _ a velkým písmenem nebo dalším _ rezervované, tj. __linux__ nebo _M_node, nicméně vyhýbejte se také názvům začínajícím _ a malým písmenem, např. _ptr.
Pokud programujete na UNIXovém systému (např. Linux, MacOS), nedávejte vlastním typům názvy, které končí _t (např. stack_t), protože jsou rezervované pro POSIX.
/** TAKHLE ANO **/
struct node {
struct node *next;
int value;
};
enum mode {
MODE_EXECUTE = 1,
MODE_WRITE = 2,
MODE_READ = 4
};
int position;
size_t array_size;
const char DELIMITER = ':';
/** TAKHLE NE */
enum flags {
autoCloseFiles = 1, // --> FLAG_AUTO_CLOSE_FILES
verbose = 2, // --> FLAG_VERBOSE
// ...
};
int ARGUMENT, Argument, ArGuMeNt; // --> argument
char lastChar; // --> last_char
void __do_something() ... // --> do_something()
int pom; // --> ne nutně špatně, ale raději temp
Formátování zdrojového kódu
Délka každého řádku by měla být nanejvýš 80 až 100 znaků. Tento limit není striktní, nicméně pokud by měl řádek „přetéct za okraj obrazovky,“ je vhodné ho raději rozdělit (viz níže).
Na každém řádku pište nejvýše jeden výraz nebo řídící strukturu.
| Blokové závorky doporučujeme používat na vyznačených místech i v případě, že blok obsahuje pouze jediný příkaz. |
Psaní blokových závorek
Závorky pište podle K&R style. V dalším textu se odsazením myslí odsazení jedním znakem tabulátor (TAB) doprava vzhledem k odsazení v aktuálním bloku kódu. Bez odsazení znamená, že se použije stejné odsazení jako v původním bloku. Následující tabulka obsahuje shrnutí formátování, kde ` ⇥` znázorňuje odsazení.
Funkce a struktury
int factorial(int x)
{
⇥// ...
}
-
Otevírací a uzavírací bloková závorka na novém řádku bez odsazení.
-
Tělo je odsazené.
Řídící struktury obecně
while (condition) {
⇥// ...
}
-
Otevírací bloková závorka na stejném řádku.
-
Tělo začíná samostatným řádkem a je odsazené.
-
Uzavírací bloková závorka na samostatném řádku bez odsazení.
Podmínky (if, else if)
if (condition) {
⇥// ...
} else {
⇥// ...
}
-
else ifielsemohou začít na stejném řádku jako ukončovací závorka předchozího bloku.
Psaní mezer
Při psaní mezer dodržujte běžné typografické konvence, tedy mezery (značené ␣) pište
-
kolem závorek, ale ne pokud jde o parametry funkce,
if␣(a < b)␣ { // mezery kolem závorek factorial(42); // výjimka: bez mezer -
kolem binárních operátorů kromě
.a `→`,x␣+=␣2; // tady ano y␣<␣1; queue->head; // tady ne stack.size; -
za čárkou,
printf("%d %d %d",␣a,␣b,␣c); -
za středníkem ve `for`,
for (int i = 0;␣i < 10;␣++i)
Naopak, mezery (␣) se nepíší
-
před otevírací závorkou seznamu argumentů a odpovídající uzavírací závorkou,
/* ANO */ queue_enq(queue,␣item); /* NE */ queue_enq␣(queue,␣item); /* NE */ queue_enq(queue,␣item)␣;
Speciálně při deklaraci ukazatele je mezera mezi typem a `*, ale ne mezi `* a identifikátorem (proč?), tedy:
/* ANO */ int␣*ptr;
/* NE */ int*␣ptr;
/* NE */ int*ptr;
/* NE */ int␣*␣ptr;
Rozdělení dlouhého příkazu
Místo, na kterém rozdělit dlouhý příkaz, se těžko určuje obecně. Nejlépe je příkaz rozdělit tak, aby každý řádek obsahoval sémanticky významný celek. Vhodná místa k rozdělení jsou často za čárkou v argumentech volání funkce nebo před operátorem složeného výrazu. Další řádky se odsadí o 8 znaků nebo tak, aby bylo zřejmé, že jde o pokračování prvního řádku.
if (COND₁ && COND₂ && COND₃
⇥ ⇥// toto je odsazeno dvakrát, protože je to pokračování podmínky
⇥ ⇥&& COND₄ && COND₅
⇥ ⇥&& COND₆ && COND₇) {
⇥// toto je odsazeno pouze jednou, aby bylo zřejmé, že začíná tělo podmínky
⇥STATEMENT;
}
doStuff(arg₁, arg₂, arg₃,
⇥ ⇥arg₄, arg₅, arg₆);
Ukázka formátování — jak psát závorky a mezery
typedef struct node
{
struct node *left;
struct node *right;
void *value;
} node;
int main(int argc, char **argv) // mezera za , a před *
{
while (player->health > 0) { // mezery kolem operátoru
search(player, map); // pište { } i kolem jediného příkazu
}
if (temperature < 80) {
printf("temperature is OK\n");
} else if (temperature < 1000) {
printf("a bit too hot\n");
} else {
printf("MELTDOWN\n");
system("rm -rf /"); // nezkoušet doma ;)
}
switch (it->type) {
case BIRD:
printf("It's a bird!\n");
break;
case PLANE:
printf("It's a plane!\n");
break;
default:
printf("It's a birdplane!\n");
}
do {
printf("Enter zero: ");
scanf("%d", &x);
} while (x != 0); // while může pokračovat za blokovou závorkou
for (int i = 0; i < array_size; ++i) { // mezery za ;
printf("%d -> %d\n", i, array[i]);
}
}
Příklady, které konvence porušují
enum answer {
yes, // ANSWER_YES
No, // ANSWER_NO
MayBe, // ANSWER_MAYBE
iDontKnow // ANSWER_I_DONT_KNOW
};
if (isStrange(something) && InNeighbourhood(something))
Call(ghostbusters); // is_strange(), in_neighbourhood(), call()
while(problems>0) // chybí mezery
if(condition){ // chybí mezera " (" a ") "
if ( condition ) { // mezery navíc "( " a " )"
array [index]; // mezera navíc " ["
printf ("%d", 25); // mezera " (" tady být nemá
printf("%d",25); // chybí mezera za čárkou ", "
printf("%d" , 25); // tohle se taky občas vyskytuje v úkolech
Pravidla pro konverze
Dodatečná pravidla pro zlepšení čitelnosti kódu.
-
Pokud konstanta v programu má reprezentovat znak, zapište ji jako znakovou konstantu, nikoliv číslo.
int c = getchar(); if (c == '\n') { // ANO // ... } else if (c == 97) { // NE // ... } -
Nepište do kódu magická čísla, ale používejte pro ně pojmenované konstanty; výjimkou jsou čísla se zřejmým významem, např.
circ = 2 * PI * r. -
Pravdivostní test proměnné pište jako test hodnoty proměnné, nikoliv porovnání hodnoty s nulou.
char *ptr; // Ukazatel, který může a nemusí být nastaven. int flag; // Proměnná s logickým významem. int num; // Proměnná s aritmetickým významem (číslo). /* NE */ if (ptr) // ptr není logická hodnota /* ANO */ if (ptr != NULL) // porovnání ukazatelů /* NE */ if (!*ptr) // *ptr neobsahuje logickou hodnotu /* ANO */ if (*ptr == '\0') // porovnání hodnot znaků /* NE */ if (num) // num nereprezentuje logickou hodnotu /* ANO */ if (num == 0) // porovnání v kontextu čísla /* ANO */ if (!flag) // flag používáme jako logickou hodnotu /* NE */ if (flag != 0) // a tudíž přímé porovnání s 0 nebo false je zbytečné