Table of Contents

Kódovací styl

Obecně nestačí, aby kód dělal jen to co má, ale musí také být čitelný a udržitelný. Během vývoje jsou zdrojové kódy upravovány mnoha různými lidmi, kteří se ke kódu mohou vrátit třeba po mnoha týdnech. V takovém případě programátor musí celý kód přečíst a znovu jej pochopit (jinak se něco může pokazit pokazí). Je tedy zapotřebí programovat tak, aby (znovu)pochopení kódu bylo co nejrychlejší. Poté, co programátor pochopí kód, tak na něm může provádět údržbu (např. oprava bugů nebo přidání nové funkčnosti). Úprava spaghetti kódu trvá dlouho, protože změna části kódu si často vyžaduje změnu dalších částí (v nejhorším případě programátor přepisuje vše), a čím více změn tím více se musí kód testovat a ladit. Proto je dobré psát kód s průhlednou logikou rozdělenou do jednoduchých funkcí, aby jeho případná úprava vyžadovala minimální změny. Psaním čitelného a udržitelného je ideální začít co nejdříve a to například podle následujících tří základních pravidel:

  1. Konzistentní formát kódu (odsazení, názvy, mezery) zjednodušuje čtení. (lze použít formátování zdrojového kódu)
  2. Účel proměnné/funkce by měl být zřejmý hned od deklarace sebepopisným názvem nebo komentářem. (viz níže Čitelnost kódu)
  3. Dobrá struktura kódu (rozdělení do funkcí, přehledná logika, použití proměnných krátce po deklaraci) umožňuje rychlé pochopení a snadnou úpravu kódu. (viz níže Struktura kódu)

Pokračovat lze studiem:

Čitelnost kódu

Špatně! Bez komentáře nelze poznat co funkce dělá. Názvy “foo”, “nbr” a “nbr2” mohou být nesrozumitelné. Název “character” by mohl být deskriptivnější zatímco “text_which_will_be_displayed_on_terminal” je příliš dlouhý.

int foo(int nbr, int nbr2, char character)
{
    int y;
    char *text_which_will_be_displayed_on_terminal;
 
    ...
    return y;
}

Správně :-D Účel funkce a proměnných je nyní zřejmý.

/*
 * Returns result of binary operation applied on given numbers.
 * "number_1" "operator" "number_2" = y
 * 
 * number_1: number on the left side of the operator.
 * number_2: number on the right side of the operator.
 * operator: characters +,-,*, or /, if other character is given the + is set.
 * return: result of the binary operation
 */
int binary_operation(int number_1, int number_2, char operator)
{
    int y;
 
    /* Text which will be displayed on the terminal. */
    char *log;
 
    ...
    return y;
}

Špatně! Není jasné co 10123 vlastně je a zda se mají oba výskyty upravovat společně. Zároveň volání malloc není kontrolované.

int* sequence = malloc(sizeof(int) * 10123);
...
for (int i = 0; i < 10123; ++i) {
    sequence[i] = i*2;
}

Správně :-D Úpravu velikosti pole lze nyní dělat na jednom místě a promenna “sequence” je kontrolovaná.

#define ARRAY_SIZE 10123
...)
int* sequence = malloc(sizeof(int) * ARRAY_SIZE);
...
if (sequence == NULL) {
    printf("ERROR during memory allocation!");
    return;
}
...
for (int i = 0; i < ARRAY_SIZE; ++i) {
    sequence[i] = i*2;
}

Špatně!

/*
 * Creates structure Dog, which contains age, race, and year of birth. Insurance and address are not required (then set it as NULL).
 */
struct Dog* create_dog(int age, const char *race, int birth_year, const char *insurance, const char *address);

Správně :-D Kód lze přečíst bez scrollování na většině monitorů.

/*
 * Creates structure Dog, which contains age, race, and year of birth.
 * Insurance and address are not required (then set it as NULL).
 */
struct Dog* create_dog(int age, 
                      const char *race, 
                      int birth_year, 
                      const char *insurance, 
                      const char *address);

Struktura kódu

Špatně! Tři zbytečně zanořené cykly.

void main(void) {
    if (/* condition 1 */) {
        ...
        while (/* condition 2 */) { /* draws multiple rectangles */
            for (int j = 0; j < n; j++) { /* draws a rectangle */
                for (int k = 0; k < n; k++) { /* draws a row */
                    printf(".");
                }
                printf("\n");
            }
            printf("\n\n");
        }
    }
}

Správně :-D Rozděleno do krátkých funkcí. Signatura funkce je sebepopisná, stačí tedy zkrácený komentář.

/* Draws line of dots into cosnole. */
void draw_row(int row_length) {
    for (int k = 0; k < row_length; k++) {
        printf(".");
    }
}
 
/* Draws square from dots into console. */
void draw_square(int edge_length) {
    for (int j = 0; j < edge_length; j++) {
        draw_row(n);
        printf("\n");
    }
}
 
void main(void) {
    if (/* condition 1 */) {
        ...
        while (/* condition 2 */) { /* draws multiple squares */
            draw_square(n);
            printf("\n\n");
        }
    }
}

Špatně! Proměnné “i” a “tmp” přežijou svůj zamýšlený scope. Proměnná “i” může být před cyklem náhodou modifikovaná.

int i = 0, tmp;
int array[8] = {1, 2, 3, 4, 5, 6, 7, 8};
// array modifications
... 
for (i; i < 4; ++i) {
    tmp = array[i];
    array[i] = array[7 - i]
    array[7 - i] = tmp;
}
// printing array
...

Správně :-D Je zajištěno, že “i” je 0. Proměnné “tmp” a “i” existuji jen po trvání svého scope.

int array[8] = {1, 2, 3, 4, 5, 6, 7};
// array modifications
...
for (int i = 0; i < 4; ++i) {
    int tmp = array[i];
    array[i] = array[7 - i]
    array[7 - i] = tmp;
}
// printing array
...

Špatně! Proměnná “r” na které závisí ukončení cyklu je zahrabaná v kódu

int r = scanf("%d", &i);
while (r > 0) {
    // many rows of code
    ...
    r = scanf("%d", &i);
    // many rows of code
    ...
}

Správně :-D Proměnná “r” je poblíž podmínky ukončení cyklu.

while (scanf("%d", &i)) {
    // many rows of code
    ...
}
 
// alternatively
 
int r = scanf("%d", &i);
if (r == 0) {
    return -1;
}
do {
    //many rows of code
    ...
    r = scanf("%d", &i);
} while(r > 0);

Špatně! Přidání dalšího pointeru vyžaduje vícenásobnou úpravu kódu. Stejně tak i úprava výpisu chyby.

int main(void) {
    while (/* condition */) {
        ...
        if (error_input()) {
            printf("ERROR!: The user input is wrong.");
            printf("Terminating the program.")
            free(some_pointer1);
            free(some_pointer2);
            free(some_pointer3);
            return -1;
        }
        ...
        if (error_connection()) {
            printf("ERROR!: Unable to connect.");
            printf("Terminating the program.")
            free(some_pointer1);
            free(some_pointer2);
            free(some_pointer3);
            return -1;
        }
        ...
    }
    free(some_pointer1);
    free(some_pointer2);
    free(some_pointer3);
    return 0;
}

Správně :-D Případné přidání pointeru nebo hromadná úprava výpisu je nyní jednodušší.

/* Prints error message. */
void print_error(const char *message)
{
    printf("ERROR!: %s", message);
    printf("Terminating the program.");
}
 
int main(void)
{
    bool error_flag = false;
    while (/* condition */) {
        ...
        if (error_input()) {
            print_error("The user input is wrong.");
            error_flag = true;
            break;
        }
        ...
        if (error_connection()) {
            print_error("Unable to connect.");
            error_flag = true;
            break;
        }
        ...
    }
 
    free(some_pointer1);
    free(some_pointer2);
    free(some_pointer3);
    if (error_flag == true) {
        return -1;
    }
    return 0;
}