LABORATORUL 04

Operatori și expresii

Durată: 2 ore Limbaj: C / C++ Precedent: Laboratorul 3 PDF îndrumar EN English version

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

a + b * c > d>+da*bcse evalueaza ultimulse evalueaza primul
Fig. - Arborele de evaluare al expresiei. Precedenta decide forma arborelui: inmultirea leaga mai strans decat adunarea, iar comparatia mai slab decat amandoua - deci se aplica ultima.
  • 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:

CategorieSintaxăExemplu
Unarioperator operand-x, !ok, ~masca, ++i
Binariop1 operator op2a + b, x < y, m & n
Ternarop1 ? op2 : op3(a > b) ? a : b
Operatori cu dublă semnificațieSimbolurile +, -, * ș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

OperatorOperațieExempluRezultat
+adunare7 + 29
-scădere7 - 25
*înmulțire7 * 214
/împărțire7 / 23 (întreagă!)
/împărțire reală7.0 / 23.5
%rest (modulo)7 % 21
++incrementarei++i crește cu 1
--decrementarei--i scade cu 1
Împărțirea întreagăDacă ambii operanzi sunt întregi, rezultatul este întreg, iar partea fracționară se pierde. int medie = suma / n; trunchiază. Pentru rezultat real, cel puțin un operand trebuie să fie real: (float)suma / n.
Operatorul %Se aplică numai operanzilor întregi. 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

OperatorSemnificațieExempluRezultat
==egal cu3 == 31 (adevărat)
!=diferit de3 != 30 (fals)
< > <= >=comparații2 <= 51
&&ȘI logic(a>0) && (b>0)1 dacă ambele
||SAU logic(a>0) || (b>0)1 dacă cel puțin una
!NU logic!01
Confuzia = cu ==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ă.
Evaluarea în scurtcircuitLa &&, 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

OperatorNumeEfect pe fiecare bitUtilizare tipică
&ȘI pe biți1 doar dacă ambii biți sunt 1testarea / mascarea biților
|SAU pe biți1 dacă cel puțin un bit este 1setarea biților
^SAU exclusiv1 dacă biții diferăcomutarea biților
~negareinversează fiecare bitconstruirea măștilor
<<deplasare stângamută biții spre stângaînmulțire rapidă cu 2ⁿ
>>deplasare dreaptamută biții spre dreaptaîmpărțire rapidă la 2ⁿ
& nu este &&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.

Operatori pe biți - reprezentare binară interactivă

7Precedență și asociativitate

NivelOperatoriAsociativitate
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
Consecință practicăOperatorii pe biți au precedență mai mică decât cei relaționali. Expresia 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.

Cum se evaluează ++i și i++
Comportament nedefinitExpresii de forma 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":

ierarhia conversiilor implicite
char / short  ->  int  ->  unsigned int  ->  long  ->  float  ->  double
ExpresieTip rezultatValoare
7 / 2int3
7 / 2.0double3.5
(float)7 / 2float3.5
(float)(7 / 2)float3.0 - conversia are loc prea târziu
'A' + 1int66
Comparația între signed și unsigned 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ă

impartire.c - întreagă vs. reală
#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;
}
biti.c - tehnici clasice cu măști
#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;
}
ternar.c - operatorul condițional
#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ă.

Incrementare prefixată și postfixată
#include <stdio.h>

int main(void)
{
    int a = 5, b;

    b = a++;                  /* mai intai atribuie, apoi incrementeaza */
    printf("dupa b = a++ :  a = %d, b = %d\n", a, b);

    a = 5;
    b = ++a;                  /* mai intai incrementeaza, apoi atribuie */
    printf("dupa b = ++a :  a = %d, b = %d\n", a, b);

    int x = 10;
    printf("\nx / 3   = %d   (impartire intreaga)\n", x / 3);
    printf("x %% 3   = %d   (restul)\n", x % 3);
    printf("x / 3.0 = %.4f (un operand real -> rezultat real)\n", x / 3.0);
    return 0;
}
Operatori pe biți - exercițiu
#include <stdio.h>

/* Afiseaza cei 8 biti ai unei valori, de la bitul 7 la bitul 0 */
void afiseazaBiti(unsigned char v)
{
    int i;
    for (i = 7; i >= 0; i--)
        printf("%d", (v >> i) & 1);
    printf("\n");
}

int main(void)
{
    unsigned char a = 0x2C;   /* 0010 1100 */

    printf("a       = "); afiseazaBiti(a);
    printf("a | 0x03= "); afiseazaBiti(a | 0x03);
    printf("a & 0x0F= "); afiseazaBiti(a & 0x0F);

    /* Completati: setati bitul 7 al lui a, apoi stergeti bitul 2.
       Indicii: pentru setare folositi |= (1 << 7)
                pentru stergere folositi &= ~(1 << 2)   */

    printf("rezultat= "); afiseazaBiti(a);
    return 0;
}

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 == 0 dă alt rezultat decât (x & 1) == 0.
  • Testați diferența dintre ++i și i++ într-un printf.
  • Comparați un int negativ cu un unsigned pozitiv și explicați rezultatul.
  • Rescrieți o structură if-else simplă folosind operatorul ternar.

13Aplicație de aprofundare

ExtindereScrieți un program care, folosind exclusiv operatori pe biți (fără 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.

14Întrebări de verificare

15Resurse