[Obsah] |
V jazyce C existuje šest bitových operátorů. Přehled uvádí tabulka 1:
Operátor | Operace |
& | Bitový součin (AND) |
| | Bitový součet (OR) |
^ | Exlusivní bitový součet - výlučné NEBO (XOR) |
~ | Bitová negace |
<< | Bitový posuv vlevo |
>> | Bitový posuv vpravo |
Funkci bitových operátorů ukážeme na příkladě:
unsigned char a = 0x85; /* 133 desitkove, 10000101 dvojkove */ unsigned char b = 0x46; /* 70 desitkove, 01000110 dvojkove */ unsigned char c,d,e,f,g,h; c = a & b; d = a | b; e = a ^ b; f = ~ a; g = a << 2; h = b >> 3;Výsledky operací součinu, součtu, exlusivního součtu a negace jsou zřejmé:
&
(AND)|
(OR)^
(XOR)~
(NOT)a =
1 0 0 0 0 1 0 1 a =
1 0 0 0 0 1 0 1 a =
1 0 0 0 0 1 0 1 a =
1 0 0 0 0 1 0 1 b =
0 1 0 0 0 1 1 0 b =
0 1 0 0 0 1 1 0 b =
0 1 0 0 0 1 1 0 c =
0 0 0 0 0 1 0 0 d =
1 1 0 0 0 1 1 1 e =
1 1 0 0 0 0 1 1 f =
0 1 1 1 1 0 1 0
Operace posuvu (vlevo, resp. vpravo) mají následující syntaxi:
op << n
, resp. op >> n
. Provedou posuv operandu op o n bitů vlevo (vpravo).
Na uvolněná místa se nasouvají nuly, nenulové bity „vypadávají“. V uvedeném příkladě, hodnoty proměnných g
,
h
budou mít hodnotu
g = | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | h = | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
Pokud neznáme předem, který bit máme testovat (je dán např. hodnotou proměnné n), vyrobíme masku pomocí operace posuvu doleva čísla 1:unsigned char x; unsigned char maska; ... maska = 0x20; if (x & maska != 0) ... else ...
unsigned char x; unsigned char maska; int n; // v proměnné n je uloženo, který bit máme testovat ... maska = (1 << n); if (x & maska != 0) ... else ...
Ukazatel na funkci deklarujeme obecně takto: návr. hodnota (*identifikátor)(seznam form. parametrů)
Příklad:
Využití ukazatele na funkci demonstrujeme ještě na jednom příkladě. V poli typufloat(*moje_fce)(int a, int b);O řádek výše je deklarována proměnnámoje_fce
, což je ukazatel na funkci, která vrací hodnotu typufloat
a má dva parametry typuint
. Závorky(*moje_fce)
jsou nezbytné, jinak bychom nedeklarovali ukazatel na fukcni, ale hlavičku funkce vracející ukazatel nafloat
.Použití ilustruje následující příklad:
float obsah(int a, int b) { return a*b; } int main(void) { float(*moje_fce)(int a, int b); moje_fce = obsah; float o = moje_fce(3,4);, }Do ukazatelemoje_fce
je možné uložit pouze adresu funkce, která vrací hodnotu typufloat
a má dva parametry typuint
.
int
kódujeme informace o vlastnostech pěti osob
(konkrétně jde o kódování chromozomu v genetickém algoritmu). Máme pole typu int
o deseti prvcích, první pětice obsahuje čísla
od 0 do 4, druhá pětice také čísla od 0 do 4. První pětice přiřazuje jménům oblíbená jídla, druhá pětice přiřazuje jménům oblíbené atrakce na pouti.
Index prvku určuje osobu (jméno). Přiřazení čísel jménům, jídlům a atrakcím definuje následující tabulka 2.
Jména | Jídla | Atrakce | |||||
indexy | jméno | hodnota | jídlo | hodnota | atrakce | ||
0 a 5 | Ron | 0 | žvýkačka | 0 | horská dráha | ||
1 a 6 | Joe | 1 | zmrzlina | 1 | zvonková dráha | ||
2 a 7 | Sam | 2 | párek v rohlíku | 2 | strašidelný zámek | ||
3 a 8 | Len | 3 | hranolky | 3 | kolotoč | ||
4 a 9 | Don | 4 | cukrová vata | 4 | lochneska |
Tabulka 2: Kódování chromozomu
Obrázek 1: Příklad zakódování chromozomu
Úkolem je napsat program, který vytiskne text s přiřazením jídel a atrakcí jménům. Nadefinujeme tři funkce
char *vrat_jmeno(int h)
, char *vrat_jidlo(int h)
, char *vrat_atrakci(int h),
které mají jako parametr
celé číslo (kód); první vrací odpovídající jméno, druhá vrací odpovídající jídlo a třetí atrakci.
Výpis textu provedeme v jednom cyklu; nadeklarujeme ukazatel na převodní funkci. Na počátku do něj přiřadíme funkci vrat_jidlo
;
jakmile se dostaneme k indexu 5, tj. začneme vypisovat druhou část chromozomu, přiřadíme do ukazatele funkci vrat_atrakci
.
Příklad: Dev C++: jmena.dev, jmena.cpp
printf
, která
má proměnný počet parametrů).
Problematika funkcí s proměnným počtem parametrů je vysvětlena v uvedené prezentaci: prom_param.ppt
Příklad z prezentace: Dev C++: varpar.dev, varpar.c
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;
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;
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 3. 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 3: Předávání struktur funkcím
Poznámka: Pokud položka struktury obsahuje ukazatel na sebe, musíme uvést jméno struktury před definicí. Následující definici využijeme při implementaci spojového seznamu, viz dále:Jednoduchý program využívající strukturu pro uložení bodu si můžete stáhnout: Dev C++: body.dev, body.ctypedef struct TPolozka { int hodnota; TPolozka *dalsi; } TPolozka;
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 4:
Položka Délka položky Popis typ 2 byty 2 znaky „BM“ velikost 4 byty celková velikost souboru rezervováno1 2 byty rezervováno pro pozdější použití, musí být na 0 rezervováno2 2 byty rezervováno pro pozdější použití, musí být na 0 posun 4 byty posun obrazových dat od začátku této hlavičky (struktury) Tabulka 4: 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
asizeof(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 souborvst
, který byl otevřen pomocífopen
v binárním módu:fread((void*)&hlavicka,sizeof(THeadBMP),1,vst)
.Upozornění: Některé překladače v rámci optimalizace přístupu do paměti neukládaí položky struktur těsně za sebou, ale zarovnávají je na adresy dělitelné 4. V takovém případě by mezi položkami M a velikost byla v paměti mezera a velikost struktury by byla větší než velikost hlavičky. Museli bychom tedy v překladači tuto optimalizaci (zarovnání položek na adresy dělitelné 4) vypnout.