Sunteți pe pagina 1din 9

PRELEGERE VIII

PROGRAMAREA CALCULATOARELOR ŞI LIMBAJE DE PROGRAMARE

I. Operatori şi expresii - continuare


5.1 Expresii Lvalue şi Rvalue
Expresiile Lvalue şi Rvalue sînt consecinţe ale expresiilor de atribuire.
Expresia Lvalue (valoare stînga) se asociază cu numele variabilei din primul membru al
unei atribuirii, iar Rvalue (valoare dreapta) cu o expresie din structura atribuirii sau cu
atribuirea propiu-zisă. În prima situaţie, expresia Lvalue indică adresa la care se poate
memora sau modifica valoarea variabilei respective, pe cînd în a doua situaţie oferă
valoarea expresiei. De exemplu, variabilele x şi y au valoare stînga, în schimb x-y, y+2 şi
x=y+2 au valoare dreapta.

6. Operatori de incrementare şi de decrementare


Operatorii de incrementare şi de decrementare se aplică unei variable de tip scalar, după
cum urmează:
preincrementare/predecrementare cu formatul:
++nume_variabila; sau --nume_variabila; ;
postincrementare/postdecrementare cu formatul:
nume_variabila++; sau nume_variabila-- ; .
Valoarea de incrementare/decrementare este 1 sau –1. Se
incrementează/decrementează operandul, apoi i se poate folosi valoarea, în prima situaţie
şi invers în cea de a doua. Nu are importanţă poziţia operatorului de incrementare în
cazurile i++; sau ++i;. În ambele cazuri valoarea lui i se măreşte cu 1. Analog, pentru
operatorul de decrementare în situaţiile --i; sau i--; . Nu acelaşi lucru se întîmplă în
atribuiri de tipul a=i++; sau a=++i;. Echivalent, prima atribuire se poate descrie prin a = i
şi i = i+1;, iar a doua prin i = i+1; şi a = i. Dacă valoarea lui i este 7, atunci se obţine a =
7 în prima situaţie, iar în a doua a = 8.
În exemplul următor prin liniile de comentariu alăturate se precizează efectul
operatorului de incrementare asupra variabililor a, b şi c:
int a = 0, b = 0, c = 0, d;
c = ++a; /* c = 1, a = 1 */
c = b++; /* c = 0, b = 1 */
d = c+b++; /* c = 0, b = 2, d = 1 */.
Suma pătratelor şi produsul primelor primelor n numere naturale constituie subiectul
programului de mai jos. Valoarea lui n se introduce de la tastatură, iar contorizarea
paşilor în instrucţiunile de repetare for este realizată cu ajutorul variabilei i. În calcularea
sumei, i primeşte valori crescătoare prin aplicarea operatorului de postincrementare (i++),
iar în aflarea produsului, valori descrescătoare prin aplicarea operatorului de
predecrementare (--i). Maximul admis pentru n în cazul produsului este 15, unde
p=22528, iar în cazul sumei este 180, unde s=32761. Pentru depăşirea acestor limite, n
trebuie să se declare de tip long int.
/* Operatori de incrementare si de decrementare */

1
#include <stdio.h>
#include<conio.h>
void main(void)
{ int i, n ,s = 0,p = 1;
clrscr();
printf("\n\t Introduceti n=");
scanf("%d", &n);
for(i = 1; I <= n; i++) s += i*i;
for(i = n; i >= 1; --i) p*= i;
printf("\n \ts=%d p=%d", s, p);
getch(); }
Introduceti n=5
s=15 p=120
Programul 8.6.1.

7 Operatorul de adresare
Determinarea adresei afectate unei entităţi în vederea înscrierii sau modificării de valori
cu un tip adecvat, selectarea unui element de tablou, accesarea unui cîmp dintr-o
structură, referirea unui cîmp dintr-o uniune şi transmiterea de parametri unor funcţii
pentru tratarea lor în diverse contexte de rezolvare sînt situaţiile de bază în care se
folosesc operatorii de adresare.
Adresarea la o entitate poate fi directă, prin nume sau indirectă, prin intermediul unui
pointer. Adresarea indirectă apare în alocarea dinamică a unei variabile sau în selectarea
unei entităţi dintre mai multe de acelaşi tip.
La ambele tipuri de adresare, rezultatul este, fie o Rvalue, fie o Lvalue.
Simbolurile şi sintaxele de accesare ale operatorilor de adresare sînt următoarele:
1. & (determinare adresă): & nume_enitate. Precizează adresa entităţii în care urmează
să se înscrie o valoare de un tip adecvat (în cazul funcţiei de citire scanf(), pentru a o
furniza unei funcţii, cu efect lateral asupra entităţii în cazul transmiterii prin valoare sau
pentru a o atribuii unui pointer.
2. [ ] (indexare): nume_element_tablou[expresie_indice]. Acest tip de adresare a fost
descris în secţiunea Tablouri cu aplicare la referirea elementelor de tablou.
3. (selectare): nume_structura.nume_cîmp sau
nume_uniune.nume_cîmp. Selectează cîmpul cu numele precizat, din structura
sau uniunea respectivă, în vederea prelucrării lui.
4. -> (selectare indirectă): pointer->nume_cîmp sau
(*pointer). nume_cîmp. Adresa cîmpului selectat din structura sau uniunea
precizată se memorează în variabila de tip pointer.
5. *(adresare indirectă): * pointer. Accesează adresa memorată în operandul de tip
pointer.

2
8 Operatorul de secvenţiere
În limbajul C virgula poate fi interpretată, fie ca separator de entităţi de acelaşi tip în
declaraţii de tip, de adrese de variabile în liste de intrare, de variabile în liste de ieşire şi
de parametri în definirea sau apelarea unor funcţii, fie ca operator de secvenţiere într-o
secvenţă de expresii de forma: expresie_1, expresie_2, …, expresie_n,
secvenţa însăşi fiind considerată o expresie.
Evaluarea secvenţei se face de la stînga la dreapta în mod secvenţial, adică expresiile se
calculează una după alta în ordinea în care apar. Valoarea ultimei expresii constituie
valoarea întregii secvenţe. Nu se impune nici o restricţie la ultima expresie, în schimb
primele n-1 componente din secvenţă trebuie să fie atribuiri valide. Parantezele simple
pot fi utilizate în delimitarea secvenţei într-o situaţie în care poate să apară confuzii.
Programul 8.8.1 are ca scop aflarea valorii unei secvenţe de atribuirii cu următoarea
structură: y=(i=5*i, j=i-2, k++, y=(i+j+k)*x);. În tabelul care însoţeşte programul se
evidenţiază şi rezultatele expresiilor componente în ordinea execuţiei lor.
/* Operatorul de secventiere */
#include <stdio.h>
#include <conio.h>
void main(void)
{ float x = 2.5, y;
int i = 10, j, k;
clrscr();
printf("\n Introduceti j si k : ");
scanf("%d%d", &j, &k);
printf("\n y=(i=5*i, j=i-2,k++,y=(i+j+k)*x): %f",
y=(i=5*i, j=i-2, k++, y=(i+j+k)*x)); i = 10;
printf("\n Introduceti j si k : "); scanf("%d%d", &j, &k);
printf("\n Valorile expresiilor din y sint : ");
printf("\n i=5*i : %d", i=5*i); printf("\n j=i-2 : %d", j=i-2);
printf("\n k++ : %d", k++);
printf("\n y=(i+j+k)*x : %f",y=(i+j+k)*x);
getch(); }
Introduceti j si k : 3 15
y=(i=5*i, j=i-2,k++,y=(i+j+k)*x) : 285.000000
Valorile expresiilor din y sint :
1. i=i*5 : 50 3. k++ : 15
2. j=i-2 : 48 4. y=(i+j+k)*x : 285.000000
Programul 8.8.1

9. Operatorul sizeof
Cu operatorul sizeof într-o primă sintaxă, am făcut cunoştinţă deja în secţiunea 2.2. Spre
deosebire de ceilalţi operatori folosiţi în limbajul C++, care se aplică asupra unor valori,
sizeof este singurul care se aplică unui tip de dată (standard sau derivat). La forma
sizeof(tip) adăugăm acum şi sizeof expresie.

3
Efectul aplicării acestui operator asupra unei expresii este precizarea dimensiunii
spaţiului de memorie, în octeţi, necesar tipului valorii expresiei. Fără să se cunoască
valoarea expresiei, sizeof se rezolvă în faza de compilare, după ce se stabileşte tipul
expresiei în funcţie de tipul operanzilor componenţi.
Programul următor precizează numărul de octeţi necesar pentru alocarea tipurilor
variabilelor j, z şi expresiei w = z*j. După care se determină valoarea lui w.
/* Operatorul sizeof */
#include <stdio.h>
#include <conio.h>
void main(void)
{ float z = 2.5;
int j = 235;
double w;
clrscr();
printf("\n Tipul int :%d\n Tipul float :%d\
\n Tipul double :%d \n w= %lf", sizeof(j), sizeof(z),
sizeof(w=z*j), w=z*j);
getch(); }
Tipul int :2
Tipul float : 4
Tipul double : 8
w=587.500000
Programul 8.9.1

10. Operatorul condiţional


Selectarea unei alternative dintre două se realizează cu operatorul condiţional ? :. Acesta
este singurul operator ternar utilizat în limbajul C.
Sintaxa generală a unei expresii condiţionale este:
expresie_logică ? expresie_1 : expresie_2;
Evaluarea expresiei condiţionale se produce în mai multe etape:
- se află valoarea expresiei logice. Rezultatul ei este, fie 0, fie 1;
- dacă valoarea expresiei logice este 1, atunci rezultatul expresiei condiţionale
este egal cu valoarea primei expresii;
- dacă valoarea expresiei logice este 0, atunci rezultatul expresiei condiţionale
este egal cu valoarea celei de a doua expresii.
Rezolvarea expresiei condiţionale: x>=1 ? (f=x*x+1) : (f=x*x-1));
în două situaţii concrete diferite, se poate urmări în tabelul asociat programului următor.
/* Operatorul conditional */
#include <stdio.h>
#include <conio.h>
void main(void)
{ float x, f;
clrscr();
printf("\n Introduceti x:");
scanf("%f", &x);
printf("\n x>=1 ? (f=x*x+1) : (f=x*x-1) : %f",

4
x>=1 ? (f=x*x+1) : (f=x*x-1));
getch(); }

Introduceti x : 17.57
x>=1 ? (f=x*x+1) : (f=x*x-1) : 309.704889
Introduceti x : -5.2
x>=1 ? (f=x*x+1) : (f=x*x-1) : 26.039998
Programul 8.10.1.
Ultimul printf() poate fi înlocuit printr-o expresie condiţională de forma: x>=1 ? printf(“\
nf=%f”, x*x+1) : printf(“\nf=%f”, x*x-1);.
Al doilea program află maximul a trei numere reale a, b şi c, comparînd numerele două
cîte două (comparări imbricate).
/* Operatorul conditional */
#include <stdio.h>
#include <conio.h>
void main(void)
{ float a, b, c, maxim;
clrscr();
printf("\n Introduceti trei numere reale:");
scanf("%f%f%f", &a, &b, &c);
maxim = a>b? (a>c? a: c):(b>c? b : c);
printf("\n maxim= %5.2f", maxim);
getch(); }
Introduceti trei numere reale: 3.5 75. 22.35
maxim = 75.00
Programul 8.10.2.
Şi aici, linia de program cu ajutorul căreia se determină maximul a trei numere
reale poate fi înlocuită prin una din variantele:
a>b? maxim =(a>c? a : c) : (b>c? b : c);
a>b? (a>c? maxim = a : maxim = c) : (b>c? maxim = b : maxim = c);
a>b? (a>c? printf("\n maxim= %5.2f", a) :
printf("\n maxim= %5.2f", c)) :
(b>c? printf("\n maxim= %5.2f", b):
printf("\n maxim= %5.2f", maxim));
În plus, prin combinarea operatorului condiţional cu instrucţiunea if sau numai prin
utilizarea instrucţiunii if se pot adăuga încă multe alte variante

11. Conversii de tip


Datorită complexităţii şi diversităţii prelucrărilor din practică, de cele mai multe ori, un
operator este forţat să acţioneze asupra unor valori de tipuri diferite sau care nu concordă
cu sintaxa sa de definiţie.
Cum marea majoritate a operatorilor s-a proiectat să opereze asupra unor operanzi de
acelaşi tip, se impun conversii de tip asupra unui operand sau chiar asupra ambilor
operanzi.
Regulile de egalare automată a tipurilor valorilor respective permit execuţia operaţiei,
deşi nu conduc în majoritatea cazurilor la soluţia optimă. Astfel de conversii se numesc

5
implicite. Am văzut deja în cazul operatorilor de atribuire că acest tip de conversie poate
afecta în mod grosolan precizia rezultatelor.
De aceea, există posibilitatea de a se interveni din exterior ( în mod explicit), prin
operatorul cast, asupra tipului unui operand în vederea obţinerii unui rezultat conform
cerinţelor programatorului.

a) Conversii implicite
Conversiile implicite permit efectuarea expresiilor în care se operează cu valori de tipuri
diferite şi pentru care precizia rezultatelor nu este pe primul plan.
Dacă într-o operaţie intervin doi operanzi de acelaşi tip, atunci se execută operaţia şi
rezultatul va avea un tip egal cu tipul operanzilor. Rezultatul este exact dacă nu se
depăşeşte domeniul de valori al tipului respectiv.
Dacă într-o operaţie intervin doi operanzi cu tipuri diferite, atunci regula de bază este
egalarea tipurilor acestora cu tipul comun. În general, tipul comun este dat de tipul cu
domeniul de valori mai larg. Deci, rezultatul operaţiei este exact dacă nu se depăşeşte
domeniul tipului comun. Altfel, se consideră nedefinit.
Regulile după care se realizează conversiile implicite sînt următoarele:
- conversia tipurilor short int, unsigned short int şi enum la tipul int nu afectează
valorile corespunzătoare;
- conversia tipului char la tipul int se produce prin completarea octetului mai
semnificativ cu 0 sau cu extensie de semn, în funcţie de tipul char implicit: unsigned,
respectiv signed. Una din cele două variante se alege şi în declararea explicită a tipurilor
unsigned char şi signed char pentru conversia la tipul int.
- pentru conversia unei perechi de tipuri din mulţimea int (inclusiv variantele cu
modificatorii unsigned şi long), float, double sau long double se reţine tipul cu domeniul
de valori cel mai vast sau unsigend în cazul cînd unul din operanzi are tipul unsigned.
După execuţia unei astfel de operaţii, nu se afişează eroare în cazul depăşirii domeniului
de valori al tipului comun. Valoarea furnizată este negativă.
Conversiile implicite evidenţiate prin programului 8.11.1.1 se referă la o împărţire
în care intervin operanzi sau atribuiri de diferite tipuri, astfel:
- în prima linie a tabelului, operanzi sînt de tip int, rezultatul de prima împărţire
este de tip int, iar de la cea de a doua este de tip float. La primul rezultat s-a pierdut doar
din precizie, în schimb al doilea este complet eronat.
- în a doua linie, rezultatul este de tip float deoarece cel de al doilea operand este
de tip float. Precizia rezultatului este acceptabilă.
- în linia a treia se pot observa, de asemenea, diferenţe în precizia valorilor finale,
rezultatul împărţirii a două numere întregi se atribuie unei variabile de tip double în prima
situaţie, iar în cea de a doua rezultatul este de tip double deoarece unul din operanzi este
double. Coincidenţa ultimului rezultat cu cel din linia a doua este numai în aparenţă,
fiindcă am văzut în secţiunile anterioare că, intern, reprezentările în virgulă mobilă diferă
de la o situaţie la alta.
/* Conversii implicite */
#include <stdio.h>
#include <conio.h>
void main(void)
{ int a = 17, b =3;

6
float e = 3;
double d, f = 3;
clrscr();
printf("\n a/b= %d, a/b=%f", a/b, a/b);
printf("\n a/e=%f", a/e);
printf("\n a/b= %lf a/f=%lf", d=a/b, a/f);
getch(); }
a/b=5 a/b=0.000000 a/e=5.666667
a/b=5.000000 a/f=5.666667
Programul 8.11.1.1

b) Operatorul cast
Operatorul unar cast realizează conversia explicită a tipului unui operand numeric la cel
precizat de acesta. Sintaxa prin care se descrie utilizarea lui este: (nume_tip) expresie.
Reprezentarea rezultatului expresiei se face conform tipului prezentat între paranteze
simple prin operatorul de conversie. Prin utilizarea acestui operator se pot evita
conversiile degradante din cadrul expresiilor de atribuire.
Programul 8.11.1.2 descrie utilizarea operatorului cast în două situaţii. Valoarea
lui a se converteşte la tipul float în prima împărţire, iar în a doua valoarea lui b se
converteşte la tipul double.
/* Operatorul cast */
#include <stdio.h>
#include <conio.h>
void main(void)
{ int a = 217, b =35;
clrscr();
printf("\n a/b= %f" ,(float)a/b);
printf("\n a/b= %lf", a/(double)b); }
a/b=6.200000
a/b=6.200000
Programul 8.11.1.2

II. Ordinea de evaluare a expresiilor


O imagine de ansamblu a priorităţii operatorilor în evaluarea expresiilor se impune avînd
în vedere numărul ridicat al lor implementat în limbajul C. La fiecare tip de expresie s-au
precizat clasa de precedenţă şi regulile de asociativitate în cadrul aceeaşi clase, iar pentru
unele expresii chiar ordinea de efectuare a diferitelor clase de precedenţă. De asemenea,
reamintim că utilizarea parantezelor simple poate schimba precedenţa în cadrul unei clase
sau între clase diferite, pe lîngă creşterea facilităţii în urmărirea structurii unei expresii.
Sinteza ordinii de execuţie a operatorilor studiaţi pînă acum se prezintată în tabelul
8.12.1. Aceasta conţine sintaxa de descriere, semnificaţie, reguli de asociere la priorităţi
egale şi numărul operanzilor asupra cărora acţionează pe clase de precedenţă. Pentru
clasa a doua de precedenţă, doar operatorii de postincrementare şi postdecrementare au
prioritatea dreapta – stînga, restul fiind executaţi în ordinea stînga – dreapta. Astfel, tipul
de operator explicitat se aplică în jos tuturor operatorilor din linia respectivă.

7
Sintaxă Semnificaţie Aso Tip
cier
e
f(lista_p ) apel funcţie
t[indice] indexare 
s.c sau u.c selecţie directă
sc sau u selecţie indirectă
!vi negaţie logică unar
~vi negaţie la nivel de bit 
+v plus (fără efect)
-v minus
++v preincrementare
--v predecrementare
v++ postincrementare
v-- postdecrementare
&v deteminare adresă 
*p adresare indirectă
p adresare indirectă 
(tip)v conversie de tip (cast)
sizeof(tip) determină lungimea unei
sizeof e locaţii de memorie
v1 * v2 înmulţire binar
v1 / v2 împărţire 
vi1%vi2 restul unei împărţiri întregi (modulo)
adunare
v1 + v2 scădere
v1 – v2
vi1<<vi2 deplasare stînga  binar
vi1>>vi2 deplasare dreapta
v1 < v2 mai mic  binar
v1 > v2 mai mare
v1<=v2 mai mic sau egal
v1>=v2 mai mare sau egal
v1==v2 egal
v1!=v2 diferit
vi1&vi2 conjuncţie la nivel de bit  binar
vi1^vi2 sau exclusiv la nivel de
bit
vi1 | vi2 disjuncţie la nivel de bit
vi1&& vi2 conjuncţie logică  binar
vi || vi2 disjuncţie logică
ei? e1 : e2 operatorul condiţional  ternar

v=e atribuire, cu variantele :  binar


v *= e v /= e v %= e

8
v += e v -= e v &= e
v ^= e v |= e v <<= e, v >>= e
e1, e2, …, en secvenţiere 

Tabelul 8.12.1
Legendă:
 - asociere stînga - dreapta  - asociere dreapta – stînga
f – nume funcţie lista-p – listă de parametri
s – nume structură u – nume uniune
c – nume cîmp vi, vi1, vi2 – nume variabile întregi
v,v1, v2 – nume variabile p – nume pointer
e, e1, e2, …, en - expresii

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