Documente Academic
Documente Profesional
Documente Cultură
I. Operatori şi expresii
O succesiune nedeterminată de operanzi şi operatori defineşte o expresie. Structura
acesteia depinde de prelucrările pe care urmează să le facă utilizatorul. Evaluarea
expresiei constă în efectuarea tuturor operaţiilor componente după reguli bine stabilite.
Rezultatul obţinut depinde atît de tipul operanzilor, cît şi de operatorii care acţionează
asupra acestora. Valorile operanzilor sînt utilizate fie direct, fie după realizarea unor
conversii impuse de semnificaţia operatorului care se execută asupra operanzilor. De
asemenea, un rol predominant îl au poziţia şi ordinea operatorilor în expresie. Vom
vedea ulterior (ca de exemplu în cazul operatorilor de incrementare şi decrementare),
poziţia operatorului faţă de operand permite ataşarea la numele acestuia unul din
atributele prefixat, infixat sau postfixat. În absenţa parantezelor, ordinea de efectuare a
operatorilor este determinată de apartenenţa operanzilor la diverse clase de precedenţă.
Pentru toate categoriile de operatori descrişi în subsecţiunile următoare, ordinea claselor
de precedenţă este dată în mod descrescător. Pentru priorităţi egale în cadrul unei clase de
precedenţă este precizată o regulă de asociativitate, care arată ordinea aplicării
operatorilor consecutivi. În cele mai multe cazuri, aceasta este de la stînga către dreapta.
O sinteză a acestora este prezentată în tabelul de la sfîrşitul secţiunii.
Spre deosebire de alte limbaje de programare de nivel înalt, limbajul C pune la dispoziţia
utilizatorului o gamă variată de tipuri de operatori: aritmetici, relaţionali, logici de bază şi
la nivel de bit, de deplasare, de incrementare şi decrementare, de asignare, de adresare,
condiţionali, de secvenţiere (operatorul virgulă), de lungime (sizeof), operatorul de
conversie (cast) şi operatorul de rezoluţie (de acces, de domeniu). Caracteristici
programării orientată spre obiect sînt operatorii de alocare dinamică a memoriei new şi
delete. Unele simboluri utilizate în notaţia operatorilor coincid cu cele din matematică,
altele sînt specifice programării calculatoarelor.
O parte dintre operatorii aritmetici şi logici sînt binari, +(adunare), -(scădere),
*(înmulţire), /(împărţire), %(modulo) şi respectiv &&(conjuncţie), ||(disjuncţie),
^(disjuncţie exclusivă), &(conjuncţie la nivel de bit), |(disjuncţie la nivel de bit).
Operatorii de negaţie (!, ~), de incrementare (++), de decrementare (--), sizeof, +, -, cast,
de rezoluţie (::), new şi delete sînt unari, iar operatorul condiţional este ternar.
Operatorul de rezoluţie va face obiectul secţiunii 4.3.2, operatorii new şi delete se vor
prezenta în secţiunea 7.5, iar restul operatorilor se vor analiza în cele ce urmează.
Combinînd operanzi de tipuri diferite cu operatori corespunzători, este posibil ca tratarea
unor situaţii, din contextul de rezolvare al problemei date, să conducă la scrierea unei
singure expresii complexe, care s-ar fi codificat, altfel, printr-o secvenţă de instrucţiuni
de atribuire. Evaluarea unei astfel de expresii poate să fie cu efecte laterale, determinate
prin schimbarea valorii unei variabile sau a valorilor mai multor variabile.
1
Un caz particular de expresie îl constituie aceea care se reduce la o constantă, un nume de
variabilă, un element de tablou, un cîmp sau un apel de funcţie, denumită şi expresie
simplă.
2
printf("\n 5*a*b-c=%f", 5*a*b-c);
printf("\n 5*a*b-c-x/y=%f", 5*a*b-c-x/y);
printf("\n 5*a*b-c-x/y+a*y=%f", 5*a*b-c-x/y+a*y);
getch(); }
Valoarea expresiei 5*a*b-c-x/y+a*y este: 85.000000
Desfasurarea operatiilor:
1. 5*a=15.000000 4. a*y=12.000000
2. 5*a*b=75.000000 5. 5*a*b-c=73.00000
3. x/y=0.000000 6. 5*a*b-c-x/y=73.000000
7. 5*a*b-c-x/y+a*y=85.000000
Programul 7.1.1
Observaţie. Operaţia de ridicare la putere (xy) se codifică, fie prin funcţia pow(x,y), fie
prin expresia exp(y*log(x)). Funcţiile predefinite pow, exp şi log sînt incluse în biblioteca
standard math.h.
3
Introduceţi x, y şi z 3 4 5 34 123 3
Val. exp. relat. 5*x+yşz<=x*x-y-z este : 0 1
Val. operand. 5*x+yşz este : 15 211
Val operand x*x-y-z este : 0 1030
Programul 7.2.1.
4
clrscr();
printf("\n x="); scanf("%f", &x);
printf(" y="); scanf("%f", &y);
printf("\n Val exp. relat. (x+3)==(y-1) este :%d",
(x+3)==(y-1));
printf("\n Val exp. logice !x este :%d", !x);
printf("\n Val exp. logice x*x && 7 este :%d", x*x && 7);
printf("\n Val exp. logice x*x && 7 && !x este :%d",
x*x && 7 && !x);
printf("\n Val exp. logice x*x && 7 && !x && 0 este :%d",
x*x && 7 && !x && 0 );
printf("\n Val. exp. logice\
((x+3)==(y-1) || x*x && 7 && !x && 0) este : %d",
(x+3)==(y-1 )|| x*x && 7 && !x && 0);
getch(); }
Val. exp. relat. (x+3)==(y-1) este : 0
Val exp. logice !x este : 0
Val exp. logice x*x && 7 este : 1
Val exp. logice x*x && 7 && !x este : 0
Val exp. logice x*x && 7 && !x && 0 este : 0
Val. exp. logice ((x+3)==(y-1) || x*x && 7 && !x && 0) este : 0
Programul 7.3.1.
5
Tabelul 7.4.1
În continuare, cîteva aplicaţii simple se pot verifica, imediat, prin consultarea
tabelului de mai sus: 1010 & 1100 = 1000, 10000 | 110 = 10110 şi ~10 = 01.
Diferenţa dintre cele două categorii de operatori logici se ilustrează prin următorul
exemplu: dacă a şi b au valorile 2 şi respectiv 3, atunci a&&b = 1, iar a&b = 3. Anexa 3
cuprinde codificarea cifrelor octale şi hexazecimale în binar pe 3 şi respectiv 4 poziţii.
În general, operatorii & şi | sînt utilizaţi pentru a masca anumite poziţii într-o
înşiruire de cifre binare. Anularea biţilor care interesează, cu reţinerea celorlalţi şi
respectiv schimbarea unor biţi 0 în 1, cu menţinerea neschimbată a celorlaţi, sînt aşa
numitele operaţii de “mascare”.
Operatorii de deplasare intervin în construcţii cu următoarea sintaxă:
operand << nr_poziţii şi operand >> nr_poziţii.
După cum s-a precizat deja, operandul este, fie o constantă întreagă, fie o variabilă de tip
întreg. Numărul de poziţii (nr_poziţii) precizează cu cîte poziţii binare se deplasează spre
stînga şi respectiv spre dreapta valoarea operandului în codificare binară. În fapt, numai
operandul este prelucrat la nivel de bit.
La o deplasare către stînga, biţii semnificativi se pierd, iar în dreapta se inserează un
număr egal cu nr_poziţii de cifre binare 0. Dacă tipul operandului este unsigned, atunci
deplasarea este echivalentă cu o înmulţire a valorii operandului cu 2nr_poziţii. Altfel, semnul
valorii poate fi afectat. Dacă nr_poziţii > sizeof(operand)*8, atunci rezultatul unei
deplasări către stînga este 0.
La o deplasare spre dreapta, biţii corespunzători din dreapta se elimină, iar în stînga se
completează cu 0 sau 1 după cum tipul operandului este sau nu unsigned. În prima
situaţie, deplasarea este echivalentă cu împărţirea operandului prin 2nr_poziţii, iar în cea de a
doua, cu păstrarea semnului.
De exemplu, dacă a=001100 atunci a<<2=110000, iar a>>2=000011.
6
parantezelor nu este obligatorie, însă utilizarea acestora se recomandă în situaţii care pot
genera confuzii. Atribuirea combinată de genul: a*= b-5; se interpretează a = a*(b-5); şi
nu a = a*b-5;.
Pînă la limbajul C, atribuirea, cu sintaxa generală:
nume_variabila=expresie;,
era tratată ca o instrucţiune de bază. Valoarea expresiei din membrul al doilea se memora
la adresa locaţiei afectată variabilei, după ce, în prealabil, se convertise la tipul variabilei
în cazul compatibilităţii lor, prin distrugerea valoarii anterioară a variabilei.
Acum, atribuirea se tratează ca o operaţie cu efecte laterale, concretizate în modificarea
valorii uneia sau a mai multor variabile, dar care furnizează şi un rezultat ce poate fi
utilizat în prelucrările ulterioare. Valoarea (sub)expresiei trebuie să fie compatibilă din
punct de vedere al atribuirii cu tipul variabilei. Aceasta se înscrie în zona de memorie
alocată variabilei, înlocuind valoarea existentă a variabilei respective. În cazul cînd tipul
valorii din membrul al doilea diferă de cel al variabilei, tipul valorii se converteşte la cel
al variabilei. Conversiile automate depind de tipul celor doi operanzi şi pot avea rezultate
diferite de la o implementare la alta. Efecte nedorite apar cînd domeniul de valori al
tipului celui de al doilea operand este mai larg decît cel al tipului variabilei. Acestea apar,
mai ales, în cazul atribuirilor de tip numeric. Ele constau în trunchieri ale cifrelor
semnificative, fără să fi fost semnalate şi deci, precizia rezultatelor este mai mult decît
precară. Conversiile cu efect negativ se numesc degradante. Tabelul 7.5.1 precizează
situaţia conversiilor degradante în cazul atribuirilor de tip aritmetic.
Dacă este necesar, cu sizeof(), se poate afla spaţiul de memorie afectat fiecărui tip
în parte pentru implementări ale limbajului pe diverse tipuri de calculatoare.
Tip variabilă Tip expresie Pierderi de informaţie
(octeţii mai semnificativi)
signed char unsigned char un număr negativ, dacă
valoarea >127
int long int 3
int float cel puţin partea fracţionară
float double se reduce precizia
( cu rotunjire)
double long double se reduce precizia
(prin rotunjire)
unsigned char int 1
unsigned char long int 3
Tabelul 7.5.1
Cu următoarele exemple se aduce un plus de clarificare la afirmaţiile de mai sus.
1. Dacă x este de tip întreg atunci, în urma efectuării expresiei de atribuire x = 5;, x
primeşte valoarea 5, deci rezultatul expresiei este 5.
2. Evaluarea expresiei de atribuire multiple x = y = z = 5; este echivalentă cu evaluarea
expresiilor de atribuire z = 15;, y = z; şi x= y;. Prin utilizarea parantezelor rotunde
expresia de atribuire devine: (x=(y=(z=15)));. În final, dacă variabilele sînt de tip întreg,
atunci în toate locaţiile de memorie corespunzătoare variabililor x, y şi z se înscrie
valoarea 15.
3. Într-o expresie de atribuire se pot introduce şi valorile afectate variabililor
componente, de exemplu: a=(b=2+(c=3));.
7
4. Aria bazei şi volumul unui con, cu raza r şi înălţimea h, pot fi calculate folosind o
expresie de atribuire de genul
volum=h * (aria_bazei=pi * r*r)/3; care este echivalentă cu secvenţa de instrucţiuni din
programarea tradiţională:
aria_bazei=pi*r * r;
volum=h * aria_bazei/3;.
5. Folosirea unei expresii de atribuire poate restrînge descrierea unor instrucţiuni de
decizie sau de repetare, cum ar fi :
while((c=getchar())!=’\0’) j-=2;. Echivalent, se poate scrie:
c=getchar();
while(c!=’\0’) j-=2;. Caracterul introdus de la tastatură prin funcţia getchar() se
atribuie lui c. Dacă acesta este diferit de ‘\0’, atunci j=j-2;.
Ultimele trei exemple reliefează utilitatea scrierii compacte şi imbricate a unor secvenţe
de prelucrări prin expresii de atribuire, care pentru începători poate, pe de o parte,
îngreuna lizibilitatea programului, iar pe de alta, facilita introducerea de erori.
Există totuşi situaţii în care nu trebuie să ne temem de aceste dezavantaje:
folosirea expresiilor de asignare în codificarea formulelor de recurenţă cu membrul stîng
conţinînd elemente de tablou cu indici expresii complexe sau forme complicate de
referiri de cîmpuri. De exemplu, tablou[i*j+5*k,i*j-5*k]+=10; este scrierea succintă
pentru expresia următoare: tablou[i*j+5*k,i*j-5*k]= tablou[i*j+5*k,i*j-5*k]+10;.
Programul 7.5.1 are ca scop simularea unor conversii degradante (inoportune) în
cazul expresiilor de atribuire de tip întreg. Limitele superioare sau inferioare ale
diferitelor domenii de valori întregi sînt exprimate în sistemul de numeraţie hexazecimal.
Operatorii multipli utilizaţi sînt += şi -=.
/* Operatorii de atribuire -
in conversii degradante de tip intreg */
#include <stdio.h>
#include <conio.h>
void main(void)
{ int a=0x7FFF;
short int d=0x0;
clrscr();
printf("\n a= %0x a+1000= %d",a,a+=1000);
printf("\n d= %0x d-1000= %d",d,d-=1000);
getch(); }
a=0x7FFF a+1000=-31769
d=0x0 d-1000=-1000
Programul 7.5.1