Programare Competitivă

Tipuri de date neomogene (struct)

Până acum am folosit tipuri simple: int, double, char, bool. Toate stochează o singură valoare.

Dar când descriem un elev, nu avem o singură valoare - avem nume, vârstă, note. Când descriem un punct în plan, avem x și y.

Pentru a grupa mai multe valori într-un singur “tip”, folosim struct - un tip de date neomogen (conține valori de tipuri diferite).


Declararea unui struct

struct Elev {
    char nume[50];
    int varsta;
    double medie;
};

Am creat un tip nou numit Elev. Fiecare Elev are trei câmpuri (sau membri): nume, varsta, medie.

Atenție: ; după acoladele struct-ului este obligatoriu! E una dintre cele mai frecvente greșeli.


Folosirea unui struct

Elev e;

strcpy(e.nume, "Ana");
e.varsta = 15;
e.medie = 9.75;

cout << e.nume << " " << e.varsta << " " << e.medie;
// Ana 15 9.75

Accesăm câmpurile cu . (punct): variabila.camp.


Inițializare directă

Elev e = {"Ana", 15, 9.75};   // în ordinea câmpurilor

Valorile se atribuie în ordinea declarării câmpurilor.


Vector de struct-uri

Cea mai utilă aplicație - un vector de elevi:

Elev elevi[101];
int n = 3;

strcpy(elevi[1].nume, "Ana");
elevi[1].varsta = 15;
elevi[1].medie = 9.75;

strcpy(elevi[2].nume, "Mihai");
elevi[2].varsta = 16;
elevi[2].medie = 8.50;

strcpy(elevi[3].nume, "Elena");
elevi[3].varsta = 14;
elevi[3].medie = 9.90;

Citire dintr-un fișier

#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

struct Elev {
    char nume[50];
    int varsta;
    double medie;
};

Elev elevi[101];
int n;

int main()
{
    fin >> n;

    for (int i = 1; i <= n; i++) {
        fin >> elevi[i].nume >> elevi[i].varsta >> elevi[i].medie;
    }

    for (int i = 1; i <= n; i++) {
        fout << elevi[i].nume << " | "
             << elevi[i].varsta << " ani | "
             << "media " << elevi[i].medie << "\n";
    }

    return 0;
}

date.in:

3
Ana 15 9.75
Mihai 16 8.50
Elena 14 9.90

date.out:

Ana | 15 ani | media 9.75
Mihai | 16 ani | media 8.5
Elena | 14 ani | media 9.9

Exemplu: elevul cu media maximă

Elev maxElev = elevi[1];

for (int i = 2; i <= n; i++) {
    if (elevi[i].medie > maxElev.medie)
        maxElev = elevi[i];   // copiere întregului struct
}

fout << "Cel mai bun: " << maxElev.nume << " cu media " << maxElev.medie;

Atribuirea între struct-uri copiază toate câmpurile automat.


Exemplu: punct în plan

struct Punct {
    int x, y;
};

Punct a, b;
a.x = 3; a.y = 4;
b.x = 6; b.y = 8;

// Distanța (la pătrat)
int dx = a.x - b.x;
int dy = a.y - b.y;
int distPatrat = dx * dx + dy * dy;

cout << distPatrat; // 25

Struct cu câmpuri de tipuri mixte

struct Carte {
    char titlu[100];
    char autor[50];
    int an;
    double pret;
    bool inStoc;
};

Carte c = {"Hobbitul", "Tolkien", 1937, 45.99, true};

Struct-ul poate conține orice tipuri: numere, șiruri, bool, chiar alte struct-uri.


Struct în struct

struct Data {
    int zi, luna, an;
};

struct Elev {
    char nume[50];
    Data dataNasterii;
    double medie;
};

Elev e;
strcpy(e.nume, "Ana");
e.dataNasterii.zi = 15;
e.dataNasterii.luna = 3;
e.dataNasterii.an = 2010;
e.medie = 9.75;

Accesăm câmpul interior cu punct dublu: e.dataNasterii.zi.


Struct și funcții

void afiseaza(Elev e) {
    cout << e.nume << " " << e.varsta << " " << e.medie << endl;
}

bool esteBun(Elev e) {
    return e.medie >= 9.0;
}

int main()
{
    Elev ana = {"Ana", 15, 9.75};
    afiseaza(ana);
    if (esteBun(ana))
        cout << "Ana are media mare!";

    return 0;
}

Transmitere prin referință

Pentru a modifica un struct într-o funcție, folosim &:

void creste(Elev &e) {
    e.varsta++;
}

Elev ana = {"Ana", 15, 9.75};
creste(ana);
cout << ana.varsta; // 16

Performanță: struct-urile mari se transmit mai eficient prin referință (Elev &e) chiar dacă nu le modificăm - evităm copierea.


Sortare vector de struct-uri

#include <algorithm>

bool cmp(Elev a, Elev b) {
    return a.medie > b.medie;  // descrescător după medie
}

sort(elevi + 1, elevi + n + 1, cmp);

Definim o funcție de comparare care primește două struct-uri și returnează true dacă primul trebuie să fie înainte.

Sortare după mai multe criterii

bool cmp(Elev a, Elev b) {
    if (a.medie != b.medie)
        return a.medie > b.medie;   // întâi după medie descrescător
    return strcmp(a.nume, b.nume) < 0; // apoi alfabetic
}

Greșeli frecvente

1. Lipsa ; după struct

struct Elev {
    int varsta;
}    // LIPSEȘTE ; - eroare de compilare!

Corect:

struct Elev {
    int varsta;
};

2. Accesare fără punct

Elev e;
varsta = 15;     // GREȘIT - ce e "varsta"? o variabilă globală?
e.varsta = 15;   // CORECT

3. Comparare struct-uri cu ==

Elev a, b;
if (a == b) // EROARE de compilare

C++ nu știe cum să compare două struct-uri. Trebuie să compari câmp cu câmp:

if (a.varsta == b.varsta && a.medie == b.medie && strcmp(a.nume, b.nume) == 0)

4. Citire/afișare direct

Elev e;
cin >> e;   // GREȘIT
cout << e;  // GREȘIT

Trebuie pe câmpuri:

cin >> e.nume >> e.varsta >> e.medie;
cout << e.nume << " " << e.varsta;

Ce să reții

  • struct grupează mai multe valori (posibil de tipuri diferite) într-un tip nou.
  • Declarare: struct Nume { tip1 camp1; tip2 camp2; }; (atenție la ;).
  • Acces la câmp: variabila.camp.
  • Inițializare: Elev e = {"Ana", 15, 9.75};.
  • Copiere automată cu = între struct-uri.
  • Poate fi transmis la funcții (prin valoare sau referință).
  • Sortabil cu sort și un comparator custom.
  • Nu suportă direct cin, cout, == - trebuie pe câmpuri.