Programarea Calculatoarelor: Laborator 12

Structuri și câmpuri pe biți

Pe lăngă cămpuri obișnuite, structurile pot avea și câmpuri pe biți: valori de tip întreg reprezentate pe un număr specificat de biți. În acest fel putem reprezenta mai eficient valori mici, care nu necesită numărul de biți pe care se reprezintă obișnuit un întreg (16, 32 sau chiar 64). Mai multe câmpuri de acest tip pot ocupa biți succesivi din octeți succesivi din memorie, rezultând într-o reprezentare mai compactă decât dacă s-ar folosi un număr întreg de octeți pentru fiecare câmp. În același timp, valorile pot fi accesate mai natural și mai simplu (cu sintaxa variabilă.câmp) decât cu măști și operatori pe biți.

Limbajul C nu precizează detalii despre ordinea în care sunt dispuși biții în astfel de structuri; acesta poate fi dependent de arhitectură și compilator, deci înainte de a ne baza pe o anumită dispunere trebuie să verificăm cu exemple.

Definim o astfel de structură pentru a reprezenta un moment de timp format din dată și oră. De exemplu, secundele și minutele iau valori între 0 și 59, deci se pot reprezenta pe 6 biți (pe care încap valori până la 26-1, deci 63). Zilele (de la 1 la 31) se pot reprezenta pe 5 biți, la fel și orele, iar lunile pe 4 biți. Încercând să folosim doar 32 de biți în total (la fel ca și un tip întreg) ne mai rămân 6 biți pentru an. Alegem să reprezentâm anii începând de la 1980 (pe care îl reprezentăm ca 0), și putem astfel ajunge până la 1980 + 63 = 2043. Putem scrie structura în felul următor:

#define SECBITS	6
#define MINBITS 6

typedef struct date {
  int sec : SECBITS;
  int min : MINBITS;
  int hr : 5;
  int day : 5;
  int mo : 4;
  int yr : 6;
} date_t;
Numărul de biți e precizat după : pentru fiecare câmp; pentru primele câmpuri am folosit macro-uri (constante simbolice), care ne vor ajuta să scriem mai flexibil și ușor de înțeles codul care urmează.

Putem folosi o variabilă de acest tip ca și orice structură obișnuită și putem verifica câți octeți ocupă o astfel de structură pe sistemul nostru (un răspuns posibil: 4).

  date_t t;
  printf("sizeof(struct date) = %u\n", sizeof(date_t));
  t.yr = 2008 - 1980; t.mo = 5; t.day = 24;
  t.hr = 15; t.min = 47; t.sec = 33;
Pentru a verifica cum sunt reprezentate datele în memorie declarăm o variabilă întreagă de aceeași dimensiune, îi atribuim valoarea aflată la adresa variabilei dată (deci octeții de memorie care formează data, priviți ca și întreg) și îi tipărim câmpurile folosind operatori pe biți. Pe o arhitectură Intel x86 / gcc / Linux, câmpurile sunt reprezentate pornind de la bitul cel mai puțin semnificativ. Pentru a extrage câmpurile, deplasăm întregul d până când câmpul dorit ajunge pe biții cei mai puțin semnificativi, și apoi facem un ȘI cu o mască cu biții respectivi pe 1, și ceilalți pe 0.
#define SECOFF	0
#define MINOFF	SECBITS		// de la ce pozitie incep bitii de minut
#define MASK(bits)	~(~0u << bits)	// 1 pe bits pozitii 

  uint32_t d = *(uint32_t *)&t;	// privim valoarea de la &t ca si intreg
  printf("%u minute %u secunde\n", d >> MINOFF & MASK(MINBITS),
	 d & MASK(SECBITS));

Programul complet se găsește aici. Extindeți programul tipărind și alte câmpuri (ora, ziua, etc.) din structură. Scrieți apoi o funcție care returnează o structură reprezentând data și ora aflată la un număr dat de secunde după începutul anului 1980 (de exemplu: un miliard de secunde).

Probleme propuse 1. Scrieți un program care citește de la intrare un număr de înmatriculare de mașină (2 litere-2 cifre-3 litere, exemplu: TM-10-UPT) și îl reprezintă ca structură pe 32 de biți.

2. Scrieți un program care citește din fișierul cu numele dat pe linia de comandă linii de forma Nume Prenume Nota și le reprezintă ca tablou de structuri (varianta 1: câmpuri de lungime fixă pentru nume; varianta 2: pointeri). Suplimentar: sortați înregistrările după nume și după notă.


Marius Minea
Last modified: Sat May 24 17:38:25 EET 2008