Sunteți pe pagina 1din 21

6.

Operatori i expresii

Expresiile sunt categorii sintactice fundamentale pentru limbajul C. Practic cele mai multe i
mai des folosite instruciuni sunt construite pe baza expresiilor. Expresiile reprezint un
mijloc extrem de puternic de manipulare a valorilor, pentru c instruciunile tuturor limbajelor
procedurale acioneaz asupra datelor prin intermediul acestora.

6.1 Evaluarea expresiilor


Singura operaie ce se poate efectua asupra expresiilor este cea de evaluare. n urma evalurii
unei expresii rezult ntotdeauna o valoare, care este utilizat n cadrul instruciunilor
limbajului ntr-un mod sau altul, n funcie de tipul instruciunii.
Din punct de vedere sintactic, o expresie este format din operatori, operanzi i
eventual perechi de paranteze rotunde. Operanzii reprezint elemente asupra crorra se poate
aciona, iar operatorii reprezint diferitele operaii ce se pot efectua asupra operanzilor.
Perechile de paranteze rotunde specific diferitele subexpresii ce sunt puse n eviden i care
sunt tratate de ctre compilator drept operanzi.
Operanzii pot fi constante, nume de variabile, nume de funcii sau subexpresii. Ei
genereaz valori fie direct (n cazul constantelor sau a variabilelor simple), fie dup realizarea
unei opraii de prelucrare.
Exemple.
2.5
a
v[3]
f(n)
a*(b+2)

x = 3

/*
/*
/*
/*

operand simplu */
operand simplu */
3 este operand simplu */
v este operand asupra caruia se efectueaza
operatia de indexare */
/* n este operand simplu */
/* f este operand asupra caruia se efectueaza
operatia de apel de functie */
/* doi operanzi:
- a este operand simplu,
- (b+2) este subexpresie
*/
/* doi operanzi simpli (x si 3);
Atentie: atribuirea este considerata
operator in limbajul C */

Limbajul C dispune de o mulime de operatori, fapt ce permite o mare flexibilitate n


descrierea prelucrrilor din cadrul programelor. Din punct de vedere al tipului operatorilor i
al semnificaiei lor, acetia se pot mpri n urmtaorele clase:
operatori aritmetici
operatori relaionali

operatori logici
operatori de atribuire
operatori de adrsare
operatori la nivel de bit
operatori specifici limbajului

n funcie de numrul de operanzi asupra crora se aplic, operatorii pot fi unari, binari,
ternari, sau n cazul general n-ari (se aplic asupra a n operanzi).
Exemple.
x-y
i--a-b
n?a:b

/* operator binar */
/* operator unar */
/* primul operator este unar */
al doilea este binar */
/* ?: este operator ternar */

Din punct de vedere al poziiei operatorilor fa de operanzi, acetia pot fi n poziie prefix
(preced operanzii), postfix (urmeaz dup operanzi) sau infix (operatorul se afl ntre
operanzi). Primele dou poziii sunt specifice operatorilor unari, pe cnd ultima este specific
celor binari.
Exemple.
n++
--i
a+b

/* operatorul ++ este in pozitie postfixa */


/* operatorul -- este in pozitie prefixa */
/* operatorul + este in pozitie infixa */

Pentru evaluarea unei expresii se iau n considerare alte dou proprieti ale operatorilor:
precedena i asociativitatea acestora. Precedena determin prioritatea operatorilor, iar
asociativitatea determin ordinea de aplicare a operatorilor consecutivi.
Pentru a determina ordinea de aplicare a operatorilor asupra operanzilor ntr-o expresie
fr paranteze, se au n vedere urmtoarele elemente:
nti se grupeaz operatorii n clase de preceden; ntr-o clas de preceden toi
operatorii au aceeai prioritate;
la evaluare, operatorii se aplic n ordinea descresctoare a precedenei;
n cadrul fiecrei clase, ordinea depinde de asociativitatea acestora (de la stnga la
dreapta sau invers).
Exemplu. Pentru expresia:
a + 2 * 3 + b + 4 + d * e * 5

clasele de preceden n ordinea descresctoare sunt:


1)
2 * 3, d * e * 5
asociativitate de la stnga la dreapta
2)
a + (2 * 3) + b + 4 + (d * e * 5)
asociativitate de la stnga la dreapta
Tabelul urmtor conine toi operatorii limbajului C grupai n ordinea descresctoare a
precedenei. Pentru fiecare clas se specific i regula de asociere.

Operator
()
[]
.
->
-++
+
-++
!
~
*
&
sizeof
()
*
/
%
+
<<
>>
<
<=
>
>=
==
!=
&
^
|
&&
||
?:
=
*=
/=
-=
+=
&=
^=
|=
<<=
>>=
,

Utilizare
f(e)
v[i]
r.c
p->c
a-a++
-n
+n
--a
++a
!i
~i
*p
&x
sizeof(x)
(d) e
v1 * v2
v1 / v2
i1 % i2
v1 + v2
v1 v2
i1 << i2
i1 >> i2
v1 < v2
v1 <= v2
v1 > v2
v1 >= v2
v1 == v2
v1 != v2
i1 & i2
i1 ^ i2
i1 | i2
i1 && i2
i1 || i2
i1 ? v1 :
v2
a = v
a *= v
a /= v
a -= v
a += v
a &= v
a ^= v
a |= v
a <<= v
a >>= v
e1, e2

Semnificaie
apel funcie
indexare
selecie
selecie indirect
postdecrementare
postincrementare
schimbare de semn
plus unar
predecrementare
preincrementare
negaie logic
negare la nivel de bit
adresare indirect
preluare adres
determinare dimensiune memorie
conversie de tip (cast)
nmulire
mprire
determinare rest (modulo)
Adunare
Scdere
deplasare stnga
deplasare dreapta
mai mic
mai mic sau egal
mai mare
mai mare sau egal
Egal
Diferit
i la nivel de bit
sau exclusiv la nivel de bit
sau la nivel de bit
i logic
sau logic
operator condiional

Asociere
---->

operatori de atribuire

<----

Secveniere

---->

<----

---->
---->
---->
---->

---->
---->
---->
---->
---->
---->
---->

n cazul n care o expresie conine subexpresii, se modific ordinea de aplicare a operatorilor.


Regula este simpl, are un caracter recursiv i descrie ordinea de aplicare a operaiilor
algebrice: dac ntr-o expresie exist subexpresii, acestea sunt evaluate naintea celorlali
operanzi.
e2

e1

Exemplu. Fie expresia: e = a * b c * (d e * f * ( g h))


- n expresia: a*b+c*e2, se evalueaz nti expresia e2;
- n expresia: d+e*f*e1, se evalueaz nti expresia e1;
- expresiile: a*b+c+e2, d+e*f*e1 se evalueaz conform regulilor de preceden;
Observaie. Se va nota n continuare cu Eval operaia de evaluare. De exemplu, pentru
expresia precedent, evaluarea ei se poate descrie astfel:
Eval(e) = Eval (a*b+c*(d+e*f*(g+h))) =
= Eval (a*b+c*Eval(d+e*f*Eval(g+h)))

Spre deosebire de alte limbaje de programare, n limbajul C evaluarea unei expresii poate
avea i efecte laterale datorate modificrii valorilor unor variabile n timpul evalurii. Acest
lucru se poate ntmpla datorit operatorului de atribuire.
Exemplu.
if ( x + ( y = v[k]) < a)
z = 1;
else
z = 2;

n expresia condiional anterioar, se evalueaz nti subexpresia y=v[k], care are drept
efect modificarea valorii variabilei y.
Aceast facilitate permite descrierea mai compact a unor secvene de prelucrri care
n mod normal necesit mai multe instruciuni de atribuire. n cazul utilizrii excesive a
acestui stil de programare, exist ns riscul srierii unor programe criptice, greu de neles i
nu ntotdeauna corecte.
Un alt aspect ce intervine n operaia de evaluare a unei expresii se refer la
conversiile automate ale tipurilor de date ale diferitelor valori ce intervin ntr-o expresie.
Exist mai multe motive ce impun conversii de tip, principala cuz fiind determinat de
tipurile de date diferite ale operanzilor asupra crora se aplic un operator.
Marea majoritate a operatorilor, n special cei aritmetici, necesit operanzi de acelai
tip, iar din acest motiv, n cazul n care operanzii sunt de tipuri diferite, este necesar (dac
este posibil) o operaie de conversie a valorii unui operand sau a ambilor operanzi. Acest
operaie se numete n mod uzual reechilibrare, regula de baz fiind urmtoarea:
dac operanzii sunt de tip unsigned int i long atunci
dac valorile unsigned int pot fi reprezentate ca long atunci
tipul echilibrat este long
altfel
tipul echilibrat este unsigned int

altfel
tipul echilibrat este tipul unuia dintre cei doi operanzi,
care apare ultimul n lista:

int

unsigned int
long
unsigned long
float
double
long double

Tipul de date al valorii rezultate n urma evalurii unei expresii simple este stabilit n urma
operaiei de echilibrare.
Aa cum se observ, regula presupune existana a doi operanzi. n cazul operatorilor
unari, este evident faptul c nu este necesar operaia deechilibrare. n cazul operatorului
condiional, care este singurul operator ternar al limbajului C, ultimii doi operanzi sunt cei
care conteaz la determinarea tipului valorii rezultate.

6.2 Operatori aritmetici


Operatorii aritmetici se mpart n mod uzual n dou categorii: operatori aditivi, specifici
operaiilor de adunare i scdere, precum i operatori multiplicativi, specifici operaiilor de
nmulire i mprire. n plus, exist operatori de incrementare i decrementare, care
reprezint un caz particular al operatorilor aditivi.
Operatorii aditivi sunt reprezentai de caracterele + i i n mod uzual sunt operatori
binari. Aceleai caractere pot ns fi utilizate i pentru specificarea unor operatori unari:
operatorul de schimbare de semn (de exemplu: -4, -(a+b)),
operatorul unar plus, care nu are ns nici un efect (de exemplu +4)
Aceti operatori unari au o poziie prefix.
Operatorii multiplicativi sunt ntotdeauna binari:
operatorul * reprezint operaia de nmulire, fiind utilizat att pentru numerele
ntregi, ct i pentru cele reale;
operatorul / reprezint operaia de mprire, dar are o semnificie distinct pentru
numerele ntregi:
n cazul n care cel puin un operand este real, operatorul reprezint operaia de
mprire real;
n cazul n care ambii operanzi sunt ntregi, operatorul reprezint operaia de
determinare a ctului mpririi celor doi operanzi; De exemplu:
-

Eval(2.0/4.0) = 0.5
Eval(2/4.0) = 0.5
Eval(2/4) = 0

operatorul % este utilizat doar pentru operatori ntregi i reprezint operaia de


determinare a restului mpririi celor dou valori; de exemplu: Eval(2%4) = 2.
Operatorii de incrementare (++) i decrementare (--) sunt operatori unari care pot fi folosii
n cazul n care se dorete ca valoarea unei variabile s creasc cu 1 (incrementare) sau s
scad cu 1 (decrementare). De exemplu, urmtoarele instruciuni sunt echivalente:
k = k + 1; k++;
k = k - 1; k--;

Din punct de vedere al prioritii operatorilor aritmetici, acetia se mpart n grupe de


prioritate, a cror ordine descresctoare este:
operatori de incrementare i decrementare
operatori aditivi unari
operatori multiplicativi
operatori aditivi binari
Utilizarea operatorilor de incrementare i decrementare necesit anumite precauii, deoarece
poziia lor poate fi att infix, ct i postfix. Privit doar ca un efect lateral, nu este nici o
deosebire ntre utilizarea postfix i cea prefix, deoarece n ambele cazuri modificarea valorii
operandului este aceeai. Probleme pot apare doar n cazul n care expresia ce conine astfel
de operatori este utilizat ca operand n cadrul altei expresii.
De exemplu, instruciunea:
n = k++;

este echivalent cu secvena:


n = k;
k = k + 1;

pe cnd instruciunea:
n = ++k;

este echivalent cu secvena:


k = k + 1;
n = k;

Se observ faptul c n cele dou cazuri, valoarea variabilei n este diferit dup evaluarea
expresiei respective.
n general, la evaluarea unei expresii ce conine un operand care este o subexpresie care
conine la rndul ei un operator de incrementare/decrementare se utilizeaz urmtoarea regul:
n cazul unui operator prefix de incrementare/decrementare, nti se modific valoarea
operandului aferent operatorului i apoi se evalueaz expresia;
n cazul unui operator postfix, se evaluaeaz nti expresia cu valoarea veche a
operandului aferent operatorului de incrementare/decrementare i apoi se modific
valoarea operandului.
Un caz uzual de utilizare al operatorilor de incrementare/decrementare se refer la operaiile
cu tablouri, cnd se dorete modificarea indicilor. De exemplu, instruciunea urmtoare
iniializeaz un tablou cu valorile: xk = k, k = 0, 1, , n-1:
for (k=0; k<n; x[k++]=k);

6.3 Operatori de relaie


Operatorii de relaie corespund operaiilor de comparare din matematic i presupun
compararea valorilor a dou expresii:
<
<=
>

mai mic
mai mic sau egal
mai mare

>=
==
!=

mai mare sau egal


egal
diferit

Aceti operatori sunt binari i presupun operaia de echilibrare a valorilor operanzilor. Din
punct de vedere al prioritii, ei se mpart n dou clase de preceden:
<, <=, >, >=
==, !=
Din punct de vedere matematic, rezultatul unei operaii de comparare este o valoare logic.
Conform regulilor de interpretare a valorilor logice n limbajul C, rezult c rezultatul
evalurii unei expresii de relaie poatr fi:
- constanta zero, n cazul n care valorile operanzilor nu respect relaia aferent
operatorului;
- o constant ntreg diferit de zero (n mod uzual canstanta 1 pentru multe
compilatoare) n caz contrar.
Observaie. Nu trebuie confundat operatorul de comparare (==) cu cel de atribuire (=). n
cazul utilizrii din greeal a operatorului de atribuire n locul celui de comparare, efectul
lateral al operaiei de atribuire este greu de detectat n anumite situaii.
Exemplul 6.1. Secvena urmtoare testeaz dac o valoare dintr-un ir de numere este egal
cu o anumit valoare dat.
int k, n, y = 7, v[4];
/* ... */
for (k=0; k<n; k++)
if (v[k] == y)
printf(Valoare gasita in sir pe pozitia %d\n, k);

Dac n interiorul instruciunii if s-ar fi scris:


if (v[k] = y)
printf(Valoare gasita in sir pe pozitia %d\n, k);

expresia condiional ar fi fost tot timpul adevrat, deoarece prin atribuirea v[k]=y,
valoarea lui v[k] ar fi fost alterat la valoarea 7.
O alt problem n utilizarea expresiilor condiionale o poate constitui apariia operatorilor de
incrementare/decrementare, datorit posibiltii poziiei prefixe sau postfixe a acestora. n
acest caz rezultatul evalurii unei expresii condiionale depinde de poziia operatorilor.
Exemplul 6.2. Urmtoarea secven calculeaz suma: S = 1 + 1/2 + 1/(n-1)
int n
float
while
s =

= 4;
s = 0;
(--n > 0)
s +1.0/n;

La nceputul fiecrei iteraii se decrementeaz valoarea lui n i apoi se evalueaz expresia


condiional, astfel nct la a 4-a iteraie n va avea valoarea 1. Dup decrementare n va avea
valoarea zero i execuia instruciunii while se termin.
Dac se inverseaz poziia operatorului de decrementare:
while (n-- > 0)
s = s +1.0/n;

execuia programului n acest caz va genera o eroare (mprire la zero). n acest caz, la
nceputul fiecrei iteraii se evalueaz nti expresia condiional i apoi se decremanteaz
valoarea lui n. Astfel, la iteraia a 4-a n va avea valoarea 1 i deci expresia condiional este
adevrat, dar nainte de execuia instruciunii de atribuire interne instruciunii while,
valoarea lui n se va decrementa, instruciunea fiind de fapt: s = s +1.0/0;

6.4 Operatori logici


Rolul operatorilor logici este acela de a putea reprezenta expresii condiionale
corespunztoare propoziiilor compuse din logica matematic. Din acest motiv, operatorii
logici corespund principalelor operaii logice:
!
&&
||

negaie logic
conjuncie logic (i logic)
disjuncie logic (sau logic)

De exemplu, pentru a specifica faptul c un numr real se afl ntr-un interval:


x [a, b]

se poate scrie propoziia logic:


(x a) (x b)

Transcrierea acesteia n limbajul C este:


(x >= a) && (x <= b)

Observaie. Prezena parantezelor rotunde nu este necesar, deoarece dup cum se poate
observa din tabelul tuturor operatorilor, operatorii de relaie au o preceden mai mare dect
operatorii logici i subexpresiile x >= a , x >= a se evalueaz primele.
Rezultatul evalurii unei expresii logice se determin n conformitate cu tabelel de adevr ale
celor trei operaii. n figura 6.1, a i b reprezint doi operanzi ntregi, iar x o valoare ntreag
diferit de zero:
a

b
0
x

a && b

b
0
x

!a

0
x

x
0

a || b

!a

Figura 6.1. Operatori logici


Observaie. Datorit regulii de interpretare a constantelor logice i a semanticii opeatorilor
logici i relaionali, anumite expresii condiionale pot avea forme diferite de reprezentare. De
exemplu, urmtoarele dou perechi de expresii sunt echivalente:
Eval(x == 0) = Eval(!x)
Eval(x != 0) = Eval(x)

Ordinea de evaluare a expresiilor logice este de la stnga la dreapta. Datorit acestui fapt i n
concordan cu semantica operatorilor logici, n anumite situaii o expresie logic nu este
evaluat pn la sfrit.

Pentru reducerea timpului de execuie al programelor, compilatoarele limbajului C


ntrerup evaluarea unei expresii logice atunci cnd rezultatul devine sigur. Urmtoarele dou
cazuri sunt uzuale:
a) ntr-o expresie de forma t1 || t2 || || tn, se ntrerupe evaluarea
expresiei la apariia primului operand tk diferit de zero;
b) ntr-o expresie de forma t1 && t2 && && tn, se ntrerupe evaluarea
expresiei la apariia primului operand tk care are o valoare egal cu zero;
Observaie. Regula de ntrerupere a evalurii expresiilor logice n condiiile prezentate
anterior poate conduce la neevaluarea anumitor operanzi. Acest fapt poate avea consecine
nedorite n program, dac operanzii au asociate efecte laterale (n cazul neevalurii lor,
efectele laterale nu mai apar).
Exemplul 6.3. Se consider un ir de numere reale x1, x2, , xn. S se determine suma
ptratelor numerelor pozitive, dar i a celor negative al cror ptrat nu depete o valoare
dat y.
#include <stdio.h>
int main() {
int k, n;
double y, d, x[10], s = 0;
scanf(%d%lf, &n, &y);
for (k=0; k<n; k++) {
scanf(%lf, &x[k]);
if ((d=x[k]*x[k])<y || x[k] > 0)
s = s + d;
}
printf(\ns = %lf, s);
return 0;
}

Dac n programul precedent s-ar inversa poziia operanzilor din expresia condiional:
if (x[k] > 0 || (d=x[k]*x[k])<y)

programul ar conine o eroare: n cazul n care x[k]>0, a doua subexpresie nu mai este
evaluat i valoarea variabilei d nu este calculat corect.
Din acest motiv, este indicat ca expresiile cu efecte laterale s fie plasate n fa, sau
atribuirile ce constituie efecte laterale s fie scoase n afara (dar naintea) expresiilor
condiionale.

6.5 Operatori de atribuire


Operaia de atribuire are o semnificaie asemntoare atribuirii din celelalte limbaje de
rpogramare. Operatorul de atribuire (=) este binar, iar sintaxa atribuirii este:
variabil = expresie

Din punct de vedere pragmatic, atribuirea presupune dou aciuni distincte, o operaie de
evaluare i una de scriere n memorie i const n scrierea valorii rezultate n urma evalurii
expresiei din dreapta operatorului de atribuire n zona de mamorie asociat variabilei din
partea stng. Din acest motiv, n sintaxa anterioar, variabil nu nseamn neaprat
numele unei variabile simple, ci o construcie sintactic ce are asociat o zon de memorie.

Exist dou noiuni importante legate de lucrul cu variabile i valori: noiunea de Lvaloare i cea de R-valoare. Valoarea curent a unei variabile (coninutul zonei de memorie
ataat) reprezint R-valoarea variabilei (right value, valoarea din partea dreapt a
operatorului de atribuire), iar adresa zonei de memorie ataat variabilei este o L-valoare (left
value, valoarea din partea stng a operatorului de atribuire).
Deoarece n dreapta operatorului de atribuire poate fi o expresie, care evaluat produce
o valoare, prin extensie se numete R-valoare orice valoare specific unui tip de date
fundamental sau derivat, cu excepia funciilor i tablourilor. n mod asemntor, deoarece n
partea stng a operatorului de atribuire poate fi o construcie sintactic ce genereaz o adres
de memorie, se numete L-valoare adresa de memorie a unei valori de orice tip. n plus, exist
noiunea de F-valoare, care este o referin ctre o funcie.
Exemple.
int n = 10, v[7];
float x, *px;
struct { int k; float r; } a;
int f(int);
int (*pf)(float) = f1;
/* pf este un pointer la o functie cu un parametru
de tip float care returneaza un intreg
*/
void f2(float);
v[3] = n;
/* n este R-valoare, v[3] este L-valoare */
x = 1;
/* 1 este R-valoare, x este L-valoare */
px = &x;
/* &x este R-valoare, px este L-valoare */
a.k = v[3];
/* v[3] este R-valoare, valoarea componantei a 4-a
a tabloului v, a.k este L-valoare, adresa
de memorie a componentei k din variabila a
*/
n = (*pf)(v[3]);
/* v[3] este R-valoare, n este L-valoare,
*pf este F-valoare, o referinta spre functia f1,
iar (*pf)(v[3]) este R-valoare
*/
f2(*px);
/* *px este o R-valoare, continutul zonei de
memorie indicata de px, iar f2 este F-valoare
*/

Rezul c n cazul general, o atribuire se poate reprezenta astfel:


L-valoare = R-valoare

O problem poate apare n cadrul operaiei de atribuire dac operandul din stnga i din
dreapta atibuirii nu au acelai tip de date. n cazul n care tipurile sunt incompatibile se
genereaz un mesaj de eroare, iar n cazul n care ele sunt compatibile, dar diferite, are loc o
conversie de tip. Mai exact, valoarea expresiei din dreapta este convertit la tipul variabilei
din stnga. n general, rezultatul unei asemenea conversii este dependent de implementare i
poate conduce la depiri.

Exemple.
float s;
s = 2;
/* se converteste intregul 2 la valoarea
reala 2.0; corect
*/
int n;
n = 12.73;
/* se converteste valoarea reala 12.73 la valoarea
intreaga 12 prin trunchiere; corect
*/
n = 124E200;
/* depasire, deoarece partea intreaga a numarului
124*10200 nu se poate reprezenta ca o valoare
intreaga; gresit
*/

Dup cum s-a precizat anterior, o particularitate a limbajului C este aceea c trateaz
atribuirea ca un operator uzual ce poate interveni n cadrul expresiilor. Aceasta permite
modificarea valorii unei variabile n timpul evalurii unei expresii, ceea ce reprezint un efect
lateral al operaiei de evaluare (al crui scop principal este s produc o valoare dup
evaluare).
Exemplul 6.4. Se va relua problema referitoare la determinarea celui mai mare divizor
comun. n programul anterior, din exemplul 3.5, expresia folosit pentru determinarea restului
era evaluat de dou ori: o dat n expresia condiional a instruciunii while, iar a doua oar
n corpul acesteia.
while(m%n != 0) {
r = m%n;
m = n;
n = r;
}

n continuare se va evalua expreia o singur dat.


#include <stdio.h>
int cmmdc(int m, int n) {
int r;
while (r = m % n) {
m = n;
n = r;
}
return n;
}
int main() {
int m, n;
printf(\nm, n: );
scanf(%d%d, &m, &n);
printf(\ncmmdc = %d, cmmdc(m, n));
return 0;
}

Datorit asocierii de la dreapta la stnga a operatorului de atribuire (se evalueaz nti


operandul din dreapta i apoi ce din stnga), limbajul C permite atribuirea multipl. De
exemplu, n secvena urmtoare:
int a, b, c;
c = b = a = 5;

se atribuie nti variabilei a valoarea 5, apoi lui b i apoi lui c. Instruciunea anterioar este
tratat de ctre compilator ca avnd forma urmtoare:
c = (b = (a = 5));

n afara operatorului simplu de atribuire, limbajul C mai conine o categorie de operatori de


atribuire compui. Rolul lor este acela de a descrie mai compact operaii de atribuire de
forma:
variabil = variabil operator expresie

fr scrierea de dou ori a L-valorii (n acest caz operator este un operator binar).
Pentru expresiile de forma anterioar, scrierea compact este urmtoarea:
variabil = operator_compus expresie

unde operator_compus se obine prin alturarea operatorului binar la cel de atribuire.


Operatorii binari ce se asociaz operatorului de atribuire sunt n mod uzual cei aritmetici i la
nivel de bit. Principalii operatori compui de atribuire sunt:
+=
-=
*=
/=
%=
&=
^=
|=
<<=
>>=
De exemplu, instruciunea:
x = x + y;

se poate scrie condensat folosind un operator compus de atribuire astfel:


x += y;

Avantajul utilizrii operatorilor compui de atribiure apare n situaiile n care L-valoarea din
cadrul atribuirii nu este o variabil simpl, ci o expresia mai complex, coninnd la rndul ei
mai muli operatori i operanzi. De exemplu, instruciunea:
grupa[i].situatie.nr_restante =
grupa[i].situatie.nr_restante + 1;

se poate scrie mai simplu astfel:


grupa[i].situatie.nr_restante += 1;

Observaie. n cazul n care expresia din dreapta operatorului compus de atribuire conine la
rndul ei operatori, aceasta este tratat de ctre compilator ca o subexpresie inclus ntre
paranteze. De exemplu, instruciunea:
x *= y + 1;

este echivalent cu:

x = x * (y + 1);

i nu cu instruciunea:
x = x * y + 1;

Exemplul 6.5. Secvena urmtoare calculeaz suma i produsul elementelor unui ir de


numere:
int k, n = 4;
float v[4] = {1, 2, 3, 4}, s = 0, p = 1;
for (k=0; k<n; k++) {
s += v[k];
p *= v[k];
}

6.6 Operatori de adresare


Operatorii de adresare sunt folosii n cteva cazuri tipice:
pentru a avea acces la o component dintr-un element compus (operatorii de indexare
i de selecie);
pentru a determina adresa unui element al unei anumite entiti (operatori de
determinare a adresei);
pentru a avea acces la o anumit entitate accesibil doar prin adresa sa de memorie
(operatori de adresare indirect).
Rezultatul evalurii unei operaii de adresare este interpretat n funcie de contextul n care
apare, fie ca o L-valoare (dac apare n stnga unei atribuiri) indicnd adresa de memorie a
unui obiect, fie ca o R-valoare, indicnd valoarea curent a elementului respectiv.
Operatorii de adresare sunt urmtorii:
a) Operatorul de indexare, reprezentat de caracterele [], este un operator binar, utilizat n
expresii de forma:
tablou [ expresie indice ]
Se observ poziia special a operatorului fa de operanzi: operatorul, format din dou
caractere (ca i operatorul de apel de funcie), ncadreaz operandul al doilea.
Primul operand reprezint un tablou, iar al doilea o expresie ntreag specificnd
indicele elementului selectat. Expresia ce conine operatorul de selecie returneaz
valoarea elementului specificat.
b) Operatorul de selecie direct, este reprezentat de caracterul . i este utilizat pentru a
selecta o component dintr-o structur sau uniune. Este un operator cu o poziie infix,
separnd structura respectiv de numele componentei:
structur . selector

Despre acest operator se va discuta n cadrul capitolului rezervat structurilor i uniunilor.


c) Operatorul de selecie indirect, este reprezentat de caracterele -> i este utilizat
pentru a selecta o component dintr-o structur sau uniune prin intermediul unui pointer.
Expresia de utilizare este:
pointer -> selector

Despre acest operator se va discuta n cadrul capitolului rezervat structurilor i uniunilor.


d) Operatorul de determinare a adresei este reprezentat de caracterul & i utilizat n
expresii de forma:
& entitate

Operatorul returneaz adresa de memorie a entitii respective.


Un exemplu frecvent de utilizare este n cazul apelului funciei scanf, pentru citirea
valorilor unor variabile de intrare. Operatorul este folosit pentru determinarea adresei de
meorie a acestor variabile. De exemplu:
int n;
scanf(%d, &n);

Despre alte cazuri de utilizare a acestui operator se va discuta n cadrul capitolului


rezervat pointerilor i tablourilor.
e) Operatorul de adresare indirect este reprezentat de caracterul * i utilizat n expresii
de forma:
* pointer

unde este o entitate ce memoreaz adresa unui element din program. Rezultatul evalurii
unei asemenea expresii este valoarea elementului a crui adres este memorat de ctre
pointer.
Exemple:
int a = 3, b, *p;
p = &a;
/* pointerul p memoreaza adresa variabilei a */
b = *p;
/* b primeste valoarea elementului a carui
adresa este memorata de p, adica 3
*/

i despre acest operator se va discuta n cadrul capitolului rezervat structurilor i


uniunilor.

6.7 Operatori la nivel de bit


Operatorii la nivel de bit reprezint operaii specifice limbajelor de asamblare. Spre deosebire
de ceilali operatori ai limbajului C, aceast categorie prelucreaz fiecare cifr binar din
cadrul operanzilor (care n acest caz sunt numai de tip ntreg).
Operatorii la nivel de bit corespund n general operaiilor principale din logica boolean
(operatorul de negaie este singurul operator unar, ceilali sunt binari):
operatorul ~ reprezint operaia de negaie;
operatorul & reprezint operaia boolean i;
operatorul ^ reprezint operaia boolean sau exclusiv;
operatorul | reprezint operaia boolean sau.
n plus, exist doi operatori binari de deplasare:
operatorul << pentru operaia de deplasare la stnga;
operatorul >> pentru operaia de deplasare la dreapta.
Operatorii ce corespund operaiilor booleene opereaz asupra cifrelor binare conform
urmtoarelor tabele de adevr din figura 6.2 (a i b reprezint cifre binare):

b
0
1

a&b

~a

0
1

1
0

a^b

a|b

~a

Figura 6.2. Operatori la nivel de bit


Pentru exemplificare, n tabelul urmtor se prezint cteva cazuri de aplicare a acestor
operatori asupra unor operanzi de tip char:
a
b
~a
a & b
a | b
a ^ b

0
0
1
0
0
0

0
1
1
0
1
1

0
1
1
0
1
1

1
1
0
1
1
0

0
1
1
0
1
1

1
0
0
0
1
1

1
1
0
1
1
0

0
1
1
0
1
1

n cazul operatorilor de deplasare, cei doi operanzi au semnificaii diferite: primul reprezint
numrul ai crui bii se deplaseaz, iar al doilea specific numrul de poziii cu care se face
deplasarea n direcia respectiv. De exemplu, expresia n << 2 specific faptul c cifrele
binare ale numrului n se vor deplasa cu dou poziii spre stnga.
La deplasarea spre stnga cu k cifre, primii k bii ai numrului se pierd, iar ultimii k se
completeaz cu zero. La deplasarea spre dreapta cu k cifre, ultimii k bii ai numrului se
pierd, iar primii k se completeaz cu cifra binar 0 sau 1, cifr ce depinde de implementare
(valoarea nu se specific in standardul limbajului).
Dac valoarea expresiei de deplasare este negativ, sau depete lungimea de
memorie a operandului din stnga, semnificaia operaiei este nedefinit.
Exemplu pentru un operand de tip char:
a
a << 2
a >> 2

0 0 1 1 1 1 0 1
1 1 1 1 0 1 0 0
0 0 0 0 1 1 1 1

n exemplul precedent s-a considerat c la deplasarea spre dreapta se introduc zerouri. n


mod uzual, valoarea cifrei cu care se completeaz primii k bii este aleas astfel nct
operaia s fie echivalent cu o mprire cu 2k (pentru numere pozitive cifra este 0, iar pentru
numere negative este 1). Aceast alegere este sugerat de urmtoarea observaie: n cazul
deplasrii spre stnga cu k cifre, n cazul n care nu se produce o depire, rezultatul obinut
este echivalent cu o nmulire a numrului cu 2k.
Pentru exemplificare, se poate compara numrul:
n = 00000101
cu numrul n1 = n << 2:
n1 = 00010100 (nmulire cu 22).
n mod similar, n2 = n1 >> 2:
n2 = 00000101 (mprire cu 22)

Exemplul 6.6. Un exemplu elocvent de utilizare a operatorilor la nivel de bit l constituie


cazul programelor care lucreaz cu informaii codificate pe poriuni. S presupunem c un
program trebuie s conduc un robot. Pentru simplitate, se consider doar urmtoarele
comenzi:
- deplasare nainte,
- deplasare napoi,
- deplasare la stnga,
- deplasare la dreapta,
- apucare a unui obiect (nchidere mn),
- lsare a unui obiect (deschidere mn).
Pentru primele patru comenzi, trebuie specificat n plus i distana pe care trebuie s se
deplaseze robotul (considerat n milimetri).
Pentru codificarea a celor 6 comenzi diferite sunt necesare 3 cifre binare. n cazul n
care o comand conine 8 cifre binare (un operand de tip char), ultimele 5 cifre vor codifica
distana. Un exemplu de codificare al tipului comenzilor:
- 000: deplasare nainte,
- 001: deplasare inapoi,
- 010: deplasare spre stnga,
- 011: deplasare spre dreapta,
- 100: apucare obiect,
- 101: lsare obiect.
S presupunem de exemplu c exist o comand de deplasare spre stnga cu 6 mm de forma:
| 0 1 0 | 0 0 1 1 0 |
Programul trebuie s detecteze tipul comenzii, iar n cazul n care comanda este de deplasare,
s determine distana de deplasare.
Funcia care decodific o comand i apeleaz funciile specifice ce realizeaz
operaiile respective trebuie s izoleze cele dou cmpuri. Ea returneaz un rezultat ntreg,
specificnd dac s-a putut sau nu decodifica i executa respectiva comand:
int DecodificareComanda(unsigned char comanda) {
unsigned char masca_tip _operatie = 0x7;
/* masca_tip_operatie = 00000111 */
unsigned char masca_distanta = 0x1f;
/* masca_distanta = 00011111 */
unsigned char tip_operatie, distanta;
masca_tip_operatie <<= 5;
/* masca_tip_operatie devine 11100000 */
tip_operatie = (comanda & masca_tip_operatie) >> 5;
distanta = comanda & masca_distanta;
if (tip_operatie == 0)
DeplasareInainte(distanta);
else if (tip_operatie == 1)
DeplasareInapoi(distanta);
else if (tip_operatie == 2)
DeplasareStanga(distanta);
else if (tip_operatie == 3)
DeplasareDreapta(distanta);
else if (tip_operatie == 4)
ApucareObiect();
else if (tip_operatie == 5)
LasareObiect();

else
Return 0;
return 1;
}

O masc reprezint un grup de cifre binare 1 consecutive i se utilizeaz pentru a putea reine
din mai multe cifre binare un grup de cifre dorit. Selecia grupului de cifre se realizeaz cu
ajutorul operaiei i la nivel de bit ntre numrul dorit i ablon.
n exemplu, s-au utilizat dou mti: prima reine primii 3 bii (masca_tip_operatie),
iar a doua ultimii 5 bii (masca_distanta). Dup efectuarea operaiei i la nivel de bit, se
obin utmtoarele structuri de cifre binare:
Comanda
Masca_tip_operatie
Masca_distanta
Tip_operatie
Distanta
Tip_operatie >> 5

b7
1
0
b7
0
0

b6
1
0
b6
0
0

b5
1
0
b5
0
0

b4
0
1
0
b4
0

b3
0
1
0
b3
0

b2
0
1
0
b2
b7

b1
0
1
0
b1
b6

b0
0
1
0
b0
b5

Valoarea corect pentru masca_tip_operatie se obine dup o deplasare spre stnga cu 5 cifre
binare. Dup selectarea biilor b7, b6 i b5 din cadrul octetului aferent unei comenzi, acetia
trebuie deplasai spre dreapta cu 5 cifre binare. n acest mod, octetul corespunztor variabilei
tip_operatie poate fi utilizat simplu prin valoarea sa n cadrul expresiilor condiionale din
instruciunile if (de exemplu: 00000101 reprezint valoarea 5, 00000100 reprezint
valoarea 4, etc.).

6.8 Operatori specifici ai limbajului C


Limbajul C conine o serie de operatori specifici, care pot mri flexibilitatea i compactitatea
programelor.
A) Operatorul sizeof
Acesta este un operator special al limbajului, fiind singurul operator care nu evalueaz
valoarea operandului asociat. Operatorul sizeof nu se aplic asupra unei valori, ci asupra
unui tip de date, forma sa de utilizare fiind:
sizeof ( tip )

sau
sizeof

expresie_unar

n primul caz operatorul se aplic direct asupra tipului de date specificat, iar n al doilea se
aplic asupra tipului de date al expresiei respective.
Operatorul returneaz o valoare ntreag, reprezentnd numrul de octei necesari
pentru memorarea valorilor tipului asociat.
Observaie. Expresia unar este o expresie care nu conine operatori binari sau ternari. n
cazul unei expresii complexe, operatorul sizeof se va aplica numai asupra primului
operand. De exemplu, considerndu-se secvena:
int n1, n2, n3, a = 5;

double b = 2.2;
n1 = sizeof a + b;
n2 = sizeof (a + b);
n3 = sizeof(double);

se observ faptul c valorile lui n2 i n3 sunt egale. Presupunnd faptul c valorile de tip int
se memoreaz pe 4 octei i cele de tip double pe 8 octei, valorile lui n2 i n3 sunt ambele
egale cu 8 (n cazul lui n2, nu se evaluaeaz expresia a + b, ci se determin tipul de date al
acesteia), iar valoarea lui n1 este 7 (conversia valorii 7.2 la valoare ntreg). Se remarc
urmtoarele echivalene:
n1 = Eval(sizeof a + b) =
= Eval(sizeof a) + Eval(b)

n cazul n care operandul lui sizeof este numele unui tablou, acesta nu este convertit la un
pointer. Valoarea returnat n acest caz reprezint numrul total de octei alocat tabloului
respectiv. De exemplu, n secvena:
int n1, n2, a[10], *p;
n1 = sizeof (a);
n2 = sizeof (p);

presupunnd faptul c valorile de tip int se reprezint pe 2 octei, valoarea lui n1 este 20, pe
cnd valoarea lui n2 (p are tipul int*) este n general 4 (adic numrul de octei necesari
pentru memorarea adresei unui obiect de tip int).
B) Operatorul de conversie de tip
Acest operator, numit i operator cast, determin schimbarea forat a tipului de date a
valorii unei expresii sau subexpresii la un alt tip de date specificat explicit de programator.
Operatorul cast are o poziie prefix, forma de utilizare fiind urmtoarea:
( tip )

expresie_unar

Se evalueaz expresia unar, iar rezultatul este convertit la tipul specificat ntre paranteze.
Observaie. Ca i n cazul operatorului sizeof, trebuie avut grij n cazul n care expresia
n care apare operatorul cast este mai complex (operatorul se aplic doar asupra
operandului urmtor). De exemplu, n cazul n care variabilele a i b sunt de tip ntreg:
int a = 5, b = 7;

n cadrul expresiei:
(double) a / b;

operatorul cast se aplic doar asupra variabilei a schimbndu-i valoarea ntrag 5 n constanta
de tip double 5.0, astfel nct operatorul / joac rol de mpire real.
O folosire uzual a operatorului cast este n cazul apelului funciilor, fie cnd se dorete
conversia tipului de date a valorii returnate de o funcie, fie cnd se dorete conversia tipului
de date al parametrilor de apel.
Exemple.
1) O funcie des utilizat pentru alocarea memoriei n zona heap a programului este
malloc, care are prototipul:
void* malloc(size_t);

Dac se dorete s se aloce memorie pentru o valoare de tip float, se poate utiliza un
pointer, ca n secvena urmtoare:
float *p;
p = (float*)malloc(sizeof(float));

Se convertete astfel tipul void* la tipul float* pentru a exista compatibilitate cu tipul
variabilei p.
2) O funcie matematic corespunztoare operaiei de ridicare la putere este funcia pow, care
are prototipul:
double pow(double, double);

n cazul n care a i b sunt dou variabile ntregi, apelul acestei funcii se poate scrie:
x = pow((double)a, (double)b);

Observaie. Operatorul nu afecateaz valoarea variabilelor din cadrul expresiei, astfel nct a
i b i pstreaz valorile iniiale.
C) Operatorul condiional (?:)
Acesta este singurul operator ternar al limbajului C i are urmtoarea form de utilizare:
expr.1 ? expr.2 : expr.3
Denumirea sa provine de la faptul c valoarea de evaluare a unei asemenea expresii depinde
de tipul primei expresii:
Eval( exp r.2 ), daca Eval( exp r.1 ) 0
Eval( exp r.1 ? exp r.2 : exp r.3 )
Eval( exp r.3 ), daca Eval( exp r.1 ) 0
Avantajul utilizrii unui asemenea operator const n faptul c permite scrierea unui cod
compact i evitarea utilizrii intruciunilor if.
Exemplul 6.7. Presupunnd faptul c variabila c este de tip caracter i memoreaz n timpul
execuiei programului o cifr a bazei de numeraie 16:
c {0, 1, ..., 9} {a, b, ..., f}

instruciunea urmtoare determin valoarea zecimal echivalent a acesteia:


if (c >= 0 && c <= 9)
v = c 0;
else
v = c a + 10;

Scris condensat, aceast instruciune este echivalent cu:


v = (c >= 0 && c <= 9) ? c 0 : c a + 10;

Observaie. S-a utilizat poziia relativ a caracterelor alfabetice i numerice din tabela de
caractere.
Operatorii condiionali pot fi imbricai. De exemplu, pentru a determina valoarea maxim a
trei numere, x, z i z, se poate scrie funcia urmtoare:
double max3(double x, double y, double z) {
return (x > y) ? (x > z ? x : z) : (y > z ? y : z) ;
}

D) Operatorul de secveniere (,)


Acesta este un operator de evaluare secvenial a unor expresii. Forma uzual de folosire este
urmtoarea:
expr.1 , expr.2 , ... , expr.k

unde cel puin o expresie este o expresie de atribuire. Ea se utilizeaz atunci cnd sintaxa
limbajului presupune prezena unei singure expresii, dar algoritmul problemei de rezolvat
necesit prelucrarea secvenial a mai multor expresii.
Operatorul de secveniere nu poate fi utilizat pentru a separa elementele dintr-o list.
n mod uzual, el se folosete n cadrul unei expresii condiionale, sau ntr-o expresie ce
conine paranteze.
Evaluarea unei asemenea expresii se face de la stnga la dreapta, valoarea de evaluare fiind de
fapt valoarea ultimei expresii:
Eval(expr.1, expr.2 , ... , expr.k) = Eval(expr.k)

Celelalte expresii sunt folosite de obicei pentru efectele laterale pe care le produc.
De exemplu, presupunnd c variabilele x, y, z i t sunt de tip numeric, atunci instruciunea:
z = (x > y) ? x : y;

atribuie lui z valoarea maxim, pe cnd instruciunea:


z = (x > y) ? (t = x, x = y, y = t) : y;

are acelai efect, dar n plus realizeaz i ordonarea cresctoare a valorilor x i y. O utilizare
eronat este urmtoarea:
z = (x > y) ? (t = y, y = x, x = t) : y;

O alt utilizare uzual a operatorului de secveniere este n cadrul instruciunii for.


Exemplul 6.8. Funcia urmtoare afieaz poziiile succesive ale unui cal pe tabla de ah,
plecnd din poziia (a, 1) i utiliznd tot timpul aceeai micare:
void PozitieCal(int jos, int dreapta) {
int i, j;
for (i=j=1; i<=8 && j<=8; i+ = jos, j+= dreapta)
printf(%c %d\n, a-i, j);
}

Funcia convertete valorile numerice 1, 2, , 8 la caracterele a, b, , h. Parametrii jos


i dreapta pot avea doar valorile (1, 2) sau (2, 1). Exemplu de utilizare:
int main() {
PozitieCal(1, 2);
PozitieCal(2, 1);
return 0;
}

6.9 Probleme
6.1. S se scrie un program pentru rezolvarea ecuaiaei biptrate: a*x4 + b*x2 + c = 0.
6.2. S se scrie un program pentru rezolvarea problemei 2.3.

6.3. S se scrie un program pentru rezolvarea problemei 2.11.


6.4. Se consider un punct P n plan specificat prin coordonatele sale i un segment de
dreapt specificat prin coordonatele extremitilor. S se scrie un program care s
determine dac punctul P se afl sau nu pe segment.
6.5. Se consider dou segmente de dreapt specificate prin coordonatele extremitilor. S
se scrie un program care s determine intersecia segmentelor.
6.6. S se scrie un program pentru determinarea naturii unui patrulater specificat prin
coordonatele vrfurilor.
6.7. S se scrie un program pentru calculul ariei determinate de intersecia a dou patrulatere
specificate prin coordonatele vrfurilor.
6.8. Se consider o plac din oel, de form poligonal, specificat prin coordonatele
vrfurilor poligonului n ordine trigonometric. S se determine centrul de greutate al
plcii.
6.9. S se determine dac un numr ntreg n este prim.
6.10. Se consider operaii cu mulimi de numere ntregi din domeniul 0, 1, , 31. Utiliznd
operatorii la nivel de bit, s se scrie funcii C pentru principalele operaii: intersecia,
reuniunea i diferena a dou mulimi, testul de incluziune a unei mulimi n alt
mulime, precum i testul de apartenen a unui element la o mulime.
6.11. Utiliznd funciile C de la problema 6, s se scrie un program pentru determinarea
tuturor numerelor prime mai mici dect un numr n dat, utiliznd un algoritm numit
Ciurul lui Erathostene. Se utilizeaz dou mulimi, a i b, cu urmtoarea semnificaie:
- la nceput, a conine toate numerele de la 2 la n, iat b va fi vid;
- la sfritul calculului, a va fi vid, iar b va conine numerele dorite.

S-ar putea să vă placă și