Konvence pro programování v jazyce C

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 underscore_case, nikoliv camelCase

struktury a výčtové typy (enum)

položky struktury

položky výčtového typu

UPPER_UNDERSCORE_CASE s jedinečným prefixem

pojmenované konstanty

UPPER_UNDERSCORE_CASE

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 ifelse mohou začít na stejném řádku jako ukončovací závorka předchozího bloku.

Cyklus do …​ while

do {
   // ...
} while (condition);
  • Stejně jako výše, while může být za ukončovací blokovou závorkou.

switch

switch (cardinal) {
case 0:
   // ...
   break;
default:
   // ...
}
  • casedefault mají stejné odsazení jako switch.

  • Těla větví jsou na samostatných řádcích s odsazením.

  • Těla větví nejsou v blokových závorkách, pokud to není potřeba.

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é
vygenerováno 2026-02-22 15:01 upravit