[Cvičení 1] [Obsah] [Cvičení 3]

Cvičení 2


Témata


Struktury

Pole je homogenní datový typ, tedy všechny položky pole jsou stejné. V případě struktur jde o heterogenní datový typ. Struktura tedy obsahuje několik položek různých datových typů, které ovšem spolu logicky souvisejí. Struktura umožňuje „společné“ pojmenování všech sdružených údajů, s nimiž se potom v programu pohodlněji pracuje.

Struktura je obecně definována následujícím zápisem:
struct [jméno struktury]
{
   typ jméno_položky; 
   typ jméno_položky;
   ...
} proměnná;
Struktura je uvedena klíčovým slovem struct. Pojmenování struktury jménem je nepovinné a zpravidla se ani nepoužívá (pouze v případě, že struktura odkazuje sama na sebe). V bloku definic položek struktury se uvádějí jednotlivé položky struktury.

Ukážeme se definici proměnné bod typu struktura, která má dvě položky (x-ovou a y-ovou souřadnici):

struct
{
  float x;
  float y;
} bod;

K položkám struktury přistupujeme pomocí tečkové notace, tedy položku x a y proměnné bod nastavíme takto: bod.x = 3.5; bod.y = 4;

V paměti je proměnná bod uložena následovně

Uložení struktury

Pomocí typedef můžeme definovat uživatelský typ, např. TBod:

typedef struct
{
  float x;
  float y;
} TBod;

Proměnné typu TBod deklarujeme jako každé jiné proměnné:
TBod b1,b2;

Inicializovat položky struktury lze již při deklaraci:

TBod b1 = {3,4};

Statické pole struktur deklarujeme TBod pole_bodu[10], k položkám pole přistupujeme pole_bodu[0].x = 3;

Je možné deklarovat také ukazatel na strukturu a paměť pro data alokovat dynamicky:

TBod *b3;

b3 = (TBod*)malloc(sizeof(TBod));
...
free(b3); /* uvolnění paměti */
K položkám bodu b3 přitupujeme standardně pomocí „*“ a „.“: *b3.x = 4;. Programátoři v jazyce C používají častěji jiný zápis: b3 -> x = 4;, tedy pokud je nějaká proměnná ukazatel na strukturu, pro přístup k položkám zapisují místo hvězdičky a tečky „šipku“ (znaky minus a větší zapsané bez mezery); oba zápisy *b3.x = 4 a b3 -> x = 4 jsou v jazyce C ekvivalentní.

Strukturu můžeme předat funkci jako parametr, předává se buď struktura sama nebo ukazatel na strukturu (což je výhodnější z hlediska paměťových nároků, pokud struktura zabírá více paměti; nezapomeňme, že v případě předání struktury se na zásobník předává kopie dat). Definici funkce, která počítá vzdálenost bodu od počátku, je v tabulce 1. Tabulka ukazuje 2 funkce, u první se předává jako parametr samotná struktura, u druhé ukazatel na strukturu.

Předání samotné struktury Předání ukazatele na strukturu
double vzdalenost1(TBod b)
{
  return sqrt (b.x*b.x + b.y*b.y);
}
double vzdalenost2(TBod *b)
{
  return sqrt (b->x*b->x + b->y*b->y);

Tabulka 1: Předávání struktur funkcím

Funkce voláme se skutečnými parametry následovně:

vzdalenost1(b1);
vzdalenost1(*b3);
vzdalenost2(&b1);
vzdalenost2(b3); 
Poznámka: Uvědomte si, že pokud bychom neměli strukturu, musely by mít funkce vzdalenost1 a vzdalenost2 místo jednoho dva parametry.

Úloha 2.1

Napište program, kde nadeklarujete strukturovaný typ pro uložení bodu v prostoru. Definujte dva body (dvě proměnné), jejichž souřadnice načtete z klávesnice. Napište dvě funkce, jedna vrací vdálenost bodu od počátku (inspirujte se funkcí vzdalenost1), druhá funkce má dva body jako parametry a vrací vzdálenost mezi nimi.

Řešení:

Příklad struktury - formát BMP

Grafický formát pro uložení obrázků bmp (Bitmap) obsahuje úvodní hlavičku o délce 14 bytů. Její struktura je zobrazena v tabulce 2:

PoložkaDélka položkyPopis
typ2 byty2 znaky „BM“
velikost4 bytycelková velikost souboru
rezervováno12 bytyrezervováno pro pozdější použití, musí být na 0
rezervováno22 bytyrezervováno pro pozdější použití, musí být na 0
posun4 bytyposun obrazových dat od začátku této hlavičky (struktury)

Tabulka 2: Hlavička formátu BMP

Předpokládejme, že pracujeme na platformě Intel (uložení dat little-endian, tj. slabiky nižšího řádu jsou uloženy na nižších adresách), dále že sizeof(unsigned short)=2 a sizeof(unsigned int)=4. Strukturu, která odpovídá hlavičce souboru, nadeklarujeme následovně:

typedef struct
{
  unsigned char B;
  unsigned char M;
  unsigned int velikost;
  unsigned short res1;
  unsigned short res2;
  unsigned int posun;
} THeadBMP;
Nadeklarujeme proměnnou pro hlavičku: THeadBMP hlavicka;
Hlavičku načteme ze soubor vst, který byl otevřen pomocí fopen v binárním módu: fread((void*)&hlavicka,sizeof(THeadBMP),1,vst).
Upozornění: Překladače většinou v rámci optimalizace přístupu do paměti neukládají položky struktur těsně za sebou, ale zarovnávají je na adresy dělitelné nějakou mocninou 2, např. gcc standardně na 2, Microsoft C++ na adresy dělitelné 4. V takovém případě by mezi položkami B, M a velikost byla v paměti mezera a velikost struktury v paměti by byla větší než velikost skutečné hlavičky v souboru. Funkce fread by načetla více dat a v jednotlivých položkách by nebyly správné hodnoty. Museli bychom tedy v překladači tuto optimalizaci (zarovnání struktur - structure alignment) vypnout. Např. v gcc použijeme přepínač překladu -fpack-struct=1, popř. lze použít makro #pragma pack(1) (zarovnat na 1 byte - pack(1) je makro z důvodu kompatibility s překladači Microsoft):
#pragma pack(1)
typedef struct
{
  unsigned char B;
  unsigned char M;
  unsigned int velikost;
  unsigned short res1;
  unsigned short res2;
  unsigned int posun;
} THeadBMP;

Ukázka:

Code Gear:bitmapa.cbp, bitmapa.c, obrazek.bmp

[Cvičení 1] [Obsah] [Cvičení 3]