10.4. Reuniune

2019/03/26 in Programare in C

Limbajul C ofera utilizatorului posibilitatea de a pastra intr-o zona de memorie date de tipuri diferite. Pana in prezent am vazut ca unei date i se aloca o zona de memorie potrivit tipului datei respective si in zona alocata ei se pot pastra numai date de acel tip. De exemplu, daca avem in vedere declaratia:

long x;

atunci pentru x se aloca 32 de biti si in zona respectiva se pastreaza intregi reprezentati prin complement fata de 2.

Se pot ivi situatii in care in momente diferite ale executiei , am dori ca in aceeasi zona de memorie sa putem pastra date de tipuri diferite. De exemplu, daca dupa un timp nu mai este nevoie de variabila x, zona alocata lui x ar putea fi utilizata in alte scopuri, pentru a pastra o data de un alt tip: char, int sau chiar float. Astfel de reutilizari ale zonelor de memorie pot conduce la economisirea memoriei.

Acest lucru este posibil grupand impreuna datele care dorim sa fie alocate in aceeasi zona de memorie. Pentru a realiza o astfel de grupare se foloseste o structura similara cu cea pentru structuri, diferenta constand in aceea ca se schimba cuvantul struct cu union. In rest, toate formatele intalnite in cazul structurilor raman valabile. Tipul introdus prin union este un tip definit de utilizator ca si cel definit prin struct. O astfel de data grupata o vom numi reuniune.

Exemple:

  1. union a {
      int x;
      long y;
      double r;
      char c;
    } p;

    p este o reuniune de tipul a.

    Componentele x, y, r si c ale lui p le referim ca si in cazul structurilor prin:

    p.x, p.y, p.r si p.c.

    Ele sunt alocate in aceeasi zona de memorie si de aceea, la un moment dat al executiei, numai una din aceste componente este definita (alocata).

    Pentru p se aloca o zona de memorie suficienta pentru a pastra data care necesita numarul maxim de octeti, deci se vor aloca 8 octeti necesari pentru a putea pastra componenta r de tip double.

    Sa observam ca daca inlocuim cuvantul union prin struct, atunci pentru p se aloca:

    • 2 octeti pentru x;
    • 4 octeti pentru y;
    • 8 octeti pentru 5;
    • 1 octet pentru c,

    deci in total 15 octeti. Aceasta deoarece, in cazul unei structuri, componentele ei sunt definite simultan si deci trebuie sa fie alocate in zone diferite de memorie.

  2. typedef union {
      char nume[70];
      int nrmat;
      long cod;
    } ZC;
    ZC sir;

    sir este o reuniune de tip ZC pentru care s-au alocat 70 de octeti. In aceasta zona se pot pastra siruri de caractere la care ne putem referi prin:

    sir.nume;
    sir.nume[0];
    sir.nume[1];
    ...

    sau intregi de tip int sau long. La acestia ne referim prin:

    sir.nrmat;
    sir.cod

    Fie:

    ZC *p;

    atunci putem realiza o atribuire de forma:

    p = &sir;

    Prin intermediul lui p ne putem referi la componentele reuniunii sir astfel:

    p -> nume;
    p -> nume[0];
    p -> nume[1];
    ...
    p -> nrmat;
    p -> cod

In legatura cu reuniunile, pot aparea probleme la utilizarea lor, deoarece programatorul trebuie sa cunoasca, in fiecare moment al executiei, ce componenta a reuniunii este prezenta in zona alocata ei.

Fie reuniunea:

union {
  int i;
  float f;
  double d;
} zc;

Daca la un moment dat, in zona alocata datei zc se pastreaza un intreg de tip int, de exemplu se face atribuirea:

zc.i = 10

si se fac referiri la componenta d:

if(zc.d > 0)

atunci rezultatul compararii va fi eronat. Aceasta eroare nu poate fi semnalata de compilator si nici la executie si de aceea, astfel de utilizari pot fi evitate numai de programator.

Pentru a inlatura erorile de acest fel se recomanda utilizarea unui indicator care sa defineasca tipul datei pastrate in fiecare moment in zona alocata reuniunii respective. De exemplu, in cazul reuniunii zc de mai sus este necesar un indicator care sa aiba trei valori corespunzatoare celor trei tipuri ale componentelor lui zc (int, float si double). Aceste valori le numim sugestiv prin constante simbolice:

#define INTREG 1
#define FSIMPLU 2
#define FDUBLU 3

Adaugam la reuniunea de mai sus un indicator care are una din aceste valori, in functie de componenta prezenta in zona reuniunii. Se obtine tipul utilizator de mai jos:

struct tszc {
  int tipcrt; /* indicator care defineste tipul curent */
  union {
    int i;
    float f;
    double d;
  } zc;
};

Declaram structura de tip tszc:

struct tszc szc;

Structura are doua componente:

tipcrt este de tip int;
zc este o reuniune de componente i, f si d.

Astfel, tot timpul este alocata data tipcrt si numai una din componentele i, f, d.

Pentru a pastra o data de tip int, de exemplu valoarea 123, vom folosi atribuirea:

szc.zc.i = 123;

Alaturi de aceasta atribuire vom mai utiliza inca una si anume:

szc.tipcrt = INTREG;

In felul acesta, componenta tipcrt ne permite sa stabilim faptul ca reuniunea zc contine o data de tip int.

In mod analog, daca dorim sa pastram, in zona respectiva, o data flotanta de tip double, de exemplu valoarea lui pi, vom folosi atribuirile:

szc.zc.d = 3.14159265;
szc.tipcrt = FDUBLU;

La utilizarea datelor pastrate in acest fel se poate folosi o constructie if de forma:

if(scz.tipcrt == INTREG)
  se foloseste szc.zc.i
else if(szc.tipcrt == FSIMPLU)
        se foloseste szc.zc.f
     else if(szc.tipcrt == FDUBLU)
            se foloseste szc.zc.d
          else
            eroare

Aceeasi utilizare se obtine folosind o instructiune switch:

switch(szc.tipcrt) {
  case INTREG;
    se foloseste szc.zc.i
    break;
  case FSIMPLU;
    se foloseste szc.zc.f
    break;
  case FDUBLU;
    se foloseste szc.zc.d
    break;
  default:
    eroare
}

Trebuie amintit ca reuniunile NU pot fi initializate, spre deosebire de structuri.

10.5. Camp