Curs 3: Operatori C++

În cursul anterior am studiat variabilele, iar acum suntem pregătiți pentru a analiza operatorii C++.

Însă, înainte de a începe discuția propriu-zisă, este necesară o mică precizare despre structura programelor C. Aceste programe sunt formate din instrucțiuni, care sunt: declararea variabilelor ( int x; ), atribuirile ( x=20; ) -deja studiate-, și structurile decizionale (if = dacă) și repetitive (while = cât timp este doar una dintre ele) și încă câteva. Observați că după orice instrucțiune se pune ; (punct și virgulă).

Ceea ce vom studia azi nu sunt instrucțiuni propriu-zise, ci expresii; vom învăța să creăm instrucțiuni mai dezvoltate decât atribuiri puerile, cu ajutorul operatorilor. Vom spune despre expresii (și mai târziu, despre funcții) că returneză o valoare.

Operatorii sunt o parte destul de naturală a unui limbaj de programare, și probabil nu necesită foarte multe explicații. Ei se clasifică în mai multe categorii:

Operatori de incrementare și decrementare

Sunt doar doi operatori:

  • Operatorul de incrementare ++
  • Operatorul de decrementare —

Cei doi măresc, respectiv scad valoarea variabilei cu o unitate. Au două forme: prefixat (++a sau –a) și postfixat (a++ sau a–). Diferența dintre cele două forme este aceea că operatorul prefixat modifică valoarea variabilei înainte de evaluare expresiei, iar cel postfixat, după. Cu alte cuvinte, dacă declarăm o variabilă int a=5; instrucțiunile a++ și ++a au exact același efect, mărind valoarea lui a cu 1. Dacă considerăm însă încă o variabilă int b; și considerăm instrucțiunile b=++a; și b=a++; atunci efectele celor două va fi diferit. Anume:

  • Pentru b=++a;

Operatorul de incrementare este prefixat deci valoarea lui a va fi mărită înainte de atribuire:

  1. Valoarea lui a este inițial 5
  2. Valoarea lui a este incrementată, a=6
  3. În b se pune valoarea 6(=a)

La finalul execuției algoritmului: a=6, b=6.

  • Pentru b=a++;

Operatorul de incrementare este postfixat deci valoarea lui a va fi modificată după atribuire:

  1. Valoarea lui a este inițial 5
  2. În b se pune valoarea 5(=a)
  3. Valoarea lui a este incrementată, a=6

La finalul execuției algoritmului: a=6, b=5.

Evident, exact la fel se vor desfășura lucrurile pentru operatorul de decrementare.

Operatori matematici

De departe cei mai simpli, sunt 5:

  1. Operatorul de adunare (+)
  2. Operatorul de scădere (-)
  3. Operatorul de înmulțire (*)
  4. Operatorul de împărțire (/): acest operator are sensul de div (returneză câtul împărțitii lui a la b) dacă este aplicat la 2 numere întregi, sau de împărtire propriu-zisă, dacă este aplicat la două numere raționale.
  5. Operatorul mod (%): a%b returnează restul împărțirii lui a la b.

Atribuiri ciclice

Înainte de a continua trebuie discutate atribuirile ciclice. Ce va face instrucțiunea a=a;? Nimic? Răspuns greșit! De fapt, va așeza în variabila a valoarea din variabila a și, chiar dacă starea finală este identică cu cea inițială, spunem că programul a executat niște transformări. Dar instrucțiunea a=a+4? Va da eroare? Nu! Atunci când se execută o atribuire, programul calculează mai întâi membrul drept, apoi așază valoarea obținută în variabila din membrul stâng. Spre exemplu, pentru instrucțiunea de mai sus si declararea int a=7; se vor efectua următorii pași:

  • Programul găsește în algoritm un apel către variabila a și caută în memorie valoarea ei (7)
  • Programul calculează valoarea expresiei a+4 (7+4=11)
  • Programul așază în variabila a (care până acum era 7) valoarea 11

La finalul execuției programului, valoarea variabilei a ajuns de la 7 la 11, deci a crescut cu 4.

Operatorul de conversie explicită

Un alt operator destul de important este Operatorul de conversie explicită, care are forma generală (tip_dată)expresie și care va converti tipul de dată imediat din dreapta lui și, implicit, întreaga expresie la tipul de dată dintre paranteze. Spre exemplu, vom analiza următoarele două mici programe:

#include <iostream>

using namespace std;

int main()
{
    int a=88,b=7;
    cout<<a/b;
    return 0;
}

Acest program va afișa când îl rulăm valoarea 12, adică câtul obținut prin împărțirea lui 88 la 7. Dacă folosim însă operatorul de conversie explicită astfel:

#include <iostream>

using namespace std;

int main()
{
    int a=88,b=7;
    cout<<(float)a/b;
    return 0;
}

Programul va afișa 12,5714, deoarece operatorul de conversie explicită conversește data a din int în float, valoarea returnată de expresie devenind de asemenea număr rațional, operația efectuată fiind deci împărțirea propriu-zisă.

În cazul împărțirii, există o metodă mult mai simplă de a transforma operatorul div în operatorul de împărțire: (float)a/3 este echivalent cu a/3.00. În primul caz, valoarea convertită la tipul real este numărul conținut în variabila a, iar în al doilea, numărul 3, scris sub forma 3.00, devine număr real. Observați însă că această metodă funcționează doar atunci când unul dintre operanzi este o constantă; dacă ambii sunt variabile de tip întreg, atunci operatorul de conversie explicită este singurul care va funcționa.

Operatori relaționali

Sunt reprezentați de <, >, ≥, ≤, =, \(\ne\). Scrierea lor în C este puțin diferită:

< \(\iff\) <, > \(\iff\) >, ≤ \(\iff\) <=, ≥ \(\iff\) >=, = \(\iff\) ==, \(\ne \iff\) !=

O expresie care conține operatori relaționali va returna valoarea 1(True) dacă egalitatea/inegalitatea este adevărată, și 0(False) dacă este falsă.

Operatori logici

Se aplică pentru două expresii și nu sunt decât 3. Expresiile care se formează cu aceștia pot returna valori Booleane (True/False).

  1. ȘI logic (&&): Are valoarea True dacă ambele sunt True și False în toate celelalte cazuri.  Este o chestie, de fapt, foarte naturală: propoziția „Maria mănâncă ciocolată și caramel” este adevărată dacă Maria mănâncă ambele dulciuri simultan, dacă mănâncă doar una sau chiar niciuna, atunci va fi falsă. Considerată o propoziție enunțiativă, informația extrasă din ea este că Maria mănâncă ambele dulciuri (și că este cam dolofană 🙂 ).
  2. SAU logic (||)Are valoarea True dacă cel puțin una din cele două expresii este adevărată (are valoarea True). Spre exemplu, din propoziția „Maria mănâncă ciocolată sau caramel” înțeleg că Maria mănâncă ciocolată sau caramel, doar una sau poate amândouă.
  3. Negarea logică(!): Este un operator unar și inversează valoarea de adevăr a expresiei:Dau mai jos câteva exemple:
  • 3<6 && 7>=4 va reuturna True, fiindcă ambele inegalități sunt adevărate
  • 4>8 && 2*6<54 va returna False, fiindcă expresia din stânga nu este adevărată
  • 15>4 || 4>-5 va returna True, fiindcă ambele expresii sunt adevărate (era suficientă doar una adevărată)
  • 4<=2 || !(2*4==8) va returna true, fiindcă expresia din stânga este adevărată (și expresia din paranteză este adevărată, dar este negată cu operatorul specific)
  • 16>4*7 || 32<6 va returna false, fiindcă ambele expresii sunt false
Legile DeMorgan

Se referă la cum afectează operatorul de negare pe ceilalți doi operatori logici. În cele două legi apar termenii conjuncție și disjuncție. Conjuncția a două expresii E1 și E2 este E1 && E2, iar disjuncția, E1 || E2.

Legea 1: Negatia disjuncției este conjuncția negațiilor.

!(E1 || E2) \(\iff\) !E1 && !E2

Legea 2: Negatia conjuncției este disjuncția negațiilor.

!(E1 && E2) \(\iff\) !E1 || !E2

Operatori logici pe biți

Sunt operatori specifici limbajului de programare C și, de departe, cei mai bizari. Se bazează pe scrierea numerelor întregi în baza 2 și pe operațiile logice care pot fi efectuate cu valorile 0 și 1 (adevărat sau fals). Prin urmare, aceste operații pot fi efectuate doar pe variabile de tip întreg.

Operatorii logici pe biți sunt 4:

  1. ȘI pe biți (&): Este un operator binar (se aplică câtor două numere) și efectuează operația logică și pe fiecare bit al celor două numere. Acestă operație este caracterizată în următorul tabel:Operația a&b are, deci, valoarea True dacă și a, și b au valoarea True, și fals în toate celelalte cazuri. Aplicată pentru două variabile întregi, operatorul va aplica operația definită mai sus, pe rând, fiecărui bit al celor două numere. Spre exemplu, pentru numere 12 și 10, cu scrierile în baza 2 1100, respectiv 1010, operația va arăta astfel:Se va obține numărul 8.Observație: Atunci când utilizați operatori pe biți, este o idee bună să puneți operația între paranteze rotunde, deoarece aceștia au prioritate mai mică decât celelalte operații.
  2. SAU pe biți (|): Operația sau este definită în următorul tabel:Această operație are valoarea True dacă măcar unul din cele două valori este True și False dacă ambele au valoare False. Un exemplu de aplicare a acestei operații este mai jos, pentru numerele 9 și 5, cu scrierea binară 1001, respectiv 101:
  3. SAU EXCLUSIV pe biți: Această operație se mai numește și xor șieste foarte simplă, deși pare puțin mai complexă. O definesc mai jos:Va avea valoarea True dacă cele două valori comparate sunt diferite, și False dacă sunt identice. În limbaj natural, acest operator este reprezentat prin fie: „Maria mănâncă fie ciocolată, fie caramel”. Această propoziție va fi adevărată dacă Maria mănâncă doar una din cele două, și falsă dacă le mănâncă pe amândouă sau niciuna.Aplicarea acestui operator pe exemplul 12 și 10, cu scrierile binare 1100, respectiv 1010, este:Se va obține valoarea 6.
    În practică, acest operator se poate folosi pentru criptare, dar este o metoda de criptare foarte slabă, bazându-se pe proprietatea că (a^b)^b=a.
  4. Negarea pe biti (~): Acesta este un operator pe biți unar, adică se aplică unei singure valori, negând fiecare bit al numărului prelucrat. Definim operația astfel:Această operație este însă mai specială, fiindca ea va modifica toți biții tipului de dată dat, nu numai cei semnificativi. Vom considera ca exemplu numărul 38 (cu scrierea binară 100110) reprezentat în memorie pe o dată short int (reprezentată în memorie pe 2 octeți), astfel: Negând această structură, toți biții egali cu 0 se vor transforma în 1 și invers:Probabil veți crede că numărul obținut este 65.497, dar de fapt este -39.

    În schimb, dacă numărul ales mai sus (38) era reprezentat într-o dată de tip unsigned short int (2 octeți) s-ar fi obținut în memorie aceeași reprezentare, dar interpretarea ei era cea uzuală, obținându-se numărul 65.497.

    De fapt, și celelalte 3 operații pe biți iau în considerare toți biții și nu doar cei semnificativi, doar că negarea este singura operație capabilă de a transforma True în False (celelalte nu au niciodată ca rezultat True când cele 2 valori de prelucat sunt amândouă False).

  5. Shift left (<<): Operatorul shift left este un operator binar care va muta toți biții numărului un număr de poziții către stânga. Spre exemplu, 11<<3:
    Astfel, se va obține în memorie 00000000 01011000, adică 88(\(88=8*2^3\)). Biții care rămân goi după mutare sunt automat ocupați cu 0. Observăm că acest operator este echivalent cu înmulțirea cu o putere a lui 2: \(a<<b \iff a=a*2^b\).
  6. Shift right (>>): Operatorul Shift Right este foarte asemănător cu Shift Left, doar că mută biții către dreapta. Exemplu: 26>>3:

Se va obține în memorie 00000000 00000011, adică numărul 3. Desigur, se vor pierde biți.

A nu se confunda operatorii Shift right (>>) și Shift left (<<) cu operatorii care se folosesc la citiri (cin>>) și afișări (cout<<).

Prioritatea operatorilor

Dacă într-o expresie apar operatori de mai multe tipuri, ordinea în care se vor evalua este cea în care au fost prezentați:

  1. Operatorii de incrementare și decrementare prefixați
  2. Operatori matematici
  3. Operatori relaționali
  4. Operatori logici
  5. Operatori logici pe biți
  6. Operatorii de incrementare și decrementare postfixați (care se execută chiar după atribuire)

Problemele care apar prin nerespectarea priorității operatorilor sunt similare cu cele de la matematica elementară, unde 3+3*2 dă 9, nu 12. De aceea, este o idee bună să folosiți paranteze, cu observația că limbajul C nu acceptă decât paranteze rotunde ca în exemplul: a=5+(3*(2+6));.

Aplicație

Interschimbarea a două variabile: Avem două variabile a și b cu două valori distincte și dorim ca, la finalul execuției algoritmului, să avem valorile lor inversate.

  1. Prin adunare și scădere:
    int a=7,b=5;
    a=a+b;//a=12,b=5
    b=a-b;//a=12,b=7
    a=a-b;//a=5,b=7
  2. Prin înmulțire și împărțire
  3. Cu operatorul sau exclusiv pe biți (cea mai rapidă metodă)

Acum cunoștiințele sunt suficiente pentru a rezolva probleme de clasa a 9-a cu operatori și expresii. Este o idee bună să vezi și acest articol.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *

Acest site folosește Akismet pentru a reduce spamul. Află cum sunt procesate datele comentariilor tale.

octombrie 2018
L Ma Mi J V S D
1234567
891011121314
15161718192021
22232425262728
293031