Vectori: tablouri unidimensionale în C/C++

Prima oră din capitolul Vectori, clasa a X-a. La final vei ști să declari un vector, să-l citești, să-l afișezi și să calculezi sume, medii, numărări, minime și maxime.

1. Introducere și motivare

Ce este un vector?

Un tablou unidimensional (vector) este o colecție de elemente de același tip, memorate unul după altul în memorie și accesate prin număr de ordine numit indice. Tot vectorul are un singur nume, iar fiecare element are propria poziție.

De ce avem nevoie de el?

Imaginează-ți un bloc cu 5 apartamente: nu îi dai fiecărui apartament un nume diferit, ci le numeri: ap. 0, ap. 1, ap. 2... Sau un rând de dulăpioare numerotate: știi exact la care să mergi.

Fără vectori, pentru 5 note ar trebui variabile separate. Pentru 100 de note, 100 de variabile ar fi imposibil de scris și de parcurs:

// Fără vectori: greoi, nu se poate parcurge cu o buclă
int a, b, c, d, e;
cin >> a >> b >> c >> d >> e;

// Cu vector: un nume, mai multe elemente, parcurgere cu for
int v[5];
for (int i = 0; i < 5; i++)
    cin >> v[i];
Ideea de reținut: cu un vector putem repeta aceeași operație pentru toate elementele, folosind o buclă și un indice.

2. Declarare și memorie

Sintaxa declarării

Forma generală este tip nume[dimensiune];, unde dimensiunea este numărul de elemente (o constantă întreagă pozitivă).

int v[5];          // 5 numere întregi
float medii[30];   // 30 de numere reale
char litere[26];   // 26 de caractere

int w[5] = {12, 7, 3, 25, 9};  // declarare cu valori inițiale

Indexare de la 0 la N-1

Într-un vector cu N elemente, indicii sunt 0, 1, ..., N-1. Primul element este v[0], iar ultimul este v[N-1]. Accesarea elementului cu indicele i se scrie v[i].

Atenție, greșeală frecventă! Pentru int v[5]; indicii valizi sunt 0–4. Elementul v[5] nu există: este în afara vectorului.

Vectorul în memorie

Mai jos, int w[5] = {12, 7, 3, 25, 9};. Elementele sunt vecine în memorie. Un int ocupă de obicei 4 octeți, deci adresele cresc din 4 în 4 (valorile adreselor sunt doar ilustrative). Celula punctată este w[5]: nu aparține vectorului.

int w[5] = {12, 7, 3, 25, 9};
cout << w[0];        // afișează 12 (primul element)
cout << w[4];        // afișează 25 (ultimul element, N-1 = 4)
w[2] = 100;          // modificăm elementul de indice 2: acum 3 devine 100
cout << w[1] + w[3]; // 7 + 25 = 32

3. Citirea elementelor unui vector

Un vector se citește element cu element. Folosim o buclă for cu indicele i de la 0 până la n-1, adică i < n.

#include <iostream>
using namespace std;

int main() {
    int n, v[100];            // v poate avea cel mult 100 de elemente
    cin >> n;                // câte elemente citim (n <= 100)
    for (int i = 0; i < n; i++)   // i ia valorile 0, 1, ..., n-1
        cin >> v[i];           // citim elementul de pe poziția i
    return 0;
}
  1. Declarăm vectorul cu o dimensiune suficient de mare (aici 100) și variabila n pentru numărul real de elemente.
  2. Citim n.
  3. Bucla pornește de la i = 0 și continuă cât timp i < n.
  4. La fiecare pas, cin >> v[i] citește o valoare și o pune pe poziția i.

Pentru intrarea 4 urmată de 10 20 30 40, rezultă v[0]=10, v[1]=20, v[2]=30, v[3]=40. În C se scrie scanf("%d", &v[i]);.

4. Afișarea elementelor unui vector

Parcurgem la fel, cu același for, dar scriem valorile în loc să le citim. Putem separa elementele prin spațiu sau prin linie nouă.

// Afișare pe același rând, separate prin spațiu
for (int i = 0; i < n; i++)
    cout << v[i] << " ";
cout << endl;

// Afișare câte un element pe linie
for (int i = 0; i < n; i++)
    cout << v[i] << "\n";
10 20 30 40

Fără spațiul din " ", numerele ar apărea lipite (10203040) și nu s-ar mai distinge.

5. Algoritmi elementari de bază

În exemplele de mai jos presupunem că vectorul v cu n elemente întregi a fost deja citit.

Suma și media aritmetică

Ținem o variabilă acumulator s, pornită de la 0, la care adăugăm fiecare element. Media este suma împărțită la numărul de elemente. Pentru media cu zecimale, facem împărțirea în numere reale.

int s = 0;                  // acumulator: ÎNTOTDEAUNA inițializat cu 0
for (int i = 0; i < n; i++)
    s = s + v[i];           // adăugăm elementul curent (sau s += v[i];)

double media = (double)s / n;   // (double) evită împărțirea întreagă
cout << "Suma: " << s << "\n";
cout << "Media: " << media << "\n";
Pentru 10 20 30 40: Suma: 100 Media: 25

Numărarea elementelor cu o proprietate

Folosim un contor pornit de la 0 și îl mărim doar când elementul îndeplinește condiția. Un număr x este par dacă x % 2 == 0.

int nr = 0;                 // contor, pornit de la 0
for (int i = 0; i < n; i++)
    if (v[i] % 2 == 0)      // elementul este par?
        nr++;               // da: mărim contorul cu 1
cout << "Numere pare: " << nr;
Pentru 3 8 5 6 10: Numere pare: 3

Valoarea minimă și maximă

Pornim cu presupunerea că primul element este și minim, și maxim. Apoi comparăm cu restul elementelor (de la indicele 1) și actualizăm când găsim ceva mai bun.

int mini = v[0], maxi = v[0];      // pornim cu primul element
for (int i = 1; i < n; i++) {     // i începe de la 1, v[0] e deja luat
    if (v[i] < mini) mini = v[i];  // am găsit un element mai mic
    if (v[i] > maxi) maxi = v[i];  // am găsit un element mai mare
}
cout << "Minim: " << mini << ", Maxim: " << maxi;
Pentru 7 2 9 4: Minim: 2, Maxim: 9
De ce nu pornim minimul de la 0? Dacă toate numerele sunt pozitive, 0 ar rămâne „minim” fără să fie în vector. Pornirea cu v[0] este mereu corectă.

6. Greșeli frecvente și bune practici

Accesarea în afara limitelor (Out of Bounds)

C/C++ nu verifică automat indicii. Dacă folosești un indice invalid, programul nu se oprește mereu cu eroare: poate afișa valori ciudate sau se poate bloca, iar comportamentul este nedefinit.

int v[5] = {1, 2, 3, 4, 5};

// GREȘIT: i ajunge la 5, dar ultimul indice valid este 4
for (int i = 0; i <= 5; i++)
    cout << v[i] << " ";

// CORECT
for (int i = 0; i < 5; i++)
    cout << v[i] << " ";
Regula: pentru n elemente scrie i < n, nu i <= n. Verifică și dacă n nu depășește dimensiunea declarată.

Neinițializarea variabilelor de acumulare

O variabilă locală neinițializată conține o valoare aleatoare rămasă în memorie. Dacă uiți s = 0, suma pornește de la o valoare necunoscută.

int s;                      // GREȘIT: s are o valoare necunoscută
for (int i = 0; i < n; i++) s += v[i];   // rezultat incorect

int suma = 0;               // CORECT: pornim de la 0
int nr = 0;                 // la fel și pentru contoare

Lista de verificare

  1. Indicii merg de la 0 la n-1.
  2. Acumulatorii și contoarele se inițializează înainte de buclă.
  3. Minimul și maximul pornesc de la v[0].
  4. Pentru medie, convertește la double înainte de împărțire.