Definitie. Succesiune de identificatori, constante, apeluri de functii (care intorc o valoare) si operatori, care respecta regulile sintatice.
Expresiile pot fi constante, atunci cand implica doar constante si operatori; astfel de expresii sunt evaluate la momentul compilarii.
Orice expresie se evalueaza si produce un rezultat. Rezultatul evaluarii unei expresii are un tip care depinde de tipul operanzilor si de operatorii din expresie.
Evaluarea expresiilor se face in concordanta cu anumite reguli care pot fi sintetizate in urmatoarea formulare:
In absenta parantezelor, evaluarea expresiilor se face in ordinea precedentei operatorilor, iar daca operatorii au aceeasi precedenta in ordinea data de asociativitatea operatorilor (de 939b18j regula de la stanga la dreapta).
Operanzi pot fi orice valori ( constante, variabile, valori returnate de functii, valori rezultate in urma evaluarii unei expresii) care se conformeaza tipului de opranzi asteptati de operatorii in cauza. (Contra)exemple: nu pot figura ca operanzi intr-o expresie aritmetica structuri/uniuni/campuri de biti (in ansamblu); valori reale nu pot fi operanzi in expresii cu operatorul %, operatorii logici pe biti, operatorii de deplasare.
In scopul evaluarii expresiilor operanzii simpli (care, la randul lor, nu sunt expresii) trebuie sa fie evaluati pentru a li se obtine valoarea. Ordinea in care se face evaluarea operanzilor simpli nu este precizata de standardul tiple ale aceluiasi operand in situatia in care cel putin una din aparitii este in contextul unei opratii cu efecte secundare. Exemplu:
a[i++]=i;
Tabela cu ordinea de precedenta a operatorilor limbajului C (preluata din B.Kernigham, D. Ritchie, - The C Programming Language, 2nd ed.) este prezentrata mai jos. Precedenta scade de la prima linie pana la ultima. In aceeasi linie sunt plasati operatori cu aceeasi precedenta.
OPERATORS |
ASSOCIATIVITY |
[ ] --> . |
left to right |
! ~ ++ -- + - * & (type) sizeof |
right to left |
/ % |
left to right |
- |
left to right |
<< >> |
left to right |
< <= > >= |
left to right |
left to right |
|
& |
left to right |
left to right |
|
| |
left to right |
&& |
left to right |
left to right |
|
?: |
right to left |
= += -= *= /= %= &= ^= |= <<= >>= |
right to left |
, |
left to right |
Operatorii disponibili in limbajul C pot fi clasificati in urmatoarele categorii:
adunare
scadere
inmultire
impartire
rest modulo
operatorul de incrementare
operatorul de decrementare
Efectul operatorilor de incrementare/decrementare este
i++ (sau ++i este echivalent cu i=i+1
i-- (sau --i este echivalent cu i=i-1
OBS. Cei doi operatori de mai sus sunt operatori unari (presupun un singur operand) si au particularitatea ca pot fi folositi ca operatori prefixati sau postfixati. Pozitia lor in raport cu operatorul indica momentul in care se face evaluarea: inainte, respectiv dupa de evaluarea operandului, evaluare care se face in eventualitatea ce operandul respectiv face parte dintr-o expresie in care intervin si alti operatori (pe langa cei de incrementare/ decrementare
Ex. In expresia
a[i++] + b[++j]
se ia elementul de indice i din tabloul a si apoi se incrementeaza I, respectiv se incrementeaza j si apoi se ia elementul corespunzator acestei noi valori a lui j din tabloul b pt evaluarea expresiei.
<
>
<=
>=
&& - "si " logic
- " sau" logic
In functie de valoarea operanzilor, operatorii logici "produc" urmatoarele rezultate:
Operatorii din aceasta categorie actioneaza asupra bitilor de pe aceasi pozitie din operanzi.
& - si" pe biti
sau" pe biti
"sau exclusiv" pe biti
complementare
Tinand cont ca operatorii din aceasta categorie actioneaza de fapt asupra bitilor din operanzi, in descrierea regulilor de evaloare, operanzii vor fi valorile posibile pentru un bit, adica 0 si respectiv 1:
OBS. Operatorii pe biti se folosesc de obicei pentru "stergerea" (punerea pe 0) sau "setarea" unor biti. De obicei, unul din operanzi e considerat masca prin care se precizeaza pozitiile din celalalt operand care trebuie sterse sau setate. Operatorul & se utilizeaza pentru stergerea bitilor care in masca au valoarea 0 (si pastrarea neschimbata a celor care au valoarea 1 in masca!) iar operatorul | se utilizeaza pentru setarea pe 1 a bitilor care in masca au valoarea 1 (si pastrarea nemodificata a celor care in masca au valoarea 0!)
>> - deplasare spre dreapta
<< - deplasare spre stanga
Sintaxa:
operand_1 >> nr_pozitii
operand_1 << nr_pozitii
Al doilea operand precizeaza numarul de pozitii cu care se deplaseaza spre dreapta sau spre stanga bitii primului operand. Ambii operanzi trebuie sa fie de tip intreg.
Indiferent de tipul operandului_1 (cu sau fara semn) pozitiile eliberate in cazul deplasarii la stanga (cele mai putin semnificative!) se completeaza cu 0.
Pozitiile eliberate in cazul deplasarii la dreapta (cele mai semnificative!) se completeaza in functie de tipul operandului_1 (cu sau fara semn)
cu bitul cel mai semnificativ, daca operandul e cu semn
cu 0 , daca operandul este fara semn (unsigned
Sintaxa:
exp1 ? exp2 : exp3
Mod de evaluare: daca exp1 este adevarata atunci rezultatul intregii expresii este rezultatul produs de evaluarea expresiei exp2, altfel cel produs de evaluarea lui exp3
operatorul de asignare
v op= u; este echivalent cu v = v op u;
Conversiile (in sensul discutiei care urmeaza) reprezinta transformari dintr-un mod de reprezentare a datelor in altul. Doua tipuri de conversii vor fi abordate:
a) conversii din reprezentarea interna in reprezentarea externa (lizibila) si invers
b) conversii intre diverse modalitati de reprezentare interna (conversii de tip
Intern (pentru stocarea in memorie si efectuarea de operatii) datele sunt reprezentate intr-unul din urmatoarele doua modalitati:
in complement fata de doi (date intregi)
in virgula mobila (date reale)
Extern ( atunci cand sunt generate de dispozitive de intrare cu care utilizatorul interactioneaza direct - tastatura - respectiv, pentru afisarea pe dispozitive de iesire destinate uzului utilizatorului uman - display, imprimanta) datele sunt reprezentate ca succesiuni de coduri (de ex. ASCII), cate un cod pentru fiecare caracter (cifra, litera, semn de punctuatie, etc).
Cand se citesc date de la tastatura, un program primeste o succesiune de coduri (de ex. ASCII) reprezentand
numere
intregi (ex. -1, 123, 1000) sau
reale (ex. 0.5, -1.0, 3.14)
"text" (orice altceva decat numere)
Datele numerice trebuie transformate in modul de reprezentare intern pentru a se putea efectua operatii cu ele.
Datele de tip text se pastreaza de obicei ca siruri de caractere (coduri ale caracterelor care compun textul)
Conversiile de date numerice in modul de reprezentare interna se realizeaza dupa regula generica:
Unde c0,c1,.cn
sunt cifrele numarului intreg de convertit din reprezentarea externa in
reprezentarea interna iar b este baza
de numeratie in care se reprezinta extern numarul (de obicei b=10).
Pentrut numerele reale, conversia se face conform regulii:
Unde m reprezinta numarul de cifre de la partea fractionara.
Pentru conversia din reprezentarea interna in cea externa, trebuie obtinute cifrele numarului respectiv, lucru posibil utilizand restul modulo b (restul imartirii intregi a numarului la baza de numeratie) pentru cea mai putin semnificativa cifra, apoi restul modulo b fata de catul dintre numarul initial si baza de numeratie b, pentru a doua (cea mai putin semnificativa) cifra si asa mai departe, pana cand catul impartirii devine mai mic decat b. Evident, intrucat cifrele se obtin si se acumuleaza intr-un buffer in ordine inversa, este necesara o inversare a sirului de caractere astfel obtinut.
Mai jos se dau codurile sursa pentru cateva rutine de conversie (din B.Kernigham & D.Ritchie - The C Programming Language, 2nd edition - [K&R 88] ).
/* atoi: convert s to integer */
int atoi(char s[ ])
#include <ctype.h>
/* atof: convert string s to double */
double atof(char s[ ])
return sign * val / power;
}
/* atoi: convert string s to integer using atof */
int atoi(char s[ ])
/* itoa: convert n to characters in s */
void itoa(int n, char s[ ])
while ((n /= 10) > 0); /* delete it */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
Cand conversiile se fac intre diverse modalitati de reprezentare interna, se vorbeste de conversii de tip. Conversiile de tip pot avea loc implicit sau explicit.
Atunci cand operanzii unei expresii sunt de tipuri diferite, valoarea operandului de tip "mai slab" se converteste la tipul operandului de tip "mai tare".
OBS. De convertit se converteste valoarea, deci, in cazul unui operand -variabila, tipul variabilei nu se schimba!
Prin tip mai tare vom intelege in continuare un tip care permite reprezentarea a mai multa informatie (plaja de valori mai mare si/sau parte fractionara) decat un tip mai slab.
De exemplu tipul int este mai tare decat tipul char pentru ca plaja de valori reprezentabile este mai mare (-32768 pana la 32767 fata de -128 pana la 127), tipul float este mai tare decat tipul long int pentru ca are plaja de valori reprezentabile mai mare si in plus permite si reprezentarea partii fractionare.
Pe scurt regulile dupa care au loc conversiile implicite pentru operanzi fara semn sunt urmatoarele:
Daca unul din operanzi este long double, atunci si celalalt este convertit la long double
altfel
daca unul din operanzi este double atunci si celalalt este convertit la double
altfel
daca unul din operanzi este float atunci si celalalt este convertit la float
altfel
se converteste operandul (operanzii) char si short la int
apoi
daca unul din operanzi este long int, atunci si celalalt operand se converteste la long int
Daca unul din operanzi este unsigned , lucrurile se complica din cauza comparatiilor dintre valorile cu semn si cele fara semn, care sunt dependente de implementare, intrucat depind de lungimea reprezentarii pentru tipurile intregi.
Exista o exceptie de la regula enuntata anterior, privind conversiile implicite si anume, cand operatorul este operatorul de asignare, atunci operandul din partea dreapta se converteste la tipul operandului din partea stanga, chiar daca acest lucru presupune pierdere de informatie, intrucat in urma asignarii, valoarea operandului drept trebuie stocata in zona de memorie ocupata de operandul stang, zona care este fixata (prin declaratia de tip!).
La conversia unei valori de tip intreg mai tare la un tip intreg mai slab se trunchiaza cifrele cele mai semnificative. In cazul conversiei unei valori reale la un tip intreg se trunchiaza partea fractionara si, daca e cazul, cifrele cele mai semnificative care nu pot fi reprezentate.
In cazul conversiei unei valori reale de tip mai tare la un tip real mai slab se face sau nu rotunjire, in functie de implementare.
Exemple
Intrucat argumentele functiilor sunt la randul lor expresii, conversiile de tip au loc si atunci cand argumentul actual e diferit de tipul specificat in prototipul (declaratia) functiei. In absenta prototipului unei functii argumentele actuale de tip char si short se convertesc la double. De aceea parametrii functiilor care in mod normal ar fi de tipul char/short se vor declara de tipul int (pentru a se economisi conversia implicita la int).
Conversiile explicite de tip se realizeaza cu ajutorul operatorului de cast.
(tip)expresie
|