Capitolul 7
Tipurile enumerare, "typedef", si operatori pentru biti
7.1 Tipurile enumerare
Pentru declararea tipurilor enumerare se foloseste cuvantul
rezervat "enum". Acesta va implica denumirea multimii, enumerarea
elementelor (numite enumeratori), ca elemente ale multimii.
Exemplu:
enum zile {luni, marti, miercuri, joi,
vineri, sambata, duminica};
Aceasta declaratie creeaza tipul utilizator "enum zile". Cuvantul
rezervat "enum" este urmat de identificatorul "zile". Enumeratorii
sunt identificatorii luni, marti, ... . Acestea sunt constante de tip "int".
Prin conventie, primul este 0, si apoi restul sunt incrementati. Declararea
variabilelor de tip "enum zile" se face astfel:
enum zile zi1, zi2;
Variabilele "zi1" si "zi2" pot lua ca valori elemente ale
acestui tip. De exemplu,
zi1 = miercuri;
va asigna variabilei "zi1" valoarea miercuri. Instructiunea
if (zi1 == zi2)
{
...
}
va testa daca valoarea variabilei "zi1" este egala cu valoarea variabilei
"zi2".
Enumeratorii pot fi initializati. De asemeni, putem declara variabilele in timpul
definirii tipului "enum".
Exemplu:
enum carti {trefla = 1, caro, frunza, inima} a, b, c;
Din moment ce "trefla" este initializata cu 1, rezulta ca "caro",
"frunza" si "inima" sunt initializate cu 2, 3, respectiv
4.
Exemplu:
enum fructe {mere = 7, pere, portocale = 3, lamai} nr_frct;
Este clar ca "pere" va fi initializat cu 8, iar "lamai"
cu 4.
Numele tipului enumerare poate lipsi, insa atunci nu mai putem declara alte
variabile de acel tip.
Exemplu:
enum {plop, molid, brad} copaci;
Singura variabila de tip "enum {plop, molid, brad}" este copaci (nu
se mai poate declara alta).
7.2 Folosirea lui "typedef"
C pune la dispozitie facilitatea "typedef" pentru redenumirea tipurilor deja existente.
Exemplu: typedef int culoare; culoare rosu,
verde, albastru. Acesta defineste tipul "culoare" ca fiind un sinonim
al lui "int". Apoi declaram trei variabile de tipul "culoare".
Exemplu:
Vom ilustra folosirea lui "typedef" pentru un tip enumerare (creand
o functie ce returneaza ziua urmatoare).
enum zile {duminica, luni, marti, miercuri,
joi, vineri, sambata};
typedef enum zile zi;
zi gaseste_ziua_urmatoare(zi z)
{
zi ziua_urmatoare;
switch(z)
{
case duminica:
ziua_urmatoare = luni;
break;
case luni:
ziua_urmatoare = marti;
break;
...
case sambata:
ziua_urmatoare = duminica;
break;
}
return ziua_urmatoare;
}
O alta versiune (mai succinta) folosind "cast" este:
enum zile {duminica, luni, marti, miercuri, joi, vineri, sambata};
typedef enum zile zi;
zi gaseste_urmatoarea_zi(zi z)
{
return ((zi)(((int) z + 1) % 7));
}
7.3 Expresii si operatori pe biti
Operatorii pe biti lucreaza cu expresii integrale reprezentate
ca siruri de cifre binare. Acesti operatori sunt dependenti de sistem.
Operatorii pe biti sunt:
Operatori logici
1. Complement pe bit (unar): ~
2. Si pe bit : &
3. Sau exclusiv pe bit : ^
4. Sau inclusiv pe bit : |
Operatori de deplasare
1. Deplasare stanga : <<
2. Deplasare dreapta : >>
Ca si ceilalti operatori, operatorii pe biti au reguli de precedenta si asociativitate
care determina precis cum se evalueaza expresiile ce contin astfel de operatori.
Operatorul ~ este unar, restul operatorilor sunt binari si lucreaza cu tipuri
integrale.
7.4 Complement pe bit
Operatorul ~ se numeste operator de complement (sau operator
de complement pe bit). Acesta inverseaza reprezentarea sirului pe biti, adica
0 devine 1 si 1 devine 0.
Exemplu: Fie declaratia
int a = 5171;
Reprezentarea binara a lui a este:
00010100 00110011
Expresia ~a este:
11101011 11001100
Aceasta are valoarea intreaga - 5172.
7.5 Complement fata de doi
Reprezentarea complementului fata de doi a unui numar natural
este un sir de biti obtinut prin complementarierea scrierii lui n in baza 2.
Considerand complementul pe biti al lui n la care adunam 1, obtinem reprezentarea
complementului fata de doi a lui -n.
O masina care utilizeaza reprezentarea complementului fata de doi ca reprezentare
binara in memorie pentru valori integrale se numeste masina complement fata
de doi. Reprezentarile complement fata de doi ale lui 0 si -1 sunt speciale.
Astfel valoarea 0 are toti bitii "off", pe cand valoarea -1 are toti
bitii "on". Numerele negative sunt caracterizate prin aceea ca au
bitul cel mai semnificativ 1. Pe masinile care utilizeaza complementul fata
de doi, hard-ul permite implementarea scaderii ca o adunare si un complement
pe biti. Operatia a - b este aceeasi cu a + (-b), unde -b se obtine considerand
complementul pe biti al lui b la care adunam 1.
7.6 Operatori logici binari pe biti
Cei trei operatori & (si), ^ (sau exclusiv) si | (sau inclusiv) sunt binari si au operanzi integrali. Operanzii sunt operati bit cu bit. Tabelul de mai jos contine semantica lor:
a |
b |
a & b |
a ^ b |
a | b |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
0 |
1 |
Exemplu: Presupunem ca avem declaratiile
si initializarile
int a = 3333, b = 7777;
Expresie | Reprezentare | Valoare |
a | 00001101 00000101 | 3333 |
b | 00011110 01100001 | 7777 |
a & b | 00001100 00000001 | 3073 |
a ^ b | 00010011 01100100 | 4964 |
a | b | 00011111 01100101 | 8037 |
~(a | b) | 11100000 10011010 | -8038 |
(~a & ~b) | 11100000 10011010 | -8038 |
7.7 Operatori de deplasare stanga si dreapta
Cei doi operanzi ai unui operator de deplasare trebuie sa fie
expresii integrale. Tipul returnat de expresie este dat de operandul din stanga.
O expresie de forma expresie1 <<
expresie2 implica reprezentarea pe bit a lui "expresie1" sa
fie deplasata catre stanga cu un numar de pozitii specificat de "expresie2".
In capatul din dreapta, vor fi adaugate 0-uri.
Exemplu: char c = 'Z';
Expresie |
Reprezentare |
Actiune |
c |
01011010 |
nedeplasat |
c << 1 |
10110100 |
deplasare la stanga cu 1 |
c << 4 |
10100000 |
deplasare la stanga cu 4 |
c << 15 |
00000000 |
deplasare la stanga cu 15 |
Chiar daca valoarea lui "c" se memoreaza pe un octet,
intr-o expresie aceasta ia tipul "int". Deci valoarea expresiilor
"c << 1", "c << 4" si "c << 15"
se memoreaza pe doi octeti.
Operatorul de deplasare la dreapta ">>" nu este chiar simetric
cu "<<". Pentru expresiile integrale fara semn, din stanga se
va deplasa 0, iar pentru cele cu semn 1.
Exemplu:
Presupunem ca avem declaratiile:
int a = 1 << 15;
unsigned b = 1 << 15;
Expresie |
Reprezentare |
Actiune |
a |
10000000 00000000 |
nedeplasat |
a >> 3 |
11110000 00000000 |
deplasare la dreapta cu 3 |
b |
10000000 00000000 |
nedeplasat |
b >> 3 |
00010000 00000000 |
deplasare la dreapta cu 3 |
Ca valoare intreaga, a >> 3 este -4096, iar b >>
3 este 4096, lucru care este in concordanta cu notiunea de numar negativ si
pozitiv din matematica.
Daca operandul din dreapta a operatorului de deplasare este negativ sau are
o valoare care este egala sau depaseste numarul de biti folositi pentru reprezentarea
operandului din stanga, atunci comportarea este nedefinita.
Tabelul de mai jos ilustreaza regulile de precedenta (+ este mai prioritar)
si asociativitate (de la stanga la dreapta) referitoare la operatorii de deplasare.
Exemplu: Presupunem ca avem: unsigned a = 1, b = 2;
Expresie |
Expresie echivalenta |
Reprezentare |
Valoare |
a << b >> 1 |
(a << b) >> 1 |
00000000 00000010 |
2 |
a << 1 + 2 << 3 |
(a << (1 + 2)) << 3 |
00000000 01000000 |
64 |
a + b << 12 * a >> b |
((a + b)<<(12 * a))>>b |
00001100 00000000 |
3072 |
7.8 Masti
O "masca" este o constanta sau variabila folosita
pentru extragerea bitilor doriti dintr-o alta variabila sau expresie. Din moment
ce constanta "int" 1 are reprezentarea pe biti:
00000000 00000001
aceasta poate fi folosita pentru determinarea bitului cel mai nesemnificativ
dintr-o expresie "int". Codul de mai jos foloseste aceasta masca si
tipareste o secventa alternativa de 0 si 1:
int i, masca = 1;
for (i = 0; i < 10; ++ i)
printf("%d", i & masca);
Daca dorim sa gasim valoarea unui anume bit dintr-o expresie, putem folosi o
masca care este 1 in acea pozitie si 0 in rest.
Exemplu:
Putem folosi expresia 1 << 2 pentru a vedea al treilea bit (numarand de
la dreapta). Expresia (v & (1 << 2)) ? 1 : 0 are valoarea 1 sau 0
dupa cum este al treilea bit din "v".
Alt exemplu de masca este valoarea constanta 255 (adica 2^8 -1). Ea are reprezentarea
00000000 11111111
Deoarece doar octetul din dreapta este plasat pe "on", atunci expresia
v & 255 va intoarce o valoare ce are reprezentare pe biti cu toti bitii
din octetul din stanga "off" si cel din dreapta identic cu octetul
din dreapta a lui "v". Spunem ca 255 este o masca pentru octetul din
dreapta.
7.9 Un program de impachetare si despachetare a cuvintelor
Stim ca un caracter se memoreaza pe un octet, pe cand un intreg
pe doi octeti. Folosind operatorii de deplasare, putem comprima doua caractere
intr-un intreg.
#include
#include
#include
void bit_print(int a)
{
int i;
int n = sizeof(int) * CHAR_BIT;
int masca = 1 << (n-1);
for (i = 1; i <= n; ++i)
{
putchar(((a & masca) == 0) ? '0' : '1');
a <<= 1;
if (i % CHAR_BIT == 0 && i < n)
putchar(' ');
}
}
int pack(char a, char b)
{
int p = a;
p = (p << CHAR_BIT) | b;
return p;
}
char unpack(int p, int k) /* k = 0, 1 */
{
int n = k * CHAR_BIT; /* n = 0, 8 */
unsigned masca = 255;
masca <<= n;
return((p & masca) >> n);
}
void main()
{
clrscr();
bit_print(65);
printf("\nab = ");
bit_print(pack('a', 'b'));
putchar('\n');
getch();
printf("Numarul 24930 este impachetarea caracterelor %c si %c",
unpack(24930,1), unpack(24930,0));
getch();
}
7.10 Litere mari -> litere mici
Reluam un exemplu dintr-un capitol precedent si anume transformarea
literelor mari in mici folosind operatori de deplasare.
#include
#include
#include
void main()
{
clrscr();
int c;
while ((c = getchar()) != EOF)
{
if (isupper(c)) // sau (c>='A' && c<='Z')
putchar(c | (1 << 5));
else
putchar(c);
}
}
![]() |
![]() |
![]() |