IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 2 - Implementace zásobníku v jazyce C

V minulé lekci, Úvod do kolekcí a datových struktur v jazyce C, jsme si uvedli kolekce a implementovali zásobník (stack).

Dnes dokončíme naši implementaci zásobníku (LIFO).

Naše jednoduchá implementace zásobníku z minula používala globální proměnnou header, která ukazovala na vrchol zásobníku. Říkali jsme si, že to není úplně šťastné řešení a že kolekci dnes ještě vylepšíme. Nejdříve ale vysvětlení, proč je používání globální proměnné header nesprávné.

Problém globální proměnné

Pokud bychom náš zásobník používali takto, fungoval by, ale mohli bychom v každém programu použít pouze jeden zásobník. Pro pochopení principu kolekce to stačilo, ale pojďme to udělat lépe. Vrchol zásobníku si budeme ukládat lokálně a každé funkci ho předáme parametrem. Tak zásobníků můžeme mít neomezené množství.

push()

Funkci push() změníme takto:

struct ITEM* push( struct ITEM **header, int new_number)
{
    struct ITEM* new_item = (struct ITEM*)malloc(sizeof(struct ITEM));
    if (new_item == NULL)
        return NULL;
    new_item->number = new_number;
    new_item->next = NULL;
    new_item->next = *header;
    *header = new_item;
    return new_item;
}

Funkci přibyl jeden parametr, header. Tento parametr je v podstatě reprezentant zásobníku, pouze přes něj se k zásobníku dostaneme. V paměti vypadá situace následovně:

Parametr header zásobníku v jazyce C - Datové struktury v jazyce C

Ty dvě hvězdičky před parametrem jsou sice zvláštní, ale říkají, že je to ukazatel na ukazatel na položku ITEM. Pokud je pro vás syntaxe ukazatelů cizí, velmi doporučuji prvně projít kurz Dynamická práce s pamětí v jazyce C.

Řetězení ukazatelů je zde nutné, protože vrchol zásobníku se ve funkcích push() a pop() mění, když se položka přidává nebo odebírá. A kdybychom si předali pouze ukazatel na hodnotu vrcholu, nemohli bychom samotný lokální ukazatel, který používá uživatel kolekce, upravit, a ukazoval by na starou adresu.

Kromě parametru se změnily pouze dva řádky, v nichž přibyly dvě hvězdičky.

pop()

Ve funkci pop() zas přibyl pouze jeden nový parametr, čtyři hvězdičky a dvě závorky:

struct ITEM* pop( struct ITEM **header )
{
    struct ITEM* old_item;
    if (*header == NULL)
        return NULL;
    old_item = *header;
    *header = (*header)->next;
    return old_item;
}

Jde pouze o to, že parametr ukazuje na ukazatel na vrchol zásobníku.

peek()

Podobně ve funkci peek() je pouze jednoduchá úprava:

struct ITEM* peek( struct ITEM *header )
{
    return header;
}

Ono to vypadá absurdně, že vrátíme to, co posíláme, v pozdějších lekcích si to vysvětlíme. Jelikož funkce peek() v zásobníku nic nemění, stačí nám pouze odkaz na vrchol zásobníku.

print_collection()

V print_collection() přibude parametr, ukazatel na vrchol zásobníku. Podobně jako peek() funkce nic nemění, tak stačí pouze odkaz na vrchol zásobníku:

void print_collection( struct ITEM* header)
{
    int i = 1;
    struct ITEM* item;
    printf("PRINT COLLECTION START:\r\n");
    item = header;
    while (item != NULL)
    {
        printf("%2d: %4d\r\n", i, item->number);
        i++;
        item = item->next;
    }
    printf("PRINT COLLECTION END.\r\n");
}

main()

Ale ve funkci main() toho musíme změnit hodně. Místo ukazatele na vrchol zásobníku totiž musíme zadat adresu ukazatele na vrchol zásobníku:

int main()
{
    struct ITEM* zasobnik = NULL;
    struct ITEM* polozka = NULL;
    push( &zasobnik, 11 );
    push( &zasobnik, 7 );
    push( &zasobnik, 8 );
    print_collection(zasobnik);
    polozka = pop(&zasobnik);
    if( polozka != NULL)
    {
        printf("Item from stack: %d\r\n", polozka->number);
        free(polozka);
    }
    else
    {
        printf("Stack is empty\r\n");
    }
    push(&zasobnik, -88);
    push(&zasobnik, 100);
    print_collection(zasobnik);
    free( pop(&zasobnik) );
    pop(&zasobnik);
    print_collection(zasobnik);
    free( pop(&zasobnik) );
    free(pop(&zasobnik));
    if( pop(&zasobnik) == NULL )
    {
        printf("Stack is empty\r\n");
    }
    print_collection(zasobnik);
    getchar();
    return 0;
}

Proměnná zasobnik je ukazatel na vrchol zásobníku. Do funkcí push(), pop() a print_collection() předáváme adresu tohoto ukazatele &zasobnik. Takto si můžeme vytvořit libovolné množství zásobníků a přistupovat k nim.

Nové funkce

Naši implementaci zásobníku můžeme dokončit ještě přidáním dvou jednoduchých, ale užitečných funkcí.

empty()

Funkce empty() vrátí 1, pokud je zásobník prázdný a 0, pokud má alespoň jednu položku:

int empty(struct ITEM* header )
{
    if (header == NULL)
        return 1;
    else
        return 0;
}

length()

Funkce length() vrátí počet položek v zásobníku:

int length(struct ITEM* header)
{
    int counter_item = 0;
    struct ITEM* item;
    item = header;
    while (item != NULL)
    {
        counter_item++;
        item = item->next;
    }
    return counter_item;
}

Funkce prochází od vrcholu zásobníku po jeho dno a při každém průchodu zvýší counter_item o jedna. Nakonec funkce counter_item vrátí jako návratovou hodnotu.

Zásobník je ke stažení v příloze níže.

V příští lekci, Implementace fronty v jazyce C, budeme implementovat jednoduchou frontu v jazyce C.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 39x (2.02 kB)
Aplikace je včetně zdrojových kódů v jazyce C

 

Předchozí článek
Úvod do kolekcí a datových struktur v jazyce C
Všechny články v sekci
Datové struktury v jazyce C
Přeskočit článek
(nedoporučujeme)
Implementace fronty v jazyce C
Článek pro vás napsal Daniel Martinko
Avatar
Uživatelské hodnocení:
5 hlasů
Aktivity