Na této stránce se používá SVG a MathML.
Firefox je umí a některé další prohlížeče se pro ně dají upravit.

Datové struktury a ztotožnění


Reminiscence minulého

Napřed si zrekapitulujeme problémy s programem z minulého cvičení, tak, jak se jeví z těch programů, které jsem našel v Odevzdávárně. Ty jsou zhusta způsobeny nejasnostmi s adresováním ‑ ukazateli a poli, které jak víme, jsou synonymické.
Připomeňme, že (znakový) řetězec je souvislá oblast paměti (pole znaků), kde je alespoň jeden znak '\0'.
ukazatel na pole řetězců **char pole řetězců *char paměť pro znaky char
Pokud ono pole ukazatelů je ve funkci výstupním parametrem, pak musí být typu ***char a při jejím volání se zadává adresa onoho ukazatele na pole řetězců &poleRetezcu.
Naše konkrétní realisace (cca) byla:
ukazatel na pole řetězců pole řetězců (pole ukazatelů na pole znaků) tady jsme se pohybovali pomocí indexů paměť pro znaky x + 430 tady jsme se pohybovali pomocí ukazatelů nebo používali funkce s hlavičkami ve string.h
Neostýchejte se používat papír (nejen k hygienickým nebo obalovým účelům) a psací náčiní a kreslete si obrázky. (Když budete učit, tak tabuli.)




Struct

Chceme-li spojit (aggregate) spolu nějaké logicky sounaléžející údaje, každý jiného typu. Příkladem budiž jména a příjmení, datum a místo narození, PSČ, adresa, rodinný stav, případný partner, počet dětí a případné další údaje vztahující se k jednomu člověku. Nebo údaje konkrétího automobilu: SPZ, majitel, značka, typ, barva, čísla karoserie a motoru …
Příkladů bychom mohli uvádět více, takže ještě jeden: název země, státní zřízení, hlavní město, oficiální jazyky, počet obyvatel, rozloha atd.

V jazyku C se pro takto dohromady dané údaje používá struct. V češtině se někdy, zejména při používání jiných programovacích jazyků používá termín záznam (v Pascalu record).

Jednodušší je nadefinovat si struct jakožto typ ‑ lépe se s ní potom pracuje.

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

/**
 *  Priklad struktury a jejiho pouziti
 *
 * @file   structExamp.c
 * @author   Ales  Zlamal
 */

#define DELKA_RETEZCU 32
#define DELKA_ORIENTACNIHO_CISLA 8
#define DELKA_PRIJMENI 48

typedef struct tagData {
   unsigned char  den;
   unsigned char  mesic;
   unsigned int   rok;
} datum;

typedef struct tagAdresy {
   char          ulice[DELKA_RETEZCU];
   char          orientacniCislo[DELKA_ORIENTACNIHO_CISLA];
   unsigned int  prvniCastPSC;
   unsigned int  druhaCastPSC;
   char          obec[DELKA_RETEZCU];
   char          zeme[DELKA_RETEZCU];
} adresa;

typedef struct tagOsoby {
   char   jmena[DELKA_RETEZCU];
   char   prijmeni[DELKA_PRIJMENI];
   datum  datumNarozeni;
   char   mistoNarozeni[DELKA_RETEZCU];
   adresa bydliste;
} osoba;

/**
 * Nacte udaje o osobe
 * 
 * @param[out]  p adresa promenne osoba, jejiz polozky se nactou
 */
void nacti(osoba *p);

/**
 * Vypise udaje o osobe
 * 
 * @param[in]   name  struktura osoba
 */
void vytiskni(osoba name);

/**
 * Nacte retezec ze standardniho vstupu nejdele v zadane delce a odstrani prechod na novy radek
 *
 * @param[out]   d  pamet, kam se ma ulozit nacteny retezec
 * @param[out]   e  o jedna zvetsena maximalni delka nacitaneho retezce
 */void ctiRetez(char *d, int e);


int main()
{
    osoba zadatel;

    nacti(&zadatel);
    printf("\n\nZadano bylo:\n");
    vytiskni(zadatel);
    return EXIT_SUCCESS;
}


void ctiRetez(char *d, int e)
{
    fgets(d, e, stdin);
    *(d + strlen(d) - 1) = '\0';
}

void nacti(osoba *p)
{
    printf("Zadejte             (krestni jmen[o|a]: ");
    ctiRetez(p->jmena, DELKA_RETEZCU);
    printf("                              prijmeni: ");
    ctiRetez(p->prijmeni, DELKA_PRIJMENI);
    printf("          cisly den mesic rok narozeni: ");
    scanf(" %hhu %hhu %u", &(p->datumNarozeni.den), &(p->datumNarozeni.mesic), &(p->datumNarozeni.rok));
    getchar();
    printf("                        misto narozeni: ");
    ctiRetez(p->mistoNarozeni, DELKA_RETEZCU);
    printf("         bydliste        -       ulice: ");
    ctiRetez(p->bydliste.ulice, DELKA_RETEZCU);
    printf("         bydliste - (orientacni) cislo: ");
    ctiRetez(p->bydliste.orientacniCislo, DELKA_ORIENTACNIHO_CISLA);
    printf("         bydliste        -         PSC: ");
    scanf(" %u %u", &(p->bydliste.prvniCastPSC), &(p->bydliste.druhaCastPSC));
    getchar();
    printf("         bydliste        -        obec: ");
    ctiRetez(p->bydliste.obec, DELKA_RETEZCU);
    printf("         bydliste        -        zeme: ");
    ctiRetez(p->bydliste.zeme, DELKA_RETEZCU);
}

void vytiskni(osoba name)
{
    printf("           (krestni jmen[o|a]: %s\n", name.jmena);
    printf("                     prijmeni: %s\n", name.prijmeni);
    printf(" cisly den mesic rok narozeni: %hhu %hhu %u\n", name.datumNarozeni.den, name.datumNarozeni.mesic, name.datumNarozeni.rok);
    printf("               misto narozeni: %s\n", name.mistoNarozeni);
    printf("bydliste        -       ulice: %s\n", name.bydliste.ulice);
    printf("bydliste - (orientacni) cislo: %s\n", name.bydliste.orientacniCislo);
    printf("bydliste        -         PSC: %u %u\n", name.bydliste.prvniCastPSC, name.bydliste.druhaCastPSC);
    printf("bydliste        -        obec: %s\n", name.bydliste.obec);
    printf("bydliste        -        zeme: %s\n", name.bydliste.zeme);
}


Všimněte si, že práce se (datovou) strukturou je, pokud je definována jakožto typ, jednodušší. Používejte jmenovku (tag), přestože jsme ji zatím nepoužili. To příjde později a umožní nám (datovou) strukturu použít v jistém smyslu rekursivně.


Můžeme v (datové) struktuře používat ukazatele:

typedef struct tagOsoby {
   char   *jmena;
   char   *prijmeni;
   datum  *datumNarozeni;
   char   *mistoNarozeni;
   adresa *bydliste;
} osoba;
Pak ale budeme mít problémy, při zapisování či čtení (datových) struktur ze a do souborů.
Zapsaly by se jen adresy oněch položek a ty by nám při čtení v jiném programu byly k ničemu.
Takže to nedělejme.

Při použivání dynamické alokace paměti uvnitř (datových) struktur bychom sice principiálně mohli paměť šetřit, ale nemohli bychom využívat některé možnosti při zapisování či čtení (datových) struktur do a ze souborů.
Takže toto také nedělejme.





Napište program, kde budete v datové struktuře mít jména, příjmení a telefonní čísla. Zadáte několik osob v jejich poli a následně zadáte jméno, respective jméno a příjmení a program vypíše telefonní číslo či čísla osob se zadaným jménem anebo jménem a příjmením.






Union

V současné době a na úvodních úrovních práce s jazykem C union nebudeme s užitkem používat.
Příkladně: pokud bychom dříve chtěli používat stejná data jak pro muže tak pro ženy, kdy muži měli povinnou vojenskou presenční službu a neměli gynekologické prohlídky, tak bychom pro ušetření mohli union používat. Může potom být několik různých datových struktur na stejném místě v paměti, ikdyž v jednom okamžiku právě jen jednu jedinou určitou z nich. Musíme se potom nějak dovědět jakou: použít další položku.


Uvedeme si zde příklad použití smysluplného užití unionu. Nevíme-li jak jsou uložena čísla v procesoru na němž pracujeme, pak můžeme

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

/**
 *  Priklad unionu a jeho pouziti
 *
 * @file   unionExamp.c
 * @author   Ales  Zlamal
 */

typedef union tagNumber {
   unsigned long  longNumber;
   unsigned char  bytes[8];
} number;


int main()
{
    number n;

    printf("%zu\n", sizeof(long int));    
    n.longNumber = 0x0123456789ABCDEFL;
    printf("|                |\n|");    
    for (int i = 0; i < 8; ++i) {
        printf("%02hhX", n.bytes[i]);    
    }
    printf("|\n");    
    return EXIT_SUCCESS;
}

Vypíše-li se nám „EFCDAB8967452301”, tak máme implementován „little endian”, což je vlastní Intelu x86 nebo AMD64 / x86-64.
Vypíše-li se nám „0123456789ABCDEF”, tak máme implementován „big endian”.


Přesto nemůžete union úplně pominout: může se objevit u colloquia v „teoretických” otázkách.