Skočiť na obsah


Programovanie v jazyku C a C#

Návody pre začiatočníkov a pokročilých



Fórum: Škôlka jazyka C
- - - - -
Fotografia

10. lekcia C (Bitové operácie a bitové pole) základná škola


10 Bitové operácie a bitové pole

Až doposiaľ sme sa zmieňovali o vyššej úrovni jazyka C, čo je možné dokumentovať napr. tým,
že takmer každá konštrukcia v C malá svoju podobu v Pascale. Bitové operácie sa týkajú nižšej
úrovne jazyka - vykazujú totiž rysy jazyka assembler - a štandardný Pascal nič také
nepodporuje.
Všeobecne sa odporúča vyhýbať sa týmto operáciám. Väčšinou totiž vyžadujú hlbšie
znalosti systému, napr. formát uloženia čísiel, a aj malá chyba môže mať veľký negatívny dopad.
Sú však situácie, keď sa ichpoužiťiu nevyhneme. Buď už z dôvodu nemožnosti napísať nejaký
úsek programu pomocou "vyšších" príkazov jazyka, alebo, čo je oveľa častejšie, že pri vhodnom
použití dovolí v určitých prípadoch výrazne urýchliť program.


10.1 operácie s jednotlivými bitmi

Pre účely manipulácie s byťmi poskytuje jazyk C 6 operátorov:

& - bitový súčin (AND)

| - bitový súčet (OR)

^ - bitový exkluzívny súčet (XOR)

<< - posun doľava

>> - posun doprava

~ - jednotkový doplnok - negácia bit po bite - unárny operátor
Poznámky:
* Argumenty bitových operácií nesmú byť premenné typu float, double a long double.
* bitové operácie je možné použiť aj pre typy signed.
Z hľadiska čitateľnosti programu je ale lepšie používať typ unsigned.


10.1.1 bitový súčin

i-ty bit výsledku bitového súčinu:
x & y
bude 1, pokiaľ i-ty bit x a i-ty bit y budú 1, ináč bude 0.
Teda jednotlivé bity výsledku budú záležať na jednotlivých bitoch operandov.

Príklad:
Nasledujúce makro môže byť použité na test, či je číslo nepárne.
(nepárne čísla majú nultý bit nastavený na 1).
#define je_parne(x) (1 & (unsigned)(x))
bitový súčin sa často používa na vymaskovanie (nastavenie na nulu) určitých bitov,
napr. ak chceme premennú typu int previesť na ASCII znak, teda využiť len najnižších 7 bitov:
c = c & 0x7F;	 /* 0x7F je 0000 0000 1111 1111 */
alebo skrátene:

c &= 0x7F;

Poznámka:
* Uvedomme si, že je rozdiel medzi bitovým súčinom a logickym súčinom:
unsigned int i = 1, j = 2, k, l;

k = i && j;	 /* k == 1 */

k = i & j;		 /* k == 0 */
pretoze:
1 = 0000 0001
2 = 0000 0010


10.1.2 bitový súčet

i-ty bit výsledku bitového súčtu:
x | y
bude 1, pokiaľ i-ty bit x alebo i-ty bit y bude 1,
ak budú obidva nulové, bude výsledok 0.
bitový súčet sa často používa na nastavenie určitých bitov na 1, pričom sa ostatné bity
nechajú nedotknuté.

Príklad:
Nasledujúce makro môže byť použité na zmenu veľkých písmen na malé:
#define na_male( c ) (c | 0x20) /* 0x20 je 0010 0000 binárne */

10.1.3 bitový exkluzívny súčet

i-ty bit výsledku bitového XOR:

x ^ y
bude 1, pokiaľ i-ty bit x sa nerovná i-temu bitu y, ak sú obidva nulové, alebo obidva
jednotkové bude výsledok 0.

Táto operácia sa dá použiť k porovnaniu dvoch celých čísiel:
if (x ^ y)

/* čísla sú rozdielne */

10.1.4 operácia bitového posunu doľava

Príkaz:

x << n
posunie bity v x doľava o n pozícií.
Pri tomto posune sa zľava bity strácajú - sú vytlačované - a sprava sú doplňované 0.
bitový posun doľava sa občas používa na rýchle násobenie dvomi, respektívne mocninou dvoch.

Napr. Príkaz:
x = x << 1;
alebo skrátene
x <<= 1;
vynásobí x dvomi,

alebo príkaz:
x <<= 3;
vynásobí x ôsmimi (8 = 23)


10.1.5 operácia bitového posunu doprava

Príkaz:
x >> n
posunie bity v x doprava o n pozícií.
Pri tomto posune sa sprava bity strácajú - sú vytlačované - a zľava sú doplňované 0.
Bitový posun doprava ma opačný význam než posun doľava, teda celočíselné delenie dvomi,
alebo mocninou dvoch.

Napr. Príkaz:
x = x >> 1;
alebo skrátene
x >>= 1;
delí x dvomi,

alebo príkaz:

x >>= 4;

delí x šesťnástimi (16 = 24)

Poznámka:
*Tieto operácie "násobenie" a "delenie" dvomi pomocou posunu su rýchlejsie ako skutočné
násobenie a delenie.
Pri výpočtoch s celými číslami, kde ide o rýchlosť, je dobré zamyslieť sa nad tým, či by sa nedali využiť.

Príklad:
Výhodnejšie ako násobiť 80-mi, je lepšie "násobiť" 64-mi a 16-mi a spočítať výsledok:
i = j * 80;			 /* pomalšie */

i = (j << 6) + (j << 4); /* rýchlejsie */
Pozor:
Je potrebné si uvedomiť, že priority operátorov << a >> sú veľmi nízke, takže je nutné
takmer vždy zátvorkovať.

Príklad:
Operátor >> sa tiež často používa na získanie hodnoty konkrétneho bitu. Používa sa jednoduchý
trik, kedy sa s bitmi posúva tak dlho, až je požadovaný bit na najnižšej pozícií.

Funkcia bit() vráti hodnotu i-teho bitu svojho parametra.
#define ERROR		 -1

#define BITOV_V_CHAR	8



int bit(unsigned int x, unsigned int i)

{

	if (i >= sizeof(x) * BITOV_V_CHAR)

		return ERROR;

	else

	 return ((x >> i) & 1);

}
Príkaz: x >> i posunie i-ty bit na poslednú pozíciu vpravo a príkaz: & 1 nastaví na nulu
všetky vyššie bity.


10.1.6 negácia bit po bite

Na túto akciu sa tiež často používa názov jednotkový doplnok.
Príkaz:
~x
prevráti nulové bity na jednotkové a naopak.
Tento operátor sa používa napr. v situáciách, keď sa chceme vyhnúť počítačovo závislej
dĺžke celého čísla.

Napríklad príkaz:
unsigned int x;

x &= 0xFFF0;
nastaví na nulu najnižšie štyri bity x. Bude ale pracovať správne len na počítačoch,
kde platí: sizeof(int) == 2.

Riešením je príkaz:
x &= ~0xF;
ktorý bude pracovať správne na všetkých typoch počítačov.


10.1.7 Spôsoby práce so skupinou bitov

Často sa bitové operácie používajú na prácu so skupinou bitov, ktorú napr. predstavuje
stavové slovo definované ako:
unsigned int status;
Poznámka:
*Na označenie krajných bitov slova sa často používajú anglické skratky.
MSB - najvýznamnejší (najľavejší) bit (most significant bit)

	LSB - najmenej významný (najpravejší) bit (least significant bit)
Najprv sa definujú konštanty, ktoré určia pozície príznakových bitov v stavovom slove.

Napr. použijeme bity 3. 4. a 5. pre príznaky čítať, písať, vymazať (READ, WRITE, DELETE).
/*bit číslo:	 7	 6	 5	 4	 3	 2	 1	 0		 */

/*MSB		 |	 |	 |	 |	 |	 |	 |	 |		 LSB */



#define READ			 0x8

#define WRITE			 0x10

#define DELETE			0x20
Po tejto príprave je možné uskutočniť:

* nastavenie všetkých príznakov na 1 je:
status |= READ | WRITE | DELETE;
* nastavenie príznakov READ a WRITE na 1 je:
status |= READ | WRITE;
* nastavenie všetkých príznakov na 0 je:
status &= ~(READ | WRITE | DELETE);
* nastavenie príznaku READ na 0 je:
status &= ~READ;
* test či sú obidva príznaky WRITE a DELETE nulové:

if ( !(status & (WRITE | DELETE)))


10.2 Bitové pole

Bitové pole je možné si predstaviť ako štruktúru, ktorej veľkosť je ale pevne obmedzená
veľkosťou typu int. Najmenšia dĺžka jednej položky v bitovom poli je 1 bit. Bitové pole sa
tiež definuje veľmi podobne ako štruktúra, odlišnosť je iba v tom, že je každá položka bitového
poľa určená ako svojim menom tak aj dĺžkou v bitoch.
ANSI C umožňuje definíciu prvkou ako signed int, tak aj unsigned int a je preto vhodné vždy
uviesť, či bude položka znamienková alebo neznamienková.

Bitové pole ma dve základné oblasti použiťia:
1) Uloženie niekoľkých celých čísiel v jednom slove, čo býva používané hlavne na šetrenie pamäti,
avšak nie príliš často.
2) Na prístup k jednotlivým bitom slova pomocou idenfifikátorov, to sa používa oveľa častejšie,
pretože sa potom operácie s jednotlivými bitmi uskutočňujú veľmi elegantne a prehľadne.

Príklad:
Ukážka využiťia bitového pola pre uloženie dátumu zhustene do jedneho slova - predpokladáme
sizeof(int) == 2.Dátum bude uložený tak, že položka deň bude zaberať najnižších 5 bitov,
položka mesiac nasledujúce 4 bity a položka rok zostávajúcich 7 bitov.
Pretože však 7 bitov je na storočie málo - najvyššie číslo sem uložené by bolo: 127=2^7 - 1
Preto použijeme trik, že k uloženému dátumu vždy pripočítame konštantu 1980.
typedef struct {

unsigned den	: 5; /* bity 0 - 4 */

unsigned mesiac : 4; /* bity 5 - 8 */

unsigned rok	: 7; /* bity 9 - 15 */

} datum;



datum dnes, zajtra;



dnes.den = 25;

dnes.mesiac = 8;

dnes.rok = 1999 - 1980;

zajtra.den = dnes.den + 1;
Príklad:
V tejto časti programu sa pokúsime pomocou bitového poľa riešiť prípad z predchádzajúcej
podkapitoly, kde sme pracovali so stavovým slovom a príznakovými bitmi READ, WRITE a DELETE.
Všetky údaje ostavajú v platnosti.
typedef struct {

unsigned zaciatok : 3;	/* bity 0 - 2 */

unsigned read	 : 1;	/* bit 3	 */

unsigned write	: 1;	/* bit 4	 */

unsigned delete : 1;	/* bit 5	 */

} FLAGY;
položka zaciatok je použitá na preskočenie bitov 0, 1 a 2.
Po definícii premennej:
FLAGY status;
je možné ľahko pracovať s jednotlivými bitmi:

* Nastavenie všetkých príznakov na 1 je:.
status.read = status.write = status.delete = 1;
* nastavenie všetkých príznakov na 0 je:
status.read = status.write = status.delete = 0;
* test či sú obidva príznaky WRITE a DELETE nulové:
if (! (status.write | status.delete))
Poznámky:
* Adresy (&) položiek bitových poli nie je možné získať a tak isto nie je možné používať
pointery na jednotlivé položky.
* Poradie, v akom sú ukladané položky bitového poľa, to znamená, či od vyšších bitov
k nižším (od MSB k LSB), alebo naopak (od LSB k MSB), je implementačne závislé! Je potrebné
vykonať pokus na konkrétnom počítači s konkrétnym prekladačom.

Úlohy:

1. Napíš funkciu int dlzka_int(void), ktorá vráti dĺžku premennej typu int v bitoch.
Zabezpeč, aby funkcia pracovala správne na ľubovoľnom type počítača. Vyskúšaj v programe.
Spoiler

2. Napíš funkciu unsigned rotuj_doprava(unsignet x, int n), ktorá spôsobí rotáciu
(nie posun) čísla o n bitov doprava. Vyskúšaj v programe.
Spoiler

3. Napíš funkciu unsigned invert(unsigned x, int p, int n), ktorá invertuje
(zmení 1 za 0 a naopak) n bitov premennej x počnúc od pozície p vrátane.
Ostatné bity ostanú nezmenené. Vyskúšaj v programe.
Spoiler

4. Napíš program, ktorý bude využívať bitové pole na úschovu dátumu. Položky poľa budú:

bity 15 - 9 : rok, ku ktorému sa pripočíta konštanta 1980
teda 22 znamená v skutočnosti rok 2002
bity 8 - 5 : mesiac
bity 4 - 0 : deň

Program vyskúšaj tak, že načítaš z klávesnice dátum, uložíš ho do bitového poľa, to vytlačíš
ako typ unsigned a potom dátum vytlačí ako naozajstný dátum. Na vytlačenie premennej
ako unsigned použi union.
Spoiler
  • 0

Popis: Bitové operácie a bitové pole

Operacie s jednotlivymi bitmi
Bitovy sucin
Bitovy sucet
Bitovy exkluzivny sucet
Operácia bitového posunu doľava
Operácia bitového posunu doprava
Negácia bit po bite
Spôsoby práce so skupinou bitov
Bitové pole
Poznámky:
Odkazy:


0 Komentárov


Najlepšie lekcie


Najnovšie pridané lekcie


Najnovšie komentáre


Najviac komentované lekcie


Najviac zobrazené lekcie


Náhodné lekcie


Na tejto stránke bolo užívateľ(ov) za posledných 30 minút

členov, návštevníkov