Předvánoční slevová akce PHP týden
Další šance dokončit svůj projekt a získat ceny v hodnotě 10.000 Kč! Pokračování úspěšné letní soutěže - ITnetwork winter
Využij předvánočních slev a získej od nás 20 % bodů navíc zdarma! Zároveň také probíhá PHP týden se slevou na e-learning až 80 %

Lekce 13 - Struktury v jazyce C

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Funkce v jazyce C, byla řeč o deklarování funkcí. V dnešním tutoriálu se naučíme používat neméně důležitou součást tohoto jazyka, kterou jsou tzv. struktury. Jelikož bude vytvořen opět jen jednoduchý program, tak ve výkladu funkce použity nebudou. My již víme, že kdyby byl program o chlup delší, tak bychom je určitě měli zavést.

Uložení složitých prvků

Když bychom chtěli uložit data jednoho uživatele, který se nějak jmenuje, má nějaký věk a bydlí v nějaké ulici, vytvořili bychom si s dosavadními znalostmi několik proměnných:

int main(int argc, char** argv)
{
    char jmeno[] = "Tomáš Marný";
    int vek = 33;
    char ulice[] = "Šikmá 5";

    return (EXIT_SUCCESS);
}

Jen zřídka ovšem ukládáme jen jednoho uživatele a tak jich budeme chtít mít uložených hned několik. Již víme, že když chceme uložit více prvků stejného typu, využijeme k tomu pole. Jelikož uživatel v sobě ovšem obsahuje hodnoty třech typů, museli bychom si vytvořit 3 různá pole. Jedno pro jména, druhé pro věky a třetí pro ulice. V dalším motivačním příkladu si tedy vytvořme několik polí o velikosti 10 (tedy max. pro uchování 10 uživatelů). Do pole si na zkoušku uložme data 2 uživatelů a uživatele v poli následně vypišme pomocí for cyklu.

char jmena[10][51];
int veky[10];
char ulice[10][51];

strcpy(jmena[0], "Tomáš Marný");
veky[0] = 33;
strcpy(ulice[0], "Šikmá 5");

strcpy(jmena[1], "Josef Nový");
veky[1] = 28;
strcpy(ulice[1], "Ve Svahu 8");

int i;
for (i = 0; i < 2; i++)
{
    printf("Uživatel na indexu %d\n", i);
    printf("Jméno: %s\n", jmena[i]);
    printf("Věk: %d\n", veky[i]);
    printf("Ulice: %s\n\n", ulice[i]);
}

Výsledek:

Konzolová aplikace
Uživatel na indexu 0
Jméno: Tomáš Marný
Věk: 33
Ulice: Šikmá 5

Uživatel na indexu 1
Jméno: Josef Nový
Věk: 28
Ulice: Ve Svahu 8

Program vypadá na naše poměry docela působivě. Až se naučíme ukládat data do souborů, mohli bychom podobně naprogramovat např. telefonní seznam. Přesto výše uvedený kód není ideální. Nejprve si však povšimněme několika věcí.

Zajímavá je zejména definice polí jmena a ulice. Protože chceme v každé přihrádce pole (jmena) další pole znaků (jméno), musíme vytvořit proměnnou, která je polem polí. Proto jsou uvedeny 2 hranaté závorky. V první závorce je uveden počet položek vnějšího pole, tedy počet jmen v poli. V druhé závorce je uveden počet položek pole, které je vložené v každé přihrádce. V našem případě je to 50 znaků (+1 pro \0).

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Již víme, že v céčku nemůžeme dosadit řetězcovou konstantu jinak, než při inicializaci proměnné. Proto zde musíme využít funkce strcpy(), která nám řetězec do již vytvořené proměnné zkopíruje. Výpis cyklem by měl být jasný, projíždíme jen indexy < 2, protože více osob v poli zatím nemáme.

Struktury

Abychom nemuseli tvořit tolik složitých polí, umožňuje jazyk C definovat tzv. strukturu. Jedná se o nový datový typ, který můžeme uložit do jedné proměnné, ale který uvnitř zároveň obsahuje několik prvků (někdy se mu říká záznamový typ). Vzdáleně se může podobat poli, jeho prvky ovšem nemusejí být stejného typu a místo číselně jsou pojmenované slovy. Pro evidenci uživatelů by bylo úplně nejjednodušší vytvořit si strukturu UZIVATEL. Někam do globálního prostoru, nad funkci main(), vložíme tuto definici:

typedef struct
{
    char jmeno[51];
    int vek;
    char ulice[51];
} UZIVATEL;

Ačkoli existuje, jak to již v céčku bývá, několik možností k definování struktury, budeme se držet výhradně tohoto zápisu. Strukturu definujeme jako nový datový typ pomocí klíčového slova typedef, čímž si dále usnadníme vytváření proměnných typu UZIVATEL. Následuje klíčové slovo struct. Do složených závorek deklarujeme prvky struktury jako obyčejné proměnné. Název struktury uvádíme vždy VELKÝMI PÍSMENY a za ním následuje středník.

Tělo funkce main() nyní přepíšeme do následující podoby:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char jmeno[51];
    int vek;
    char ulice[51];
} UZIVATEL;

int main(int argc, char** argv)
{
    UZIVATEL uzivatele[10];

    strcpy(uzivatele[0].jmeno, "Tomáš Marný");
    uzivatele[0].vek = 33;
    strcpy(uzivatele[0].ulice, "Šikmá 5");

    strcpy(uzivatele[1].jmeno, "Josef Nový");
    uzivatele[1].vek = 28;
    strcpy(uzivatele[1].ulice, "Ve Svahu 8");

    int i;
    for (i = 0; i < 2; i++)
    {
        printf("Uživatel na indexu %d\n", i);
        printf("Jméno: %s\n", uzivatele[i].jmeno);
        printf("Věk: %d\n", uzivatele[i].vek);
        printf("Ulice: %s\n\n", uzivatele[i].ulice);
    }
    return (EXIT_SUCCESS);
}

Celá aplikace je mnohem čitelnější. Obsahuje jednoduše jedno pole typu UZIVATEL místo předchozích 3 polí. K jednotlivým prvkům struktur v poli přistupujeme pomocí operátoru tečky. Pokud jsou struktury používány dynamicky (což ještě neumíme), používá se operátor šipky (->). Vše si ještě ukážeme dále v kurzu.

Další definice struktur

Jen pro úplnost si uveďme další způsoby, kterými lze strukturu vytvořit a to zejména proto, abyste uměli číst cizí programy. Když strukturu vytvoříme bez klíčového slova typedef, pojmenujeme ji malými písmeny:

struct uzivatel
{
    char jmeno[51];
    int vek;
    char ulice[51];
};

U proměnných typu této struktury musíme uvádět klíčové slovo struct:

struct uzivatel uzivatele[10];

Někdy se struktura definuje dokonce přímo s proměnnou:

struct
{
    char jmeno[51];
    int vek;
    char ulice[51];
}  uzivatele[10];

Tento zápis berte spíše jako odstrašující příklad. To, že je něco kratší, vůbec neznamená, že je přehlednější. Navíc strukturu takto nemůžeme použít na více místech programu.

Pozn.: Struktury samozřejmě nemusíme používat jen v polích, je to úplně normální datový typ jako např. int. Zvyšuje přehlednost a určitě ho používejte všude, kde potřebujete ukládat více hodnot, které spolu logicky souvisí.

Pozn.: Kromě struktur můžeme v céčku definovat i tzv. uniony. Ty se tváří stejně, jako struktury, ovšem proměnná typu union může mít vyplněnou jen jednu hodnotu. Každý uživatel by tedy mohl mít vyplněné buď jen jméno, jen věk nebo jen ulici. U uživatelů toto nedává příliš velký smysl, v praxi se nám teoreticky může stát, že potřebujeme ukládat prvky a každý prvek je trochu jiný. Stejně se uniony však příliš nepoužívají, je problém poznat co je kde vyplněné (proto se často balí do struktur) a my se tu s nimi nebudeme zabývat.

Ke strukturám se v kurzu ještě jednou vrátíme. Dnešní aplikaci máte níže ke stažení se zdrojovým kódem.

Tímto jste dočetli úvodní sekci seriálu do základů jazyka C. Gratuluji vám, jste seznámení s většinou jeho konstrukcí! :) Výuka samozřejmě pokračuje dále, jsou zde další cvičení a poté navazuje kurz Programování v jazyce C - Dynamická správa paměti, kde se dozvíte zejména jak v céčku dynamicky pracovat s pamětí a jak přestat být omezení délkou polí a řetězců. Protože je tato problematika poměrně komplikovaná a začátečníky často mate, byla od ni celá základní sekce odstíněná a vy jste si mohli bez zádrhelů vyzkoušet různé konstrukce céčka. Těším se na vás tedy dále, kdy začneme vytvářet reálně použitelné aplikace.


 

Stáhnout

Staženo 177x (32.65 kB)
Aplikace je včetně zdrojových kódů v jazyce c

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
9 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Předchozí článek
Funkce v jazyce C
Všechny články v sekci
Základní konstrukce jazyka C
Miniatura
Následující článek
Cvičení k 13. lekci Cečka
Aktivity (18)

 

 

Komentáře

Avatar
emedla
Člen
Avatar
emedla:3.12.2014 21:52

Prosím o vysvětlení tohoto rozdílu:

char jmena[10];
char jmena[10][20]

V prvním případě je číslo 10 délka řetězce
a ve druhém to má jaké funkce?
Napadá mě, že ve druhém případě je desítka pouze jakési číselné označení celého řetězce a druhá závorka délka. Ale asi to tak nebude, protože, když to zkusím napsat, tak to nefunguje. Hodí mi to chybu.

 
Odpovědět
3.12.2014 21:52
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na emedla
David Čápka:4.12.2014 9:42

V clanku je to přeci vysvětleno.

Odpovědět
4.12.2014 9:42
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Jirka Vavřík:24.9.2015 17:53

Dodám, že v C++ je možné mít ve struktuře i funkce :-)

Odpovědět
24.9.2015 17:53
Inteligentní nemá čas si pamatovat, inteligentní musí vymýšlet.
Avatar
David Novák
Redaktor
Avatar
Odpovídá na Jirka Vavřík
David Novák:24.9.2015 18:27

Zajímavé..

Technicky bude do struktury uložen ukazatel na danou funkci?

Odpovědět
24.9.2015 18:27
Chyba je mezi klávesnicí a židlí.
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Odpovídá na David Novák
Drahomír Hanák:24.9.2015 18:49

Pokud vím tak se v C++ dá se strukturami pracovat úplně stejně jako s třídami (včetně konstruktoru, destruktoru, přetěžování operátorů apod.). Jediný rozdíl (z pohledu jazyka) je výchozí přístup k těm vlastnostem a funkcím. Implementace je nejspíš na překladači, ale podle toho, co vím, je to stejné jako s funkcemi ve třídě, a tak s nimi nejde manipulovat jako s pointerem.

 
Odpovědět
24.9.2015 18:49
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
David Novák
Redaktor
Avatar
Odpovídá na Drahomír Hanák
David Novák:24.9.2015 19:22

To někdy, až se budu nudit, vyzkouším.. Protože ta definovaná funkce musí být uložena někde v paměti programu a z bezpečnostních důvodů se nemíchají data a kód (fce jsou tedy uloženy v sekci kódu). Když se alokuje staticky struktura, bude v datové oblasti, když dynamicky tak na hromadě nebo zásobníku.

Takže předpokládám, že technicky bude ve struktuře pouze ukazatel někam do sekce kódu, kde se bude nacházet tělo funkce. :)

Odpovědět
24.9.2015 19:22
Chyba je mezi klávesnicí a židlí.
Avatar
Odpovídá na David Novák
Lukáš Hruda (Luckin):24.9.2015 20:55

Instance struktury sama o sobě metody vůbec neobsahuje, ty se překládají v podstatě jako obyčejné funkce, které danou instanci přebírají parametrem, rozdíl je pak jenom v tom, jak si překladač ve svém object kódu funkci pojmenuje, pokud patří třídě nebo struktuře, přidá si tam nějaký identifikátor. Jinak struktura a třída je v C++ to samé, rozdíl je pouze v implicitních modifikátorech přístupu, kde struktura má implicitně všechny položky public, zatímco třída private.

 
Odpovědět
24.9.2015 20:55
Avatar
Lukáš Hruda (Luckin):24.9.2015 21:11

Samozřejmě trochu jiná je situace ve chvíli, kdy je metoda označená jako virtuální, pak instance obsahuje ještě pointer do vtable.

 
Odpovědět
24.9.2015 21:11
Avatar
DarkCoder
Člen
Avatar
DarkCoder:27. listopadu 21:40

Upřesním text článku:

Pokud se k prvkům struktury přistupuje pomocí strukturové proměnné, používá se tečkový operátor. Pokud se k prvkům struktury přistupuje pomocí ukazatelové proměnné, používá se šipkový operátor.

#include <stdio.h>

struct s_point {
        int x;
        int y;
};

int main(void) {
        struct s_point point, *ppoint;

        // prirazeni adresy struktury ukazatelove promenne
        ppoint = &point;

        // teckovy operator
        // pristup k prvkum struktury pomoci strukturove promenne
        point.x = 10;
        point.y = 20;

        // sipkovy operator
        // pristup k prvkum struktury pomoci ukazatelove promenne
        ppoint->x = 10;
        ppoint->y = 20;

        return 0;
}

Následující kód je naprosto korektní

struct {
    char jmeno[51];
    int vek;
    char ulice[51];
}  uzivatele[10];

Že chybí jméno typu struktury vůbec nevadí. Tento zápis se používá tam, kde se ví, že se bude používat pevný počet strukturových proměnných. Pro praktické programování se ale jméno typu struktury bude používat, popřípadě vytvoření nového typu pomocí typedef.

Uniony jsou jistým druhem struktur kde prvky unionu sdílejí stejný paměťový prostor. V danou chvíli lze používat pouze jednu proměnnou unionu. Smysl použití unionu je jinde nežli u struktury a jsou oblasti, kde se používá velmi často (zejména tam kde je třeba interpretovat data dvěma nebo více odlišnými způsoby, embeded systémy, práce s událostmi, různé konverze a další).

#include <stdio.h>

struct s_type {
        int x;
        double y;
};

union u_type {
        int x;
        double y;
};

int main(void) {
        struct s_type s;
        union u_type u;

        printf("Velikost int: %d bytu\n", sizeof(int));
        printf("Velikost double: %d bytu\n", sizeof(double));
        printf("Velikost strukturove promenne: %d bytu\n", sizeof(s));
        printf("Velikost unionove promenne: %d bytu\n", sizeof(u));

        return 0;
}

Výše uvedený příklad ukazuje rozdíl ve velikosti strukturové proměnné a unionové proměnné. U strukturové proměnné lze pracovat současně se všemi jejími prvky (v paměti jsou uloženy za sebou). U unionové proměnné lze pracovat pouze s jednou proměnnou (v paměti se překrývají, všechny mají nastaven offset na 0).

Velikost unionové proměnné je dána velikostí největšího prvku plus možným zarovnáním. Velikost strukturové proměnné je dána součtem prvků plus zarovnáním. Pro zjištění velikosti struktury a unionu se tak bezpodmínečně používá operátor preprocesoru sizeof (viz. příklad výše, kde velikost struktury neodpovídá součtu velikostí int a double typů).

Odpovědět
27. listopadu 21:40
"„Učíš-li se proto, aby sis zapamatoval, zapomeneš. Učíš-li se proto, abys porozuměl, zapamatuješ si."
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 9 zpráv z 9.