De ce contează?
Imaginează-ți contorul de kilometri al unei mașini vechi, cu cifre care se rotesc. A ajuns la 999999 km. Mai mergi un singur kilometru și contorul nu afișează 1000000 — nu are loc pentru a șaptea cifră. Se rotește înapoi la 000000, ca și cum mașina ar fi nouă. Nimic nu se rupe, niciun bec nu se aprinde — pur și simplu numărul devine, dintr-o dată, fals. Exact asta pățește un int în C++ când îl umpli prea tare.
Ideea-cheie
Fiecare tip de date întreg are un sertar de o mărime fixă. Un int ține numere cam până la 2147483647 (puțin peste 2 miliarde). Atâta timp cât valorile tale încap în sertar, totul e perfect.
Problema apare când rezultatul unui calcul depășește marginea sertarului. Calculatorul nu îți spune „nu mai am loc". Nu primești nicio eroare, programul nu se oprește. Valoarea pur și simplu se rotește — la fel ca odometrul — și ajunge un număr complet greșit, de obicei un negativ uriaș. Asta se numește overflow (depășire).
Partea periculoasă a overflow-ului nu e că dă un rezultat greșit — e că îl dă în tăcere. Nu există eroare, nu există avertisment. Programul tău rulează frumos, afișează un număr cu încredere, iar tu iei zero puncte pe test fiindcă acel număr este, de fapt, gunoi. Un olimpic nu caută overflow-ul după ce greșește — îl previne înainte să scrie codul.
Cum alegi
Reflexul corect e simplu și se aplică înainte de a scrie calculul: estimează cât de mare poate ajunge valoarea cea mai mare, fie ea și intermediară (un produs sau o sumă pe parcurs). Dacă poate depăși aproximativ 2 * 10^9, nu mai folosi int — folosește long long (sertar până la cam 9 * 10^18).
Hai pe exemple concrete.
Exemplul 1 — un produs. Înmulțești două numere de câte cinci cifre:
int a = 50000, b = 50000;
int p = a * b; // vrei 2500000000 (2,5 miliarde)
cout << p; // afiseaza -1794967296, nu 2,5 miliarde!50000 * 50000 = 2500000000, adică 2,5 miliarde. Dar limita lui int e cam 2 miliarde. Rezultatul se rotește și iese un negativ fără sens. Estimarea ți-ar fi spus din start: 2,5 * 10^9 > 2 * 10^9, deci aici trebuie long long.
Exemplul 2 — o sumă. Aduni n numere, fiecare până la un miliard:
// n = 100000 valori, fiecare pana la 10^9
// suma maxima = 100000 * 10^9 = 10^14Fiecare număr în parte încape lejer într-un int. Capcana e că suma lor ajunge la 10^14, cu mult peste limita lui int. Variabila în care aduni trebuie să fie long long, chiar dacă elementele adunate sunt mici.
Capcana subtilă — calculul se strică ÎNAINTE de atribuire. Mulți elevi cred că e de ajuns ca rezultatul să fie long long:
int a = 50000, b = 50000;
long long s = a * b; // GRESIT: s devine -1794967296De ce? Pentru că a și b sunt amândoi int, iar C++ calculează a * b în int, depășește acolo, și abia rezultatul deja stricat este copiat în s. Faptul că s e long long ajunge prea târziu. Soluția e să forțezi măcar un operand la long long ÎNAINTE de înmulțire, ca tot calculul să se facă în spațiu mare:
int a = 50000, b = 50000;
long long s = (long long)a * b; // CORECT: s = 2500000000Acum primul operand e long long, deci înmulțirea se face în long long, iar 2,5 * 10^9 încape fără probleme.
Greșeala clasică: scrii long long s = a * b; și crezi că ești în siguranță fiindcă s e long long. Nu ești — a * b se calculează încă în int și depășește înainte de atribuire. Regula de aur: tipul mare trebuie să fie la operand, nu doar la rezultat. Pune cast pe primul factor — (long long)a * b — sau declară chiar variabilele a și b ca long long de la început. Și, ori de câte ori aduni multe numere sau înmulțești două numere mari, fă reflexul de a estima maximul: dacă trece de 2 miliarde, long long din prima.