Operatorii aplicați unor operanzi formează expresii. C/C++ are una dintre cele mai bogate colecții de operatori dintre limbajele de programare - ceea ce oferă putere de exprimare, dar face ca regulile de precedență și conversiile implicite să conteze enorm.
1Obiectivele lucrării
- Clasificarea operatorilor după numărul de operanzi și după tipul operației
- Folosirea corectă a împărțirii întregi și a operatorului modulo
- Distingerea operatorilor logici de cei pe biți
- Aplicarea regulilor de precedență și asociativitate
- Înțelegerea diferenței dintre preincrementare și postincrementare
- Controlul conversiilor implicite și explicite de tip
2Clasificarea operatorilor
Operatorii se pot grupa după numărul de operanzi:
| Categorie | Sintaxă | Exemplu |
|---|---|---|
| Unari | operator operand | -x, !ok, ~masca, ++i |
| Binari | op1 operator op2 | a + b, x < y, m & n |
| Ternar | op1 ? op2 : op3 | (a > b) ? a : b |
+, -,
* și & sunt definite atât ca operatori unari, cât și binari.
Interpretarea se face implicit, după numărul de operanzi: *p este dereferențiere,
iar a * b este înmulțire.Operatorii proprii limbajului C++, care nu există în C: new, delete,
operatorul de scop ::, pointerul la membru .* și forma sa echivalentă ->*.
3Operatori aritmetici
| Operator | Operație | Exemplu | Rezultat |
|---|---|---|---|
+ | adunare | 7 + 2 | 9 |
- | scădere | 7 - 2 | 5 |
* | înmulțire | 7 * 2 | 14 |
/ | împărțire | 7 / 2 | 3 (întreagă!) |
/ | împărțire reală | 7.0 / 2 | 3.5 |
% | rest (modulo) | 7 % 2 | 1 |
++ | incrementare | i++ | i crește cu 1 |
-- | decrementare | i-- | i scade cu 1 |
int medie = suma / n; trunchiază. Pentru rezultat real,
cel puțin un operand trebuie să fie real: (float)suma / n.7.5 % 2
este eroare de compilare. Este util pentru: testarea parității (n % 2 == 0),
extragerea cifrelor (n % 10) și limitarea ciclică a unui indice (i % dim).4Operatori relaționali și logici
| Operator | Semnificație | Exemplu | Rezultat |
|---|---|---|---|
== | egal cu | 3 == 3 | 1 (adevărat) |
!= | diferit de | 3 != 3 | 0 (fals) |
< > <= >= | comparații | 2 <= 5 | 1 |
&& | ȘI logic | (a>0) && (b>0) | 1 dacă ambele |
|| | SAU logic | (a>0) || (b>0) | 1 dacă cel puțin una |
! | NU logic | !0 | 1 |
if (x = 5) atribuie 5 lui x și rezultatul
este 5, adică adevărat - condiția este mereu îndeplinită. Corect este if (x == 5).
Compilatorul acceptă ambele forme, deci eroarea nu este semnalată.&&, dacă primul operand este fals,
al doilea nu se mai evaluează. La ||, dacă primul este adevărat, al doilea se sare.
Se folosește pentru protecție: if (p != NULL && *p > 0) - dereferențierea are loc
doar dacă pointerul este valid.5Operatori pe biți
| Operator | Nume | Efect pe fiecare bit | Utilizare tipică |
|---|---|---|---|
& | ȘI pe biți | 1 doar dacă ambii biți sunt 1 | testarea / mascarea biților |
| | SAU pe biți | 1 dacă cel puțin un bit este 1 | setarea biților |
^ | SAU exclusiv | 1 dacă biții diferă | comutarea biților |
~ | negare | inversează fiecare bit | construirea măștilor |
<< | deplasare stânga | mută biții spre stânga | înmulțire rapidă cu 2ⁿ |
>> | deplasare dreapta | mută biții spre dreapta | împărțire rapidă la 2ⁿ |
1 & 2 dă 0 (biții nu se suprapun),
dar 1 && 2 dă 1 (ambele valori sunt nenule, deci adevărate).
Confuzia dintre cele două produce erori logice foarte greu de găsit.6Laborator de biți
Comutați biții direct pe reprezentarea binară și urmăriți cum se formează rezultatul,
poziție cu poziție. Încercați în special << și >> pentru a vedea
echivalența cu înmulțirea și împărțirea la puteri ale lui 2.
7Precedență și asociativitate
| Nivel | Operatori | Asociativitate |
|---|---|---|
| 1 (maxim) | () [] -> . | stânga → dreapta |
| 2 | ! ~ ++ -- +/- unari, * & sizeof (cast) | dreapta → stânga |
| 3 | * / % | stânga → dreapta |
| 4 | + - | stânga → dreapta |
| 5 | << >> | stânga → dreapta |
| 6 | < <= > >= | stânga → dreapta |
| 7 | == != | stânga → dreapta |
| 8 | & | stânga → dreapta |
| 9 | ^ | stânga → dreapta |
| 10 | | | stânga → dreapta |
| 11 | && | stânga → dreapta |
| 12 | || | stânga → dreapta |
| 13 | ?: | dreapta → stânga |
| 14 (minim) | = += -= *= /= %= | dreapta → stânga |
x & 1 == 0 se evaluează ca x & (1 == 0),
adică x & 0, care este mereu 0. Corect: (x & 1) == 0.
Când aveți dubii, folosiți paranteze - nu costă nimic la execuție.8Simulator: preincrementare vs. postincrementare
Diferența dintre ++i și i++ apare doar când rezultatul
expresiei este folosit. Urmăriți pas cu pas ce valoare are variabila și ce valoare are expresia.
i = i++ + ++i; nu au
un rezultat garantat de standard - compilatoare diferite produc valori diferite. Nu modificați
niciodată aceeași variabilă de două ori în cadrul aceleiași expresii.9Conversii de tip
Când operanzii au tipuri diferite, compilatorul convertește automat spre tipul „mai încăpător":
char / short -> int -> unsigned int -> long -> float -> double
| Expresie | Tip rezultat | Valoare |
|---|---|---|
7 / 2 | int | 3 |
7 / 2.0 | double | 3.5 |
(float)7 / 2 | float | 3.5 |
(float)(7 / 2) | float | 3.0 - conversia are loc prea târziu |
'A' + 1 | int | 66 |
int i = -1; unsigned u = 1; if (i < u) este fals! Valoarea −1 se convertește
la unsigned și devine 4 294 967 295, mai mare decât 1. Evitați amestecul celor două tipuri
în comparații.10Cod sursă
#include <stdio.h>
int main(void)
{
int a = 7, b = 2;
printf("a / b = %d\n", a / b); // 3 - trunchiat
printf("a %% b = %d\n", a % b); // 1 - restul
printf("(float)a/b = %.3f\n", (float)a / b); // 3.500 - corect
printf("(float)(a/b)= %.3f\n", (float)(a / b)); // 3.000 - prea tarziu!
// extragerea cifrelor unui numar
int n = 4739, cifra;
printf("\nCifrele lui %d, de la dreapta: ", n);
while (n > 0) {
cifra = n % 10; // ultima cifra
n = n / 10; // elimina ultima cifra
printf("%d ", cifra);
}
printf("\n");
return 0;
}
#include <stdio.h>
void afiseazaBinar(unsigned char v)
{
for (int i = 7; i >= 0; i--)
printf("%d", (v >> i) & 1);
printf("\n");
}
int main(void)
{
unsigned char reg = 0b00001010; // 10 zecimal
printf("initial: "); afiseazaBinar(reg);
reg |= (1 << 4); // SETEAZA bitul 4
printf("dupa set b4: "); afiseazaBinar(reg);
reg &= ~(1 << 1); // STERGE bitul 1
printf("dupa clear b1: "); afiseazaBinar(reg);
reg ^= (1 << 0); // COMUTA bitul 0
printf("dupa toggle b0: "); afiseazaBinar(reg);
// TESTEAZA un bit
if (reg & (1 << 4))
printf("bitul 4 este setat\n");
// inmultire si impartire rapida
int x = 12;
printf("\n%d << 2 = %d (adica %d * 4)\n", x, x << 2, x);
printf("%d >> 2 = %d (adica %d / 4)\n", x, x >> 2, x);
return 0;
}
#include <stdio.h>
int main(void)
{
int a = 17, b = 42;
int maxim = (a > b) ? a : b; // echivalent cu un if-else
printf("Maximul este %d\n", maxim);
// se pot inlantui, dar lizibilitatea scade rapid
int n = 0;
printf("Numarul este %s\n",
(n > 0) ? "pozitiv" : (n < 0) ? "negativ" : "zero");
// atentie la precedenta: parantezele NU sunt optionale aici
printf("Suma: %d\n", a + ((a > b) ? a : b));
return 0;
}
11Atelier de cod
Operatorii au reguli de precedență și efecte laterale care se rețin greu din text. Rulați și, mai ales, folosiți Pas cu pas ca să vedeți când anume se schimbă fiecare variabilă.
12Sarcini de lucru
- Calculați media aritmetică a trei numere întregi, obținând un rezultat real corect.
- Scrieți un program care extrage și afișează cifrele unui număr, folosind
%și/. - Implementați funcția de afișare binară a unui octet și verificați-o pentru valorile 0, 1, 128 și 255.
- Aplicați cele patru operații clasice pe biți (set, clear, toggle, test) asupra unei variabile.
- Verificați experimental că
x & 1 == 0dă alt rezultat decât(x & 1) == 0. - Testați diferența dintre
++ișii++într-unprintf. - Comparați un
intnegativ cu ununsignedpozitiv și explicați rezultatul. - Rescrieți o structură
if-elsesimplă folosind operatorul ternar.
13Aplicație de aprofundare
if, fără înmulțire sau împărțire), determină: dacă un număr este par, dacă este
putere a lui 2, câți biți de 1 conține și care este poziția celui mai semnificativ bit setat.
Indiciu pentru puterea lui 2: n & (n - 1) este 0 doar în acest caz.