Document, comentariu, eseu, bacalaureat, liceu si facultate
Top documenteAdmitereTesteUtileContact
      
    


 


Ultimele referate adaugate

Adauga referat - poti sa ne ajuti cu un referat?

Politica de confidentialitate



Ultimele referate descarcare de pe site
  CREDITUL IPOTECAR PENTRU INVESTITII IMOBILIARE (economie)
  Comertul cu amanuntul (economie)
  IDENTIFICAREA CRIMINALISTICA (drept)
  Mecanismul motor, Biela, organe mobile proiect (diverse)
  O scrisoare pierduta (romana)
  O scrisoare pierduta (romana)
  Ion DRUTA (romana)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  COMPORTAMENT PROSOCIAL-COMPORTAMENT ANTISOCIAL (psihologie)
  Starea civila (geografie)
 

Ultimele referate cautate in site
   domnisoara hus
   legume
    istoria unui galban
   metanol
   recapitulare
   profitul
   caract
   comentariu liric
   radiolocatia
   praslea cel voinic si merele da aur
 
despre:
 
TIPURI, OPERATORI SI EXPRESII IN C
Colt dreapta
Vizite: ? Nota: ? Ce reprezinta? Intrebari si raspunsuri
 

Variabilele si constantele sint obiectele - date de baza mani pulate intr-un program. Declaratiile listeaza variabilele ce se vor folosi si specifica tipul lor si probabil, valorile lor initiale. Operatorii specifica ce trebuie facut cu ele. Expre siile combina variabile si constante pentru a produce valori noi. v2s23se
Toate acestea constituie subiectul acestui capitol.

2.1. Nume de variabile

Cu toate ca nu am spus-o pina acuma, exista unele restrictii asupra numelor de constante si variabile. Numele sint alca tuite din litere si cifre; primul caracter trebuie sa fie o litera. Liniuta de subliniere "_" este considerata litera; ea este utila in usurarea citirii numelor lungi de variabile. Literele mari si mici sint caractere distincte; practica traditionala in
C foloseste literele mici pentru nume de variabile si literele mari pentru constantele simbolice.
Numai primele opt caractere ale unui nume intern sint semni ficative, cu toate ca se pot folosi mai multe. Pentru numele externe, de exemplu nume de functii si de variabile externe, numarul de caractere poate sa fie mai mic ca 8, deoarece numele externe sint folosite de diferite asambloare si incarcatoare. In
Anexa A se dau detalii. Mai mult, cuvinte cheie ca: if, else, int, etc sint rezervate: nu pot fi folosite ca nume de variabile
(trebuie sa fie scrise cu litere mici).
Natural, e intelept sa alegem numele de variabile astfel incit sa insemne ceva, legat de scopul variabilei, si e nepla cut sa amestecam litere mari cu mici.

2.2. Tipuri si marimi de date

Exista numai citeva tipuri de date de baza in limbajul C:

char un singur octet, capabil sa pastreze un caracter din setul local de caractere

int un intreg, reflectind tipic marimea efectiva a intregilor pe calculatorul gazda

float numar flotant in simpla precizie




double numar flotant in dubla precizie.

In plus, exista un numar de calificatori care pot fi aplicati tipului "int": short, long si unsigned. short si long se refera la diferite marimi de intregi. Numerele "unsigned" se supun legilor aritmeticii modulo 2^n unde n este numarul de biti dintr-un int; ele sint intodeauna pozitive. Declaratiile pentru cali ficatori arata astfel:

short int x;

long int y;

unsigned int z;

Cuvintul int poate fi omis in astfel de situatii, ceea ce se si intimpla de obicei.

Precizia acestor obiecte depinde de calculatorul care le mi nuieste; tabelul urmator da citeva valori reprezentative:

DEC PDP11 Honeywell 6000 IBM/370 Interdata 8/32
ASCII ASCII EBCDIC ASCII

char 8 biti 9 biti 8 biti 8 biti int 16 36 32 32 short 16 36 16 16 long 32 36 32 32 float 32 36 32 32 double 64 72 64 64

Intentia e ca short si long sa aiba lungimi diferite de intregi unde e practic; int reflecta normal, cea mai "naturala" lungime pentru un calculator. Asa cum puteti vedea, fiecare compilator este liber sa interpreteze short si long in functie de hardul pe care se executa. Ceea ce trebuie sa notati este ca short nu este niciodata mai lung decit long.

2.3. Constante

Constantele int si float au fost deja expuse; notam in plus ca notatia uzuala

123.456e-7

sau notatia stiintifica

0.12E3

pentru numerele flotante sint ambele legale. Orice constanta flotanta este considerata ca fiind de tipul double, asa ca nota tia "e" serveste atit pentru float cit si pentru double.
Constantele lungi sint scrise in stilul 123L. O constanta intreaga normala care este prea lunga pentru un int, este luata deasemenea ca fiind o constanta long.
Exista o notatie speciala pentru constantele octale si hexazecimale: un 0 (zero) la inceputul unei constante int inseamna octal; un 0x sau 0X la inceputul unei constante int inseamna hexazecimal. De exemplu, numarul zecimal 31 poate fi scris 037 in octal si 0x1f sau 0X1F in hexazecimal. Constantele octale si hexazecimale pot fi urmate un L pentru a le face "long".
O constanta caracter este un caracter singur scris intre ghilimele simple ca, de exemplu, 'x'. Valoarea unei constante caracter este valoarea numerica a caracterului in setul de carac tere al calculatorului. De exemplu, in setul de caractere ASCII, caracterul zero, sau '0', are valoarea 48, iar in EBCDIC, 240, amindoua valorile fiind diferite de valoarea numerica 0. Scriind
'0' in loc de o valoare numerica de tipul 48 sau 240, facem programul independent de o valoare particulara. Constantele caracter participa in operatiile numerice la fel ca oricare alte numere, cu toate ca cel mai adesea ele sint folosite in comparari cu alte caractere. O sectiune viitoare va trata toate regulile de conversie.
Anumite caractere negrafice pot fi reprezentate constante caracter cu ajutorul secventelor escape, de exemplu \n (linie noua), \t (tab), \0 (nul), \\ (backspace), \'(ghilimea simpla) etc, care arata ca doua caractere, dar de fapt sint unul singur.
In plus, se poate genera orice model de lungime un octet, scriind:

'\ddd'

unde 'ddd' reprezinta 1 - 3 cifre octale, ca in

#define FORMFEED '\014' /* ASCII formfeed */

Constanta caracacter '\0' reprezinta caracterul ce are valoarea
'\0' se scrie adesea in locul lui 0 pentru accentua natura caracter a anumitor expresii.
O expresie constanta este o expresie care implica numai constante. Astfel de expresii sint evaluate la compilare si nu la executie si ele pot fi folosite in orice loc in care poate apare o constanta, ca in

#define MAXLINE 1000 char lineaMAXLINE+1i; sau

seconds = 60 * 60 * hours;

O constanta-sir este o secventa compusa din zero sau mai multe caractere intre ghilimele duble, ca

"I am a string"

sau
"" /* un sir nul */

Ghilimelele duble nu sint parte a sirului ci servesc doar ca delimitatori. Aceleasi secvente escape folosite pentru constan tele caracter se aplica si la siruri; \" reprezinta caracterul dubla ghilimea.
Tehnic, un sir este un tablou ale carui elemente sint carac tere. Compilatorul plaseaza automat un caracter nul \0 la sfirsi tul oricarui astfel de sir, astfel ca programele pot determina lesne sfirsitul sirului. Aceasta reprezentare spune ca nu exista o limita reala pentru lungimea unui sir, dar programele trebuie sa parcurga tot sirul pentru a-i determina lungimea. Memoria fizica ceruta este cu o locatie mai mult decit numarul de carac tere scrise intre ghilimele duble. Functia urmatoare, strlen(s) returneaza lungimea unui sir de carctere s, exclusiv termina torul \0.

strlen(s) /* returneaza lungimea lui s */ char sai;
A int i; i = 0;
while (saii != '\0')
++i; return(i);
S

Trebuie distins intre o constanta caracter si un sir care contine un singur caracter: 'x' si "x" nu sint acelasi lucru.
Primul este un caracter, folosit pentru a produce valoarea numerica a caracterului x din setul de caractere al calculatoru lui; al doilea este un sir de caractere care contine un singur caracter (litera x) si un \0.

2.4. Declaratii

Toate variabilele trebuie declarate inainte de a fi folosite , cu toate ca anumite declaratii pot fi facute implicit de con text. O declaratie specifica un tip si este urmata de o lista de una sau mai multe variabile de acel tip, ca in exemplul de mai jos:

int lower, upper, step; char c, linea1000i;

Variabilele pot apare oricum printre declaratii. Lista de mai sus poate fi scrisa, in mod egal, si astfel:

int lower; int upper; int step; char c; char linea1000i;

Aceasta ultima forma ocupa mai mult spatiu dar este mai comoda pentru a adauga cite un comentariu la fiecare declaratie sau pentru modificari ulterioare.
Variabilele pot fi, deasemenea, initializate in declara tia lor, cu toate ca exista anumite restrictii. Daca numele este urmat de un semn egal si de o constanta, aceasta serveste la initializare, ca in:

char backslash = '\\'; int i = 0; float eps = 1.0e-5;

Daca variabila in chestiune este externa sau statica, initializa rea este facuta o singura data, conceptual inainte ca pro gramul sa-si inceapa executia. Variabilele automate initializate explicit sint initializate la fiecare apel al functiei in care sint continute. Variabilele automate pentru care nu exista o initializare explicita au valoare nedefinita (adica gunoi). Varia bilele externe si statice se initializeaza implicit cu zero dar este un bun stil de programare acela de a declara initializrea lor in orice caz.
Vom discuta initializarile mai departe pe masura ce se introduc noi tipuri de date.

2.5. Operatori aritmetici

Operatorii aritmetici binari sint "+", "-", "*", "/" si operatorul modulo "%". Exista operatorul "-" unar dar nu exista opera torul unar "+". Impartirea intregilor trunchiaza orice parte frac tionara. Expresia

x % y

produce restul cind x se imparte la y si deci este zero cind impartirea este exacta. De exemplu, un an este bisect daca este divizibil cu 4 si daca nu este divizibil cu 100, insa anii divizi bili cu 400 sint bisecti. Deci

if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) it's a leap year else it's not

Operatorul % nu poate fi aplicat la float sau double.

Operatorii + si - au aceeasi pondere, care este mai mica decit ponderea (identica) a lui *, / si % care la rindul ei este mai mica decit ponderea operatorului unar -. Operatorii aritmetici se grupeaza de la stinga la dreapta (Tabela de la sfirsitul capitolului rezuma ponderea si asociativitatea pentru toti operatorii). Ordinea de evaluare nu este specificata pentru operatorii asociativi si comutativi de tipul lui * si +.
Compilatorul poate rearanja un calcul cu paranteze implicind unul din acestia. Astfel, a+(b+c) poaate fi evaluat ca (a+b)+c.
Acest lucru produce rar diferente dar daca se cere o ordine parti culara, trebuie folosite explicit variabilele temporare.
Actiunile care produc depasiri superioare sau inferioare depind in ultima instanta de calculator.

2.6. Operatori relationali si logici

Operatorii relationali sint > >= < <=. Ei au toti aceasi pon dere. Sub ei in tabelul de ponderi se afla operatorii de egali tate == != , care au o aceeasi pondere. Operatorii relationali au ponderea mai mica decit cei aritmetici, asa ca expresii de tipul i < lim-1 se evalueaza ca i < (lim-1), asa cum ar fi de asteptat.

Mai interesanti sint conectorii logici && si ||. Expresiile care-i contin sint evaluate de la stinga la dreapta si evaluarea se opreste in clipa in care se cunoaste adevarul sau falsul rezultatului. Aceste proprietati se dovedesc critice in scrierea programelor. De exemplu iata o bucla luata din functia de intrare getline, pe care am scris-o in Capitolul 1:

for (i=0; i<lim-1 && (c = getchar()) != '\n' && c != EOF;++i) saii = c;

In mod clar, inainte de a citi un nou caracter trebuie vazut daca mai exista loc pentru a-l depune in tabloul s , asa ca testul i<lim-1 trebuie facut in primul rind. Nu numai atit dar daca testul esueaza, nu trebuie sa mai citim un nou caracter.

Similar, ar fi nepotrivit sa testam daca c este EOF inainte de apelul lui getchar; apelul trebuie sa aiba loc inainte ca sa testam caracterul c.
Ponderea lui && este mai mare decit cea a lui || si amindoua sint mai mici decit cele ale operatorilor relationali si de egalitate, asa ca expresii de tipul

i<lim-1 && (c = getchar()) != '\n' && c != EOF

nu mai au nevoie de paranteze suplimentare. Dar, deoarece ponde rea lui != este mai mare decit cea a asignarii, este nevoie de paranteze in

(c = getchar()) != '\n'

pentru a obtine rezultatul dorit.
Operatorul unar de negatie "!" converteste un operand nonzero sau adevarat in zero si un operand zero sau fals in 1. O utilizare obisnuita a lui ! este in constructii de tipul

if (!inword)

mai degraba decit

if (inword == 0)

Este mai greu sa generalizam care forma este mai buna. Construc tiile de tipul !inword arata mai frumos ("daca nu e in cuvint"), dar constructiile mai complicate pot fi greu de inteles.

Exercitiul 2.1. Scrieti o bucla echivalenta cu bucla for de mai sus fara a folosi &&.

2.7. Conversii de tip

Cind intr-o expresiie apar operanzi de mai multe tipuri, ei se convertesc intr-un tip comun, dupa un numar mic de reguli. In general, singurele conversii care se fac automat sint acelea cu sens , de exemplu convertirea unui numar intreg intr-un flotant in expresii de tipul f + i. Expresiile fara sens, de exemplu folosirea unui float ca indice de tablou, nu sint permise.
In primul rind, char-i si int-i pot fi amestecati in expresiile aritmetice: orice char este convertit automat intr-un int. Aceasta permite o flexibilitate remarcabila in anumite tipuri de transformari de caractere. Exemplificam cu functia atoi, care converteste un sir de cifre in echivalentul lui numeric.

atoi(s) /* converteste un sir s in intreg */ char sai;
A int i, n; n = 0; for (i = 0; saii >= '0' && saii <= '9' ; ++i) n = 10 * n + saii - '0'; return(n);
S

Asa cum am vazut in Capitolul 1, expresia

saii-'0'

reprezinta valoarea numerica a caracterului aflat in saii deoarece valorile lui '0','1', etc formeaza un sir crescator pozitiv si contiguu.
Un alt exemplu de conversie intre char si int il constituie functia lower care transforma literele mari din setul de caractere ASCII in litere mici. Daca intrarea nu este o litera mare, functia o returneaza neschimbata:

lower(c) /* conversie ASCII litere mari in litere mici */ int c;
A if (c >= 'A' && c <= 'Z') return(c + 'a' - 'A'); else return(c)
S

Aceasta functie este valabila numai pentru ASCII deoarece pe de o parte intre literele mari si literele mici exista o distanta fixata, ca valoare numerica, iar pe de alta parte ambele alfabete sint contigue - intre A si Z se gasesc numai litere.
Aceasta ultima observatie nu este valabila pentru setul de caractere EBCDIC (IBM 360/370), asa incit functia lower esueaza pentru aceste sisteme, ea va converti mai mult decit literele mari.
Exista o subtilitate in conversia caracterelor in intregi.
Limbajul nu specifica daca o variabila de tip char este o canti tate cu semn sau fara semn. Cind un char este convertit intr-un int, poate el produce un intreg negativ ? Din pacate, aceasta variaza de la calculator la calculator, reflectind diferen tele arhitecturale. Pe anumite calculatoare (de exemplu PDP-11) un char al carui cel mai din stinga bit este 1 va fi convertit intr-un intreg negativ ("extensie de semn". Pe altele, un char este convertit intr-un int prin adaugarea de zerouri in partea stinga si astfel el este intodeauna pozitiv.
Definitia lui C asigura ca orice caracter din setul stan dard al masinii nu va fi niciodata negativ, asa ca aceste carac tere pot fi folosite liber in expresii ca si cantitati pozitive.
Dar modele arbitrare de biti memorate in variabile de tip charac ter pot apare drept negative pe anumite calculatoare si drept pozitive pe altele.
Cea mai comuna aparitie a acestei situatii este cind pentru
EOF se foloseste -1. Sa consideram codul:

char c; c = getchar(); if (c == EOF)
...

Pe un calculator care nu face extensie de semn, c este intodeauna pozitiv deoarece el este un char, dar totusi EOF este negativ. In consecinta testul esueaza intodeauna. Pentru a evita aceasta, trebuie sa avem grija atunci cind folosim int in loc de char pentru orice variabila care primeste o valoare returnata de getchar.
Adevarata ratiune pentru utilizarea lui int in loc de char nu este legata cu nimic de posibila extensie de semn. Pur si simplu, getchar trebuie sa returneze toate caracterele posibile
(astfel incit sa poate fi folosita pentru a citi o intrare arbi trara) si in plus, o valoare pentru EOF distincta. Astfel, aceasta valoare nu poate fi reprezentata ca si un char dar, in schimb, trebuie memorata ca si un int.
O alta forma utila de conversie de tip automata este aceea ca expresiile relationale de tipul i > j si expresiile logice conectate prin && si || se definesc a avea valoarea 1 pentru adevar si 0 pentru fals. Astfel, o asignare:

isdigit = c >= '0' && c <= '9';

pune pe isgit pe 1 daca c este o cifra si pe 0 daca nu. (In partea de test a lui if, while ,for, etc, "adevarat" inseamna
"nonzero").

Conversiile aritmetice implicite lucreaza in mare masura cum ne asteptam. In general, daca un operator ca + sau * care are doi operanzi (un "operator binar") are operanzi de tipuri diferite, tipul "inferior" este promovat la tipul "superior" ina inte de executia operatiei. Rezultatul insusi este de tipul supe rior. Mai precis, pentru fiecare operator aritmetic, se aplica urmatoarea secventa de reguli de conversie:

char si short se convertesc in int iar float este conver tit in double.

Apoi, daca un operand este double , celalalt este conver tit in double iar rezultatul este double.

Altfel, daca un operand este long, celalalt este convertit in long iar rezultatul este long.

Altfel, daca un operand este unsigned, celalalt este convertit inunsigned, iar rezultatul este unsigned.

Altfel, operanzii trebuie sa fie int, iar rezultatul este int.

Sa notam ca toti float dintr-o expresie sint convertiti in double; orice calcul flotant in C este facut in dubla precizie.

Conversiile se fac in asignari; valoarea partii drepte este convertita la tipul din stinga, care este tipul rezulta tului. Un caracter este convertit intr-un int fie cu exten sie de semn, fie nu, asa cum s-a descris mai sus. Operatia inversa, int in char, se comporta bine, pur si simplu, bitii de ordin superior in exces sint eliminati. Astfel, in:

int i; char c; i = c; c = i;

valoarea lui c ete neschimbata. Acesta este adevarat si cind extensia de semn este implicita si cind nu este implicita.

Daca x este float iar i este int, atunci:

x = i; si i = x;

provoaca amindoua conversii; float in int provoaca trunchierea oricarei parti fractionare. double este convertit in float prin rotunjire. Intregii lungi sint convertiti in scurti sau in char prin pierderea bitilor de ordin superior in exces.
Deoarece argumentul unei functii este o expresie, con versia de tip are loc deasemenea si cind argumentele sint pasate functiei in particular, char si short devin int, iar float devine double. Iata de ce am declarat argumentul functiei ca fiind int si double chiar cind functia este apelata cu char si float.
In final, conversia explicita de tip poate fi fortata in orice expresie cu o constructie numita "distribuire"(cast). In constructia:

(numedetip) expresie

sus. Semnificatia precisa a unei distribuiri este de fapt ca si daca o expresie ar fi asignata la o variabila de tipul specifi cat, care este apoi folosita in locul intregii constructii. De exemplu, rutina din biblioteca sqrt are nevoie de un argument double si va produce nonsens daca i se da sa minuiasca altceva.
Astfel, daca n este un intreg:

sqrt((double) n)

il converteste pe n in double inainte de a-l pasa lui sqrt.

(De notat ca distribuirea produce valoarea n in tipul potrivit; continutul efectiv al lui n nu este alterat ). Operatorul de distribuire are acceasi pondere ca si alti operatori unari, asa cum apare si in tabelul recapitulativ de la sfirsitul capitolu lui.

Exercitiul 2.2. Scrieti o functie htoi(s) care converteste un sir de cifre hexazecimale in valoarea sa intreaga echiva lenta. Cifrele sint de la 0 la 9, literele de la a la f si de la
A la F.

2.8. Operatori de incrementare si decrementare

Limbajul C ofera doi operatori neuzuali pentru incrementarea si decrementarea variabilelor. Operatorul de incrementare ++ aduna 1 la operandul sau; operatorul de decrementare -- scade 1. Am folosit frecvent ++ pentru a incrementa variabi lele, de exemplu:

if (c == '\n')
++nl;

Aspectul neobisnuit al lui ++ si al lui -- este acela ca ei pot fi folositi atit ca operatori prefix (inaintea variabilei, ca in ++n) cit si ca operatori sufix (dupa variabila, ca in n++).
In ambele cazuri, efectul este incrementarea lui n. Dar expresia
++n il incrementeaza pe n inainte de a-i folosi valoarea, in timp ce expresia n++, il incrementeaza pe n dupa ce a fost folo sita valoarea lui. Aceasta inseamna ca intr-un context in care valoarea este folosita, si nu numai efectul, ++n si n++ sint diferiti. Daca n este 5, atunci:

x = n++;

il face pe x egal cu 5, dar

x = ++n;

il face pe x egal cu 6. In ambele cazuri, n devine 6.
Operatorii de incrementare si decrementare se pot aplica numai variabilelor. O expresie de tipul x = (i+j)++ este ilegala.
Intr-un context in care valoarea nu este folosita, ci numai efectul de incrementare, ca in

if (c == '\n') nl++;

alegeti modul prefix sau sufix dupa gustul dumneavoastra. Dar exista totusi situatii in care unul sau altul este apelat din adins. De exemplu, sa consideram functia squeeze(s,c) care elimina toate aparitiile lui c din sirul s:

squeeze (s,c) /* sterge toate aparitiile lui c din s */ char sai; int c;
A int i, j; for (i = j = 0; saii != '\0'; i++) if (saii != c) saj++i = saii; saji = '\0';
S

De fiecare data cind apare un caracter non-c el este copiat in pozitia j curenta si numai dupa aceea j este incrementat pentru a fi gata pentru urmatorul caracter. Aceasta constructie este echivalenta cu urmatoarea:

if (saii != c) A saji = saii; j++;
S

Un alt exemplu de constructie similara este luata din functia getline pe care am scris-o in Capitolul 1, in care putem inlocui

if (c == '\n' A saii=c;
++i;
S

cu mult mai compacta constructie:

if (c == '\n') sai++i = c;

Ca un al treilea exemplu functia strcat(s,t) care concatenea za sirul t la sfirsitul sirului s. strcat presupune ca exista suficient spatiu in s pentru a pastra combinatia.

strcat (s,t) /* concateneaza pe t la sfirsitul lui s */ char sai, tai; /* s trebuie sa fie suficient de mare */
A int i, j; i = j = 0;
while (saii != '\0') /* gaseste sfirsitul lui s */ i++;
while ((sai++i = taj++i) != '\0') /* copiaza pe t */
;
S

Cum fiecare caracter este copiat din t in s, se aplica postfixul
++ atit lui i cit si lui j pentru a fi siguri ca sint pe pozitie pentru urmatorul pas din bucla.

Exercitiul 2.3. Scrieti o alta versiune a lui squeeze(s1, s2) care sterge fiecare caracter din s1 care se potriveste cu vreun caracter din s2.

Exercitiul 2.4. Scrieti functia any(s1, s2) care returneaza prima locatie din sirul s1 in care apare vreun c acter din sirul s2, sau pe -1 daca s1 nu contine nici un caracter din s2.

2.9. Operatori logici pe biti

Limbajul C ofera un numar de operatori pentru manipularea biti lor; acestia nu se pot aplica lui float si double.

& SI bit cu bit
| SAU inclusiv bit cu bit
^ SAU exclusiv bit cu bit
<< deplasare stinga
>> deplasare dreapta
I complement fata de 1 (unar)

Operatorul SI bit cu bit "&" este folosit adesea pentru a masca anumite multimi de biti; de exemplu

c = n & 0177;

pune pe zero toti biti lui n, mai putin bitul 7 (cel mai tare).
Operatorul SAU bit cu bit "|" este folosit pentru a pune pe 1 biti:

x = x | MASK;

pune pe 1 in x bitii care sint setati pe 1 in MASK.

Trebuie sa distingeti cu grija operatorii pe biti & si | de conectorii logici && si ||, care implica o evaluare de la stinga la dreapta a unei valori de adevar. De exemplu, daca x este 1 si y este 2, atunci x & y este zero dar x && y este 1. (De ce ?)

Operatorii de deplasare << si >> realizeaza deplasari la stinga si la dreapta pentru operandul lor din stinga, cu numarul de pozitii dat de operandul din dreapta lor. Astfel x << 2 deplasea za la stinga pe x cu doua pozitii, umplind locurile libere cu zero; aceasta este echivalent cu inmultirea cu 4. Deplasind la dreapta o cantitate unsigned, bitii vacanti se umplu cu zero.
Deplasind la dreapta o cantitate cu semn, bitii vacanti se umplu cu semnul ("deplasarea aritmetica") pe anumite calculatoare, ca de exemplu PDP-11 si cu 0 ("deplasare logica") pe altele.

Operatorul unar I da complementul fata de 1 al unui intreg; adica, el converteste fiecare bit de 1 in 0 si vicevesa. Acest operator isi gaseste utilitate in expresii de tipul

x & I077

care mascheaza ultimii 6 biti ai lui x pe 0. De notat ca x & I077 este independent de lungimea cuvintului si deci prefe rabil, de exemplu, lui x & 0177700, care presupune ca x este o cantitate cu o lungime de 16 biti. Forma portabila nu implica un cost mai mare, deoarece I077 este o expresie constanta si deci evaluata la compilare.

Pentru a ilustra folosirea unora din operatorii de biti, sa consideram functia getbits(x,p,n) care returneaza (cadrat la dreapta) cimpul de lungime n biti al lui x care incepe la pozitia p. Presupunem ca bitul 0 este cel mai din dreapta si ca n si p sint valori pozitive sensibile. De exemplu, getbits(x,4,3) returneaza 3 biti in pozitiile 4, 3 si 2, cadrati la dreapta.

getbits (x, p, n) /* ia n biti de la pozitia p */ unsigned x, p, n;
A return( (x >> (p+1-n) ) & I(I0 << n));
S

x >> (p+1-n) muta cimpul dorit la sfirsitul din dreapta al cuvintului. Declarind argumentul x ca fiind unsigned ne asiguram ca atunci cind el este deplasat la dreapta bitii vacanti vor fi umpluti cu 0 si nu cu bitii de semn, independent de calculatorul pe care este executat programul. I0 este cuvintul cu toti bitii pe
1; deplasindu-l la stinga cu n pozitii prin I0 << n cream o masca cu zerouri pe cei mai din dreapta n biti si 1 in rest; complemen tindu-l cu I facem o masca de 1 pe cei mai din dreapta n biti.

Exercitiul 2.5. Modificati getbits pentru a numara bitii de la stinga la dreapta.

Exercitiul 2.6. Scrieti o functie wordlength() care calculea za lungimea unui cuvint de pe calculatorul gazda, adica numarul de biti dintr-un int. Functia sa fie portabila in sensul ca acelasi cod sursa sa lucreze pe toate calculatoarele.

Exercitiul 2.7. Scrieti o functie rightrot(n, b) care roteste intregul n la dreapta cu b pozitii.

Exercitiul 2.8. Scrieti o functie invert(x,p,n) care inverseaza
(i.e. schimba pe 1 in 0 si viceversa) cei n biti ai lui x care incep de la pozitia p,lasindu-i pe ceilalti neschimbati.

2.10. Operatori si expresii de asignare

Expresii de tipul:

i = i + 2

in care membrul sting este repetat in membrul drept pot fi scrise intr-o forma condensata:

i += 2

folosind operatorul de asignare +=.
Majoritatea operatorilor binari (operatori ca +, care au un operand sting si un operand drept) au un operator de asignare corespunzator "op=", unde op este unul din:

+ - * / % << >> & ^ |

Daca e1 si e2 sint doua expresii, atunci:

e1 op= e2

este echivalent cu

e1 = (e1) op (e2)

cu exceptia ca e1 este calculat o singura data. Sa remarcam parantezele din jurul lui e2:

x *= y + 1

inseamna de fapt

x = x * (y + 1)

si nu

x = x * y + 1

Dam in continuare, drept exemplu, functia bitcount, care conto rizeaza numarul de biti pe 1 dintr-un argument intreg.

bitcount(n) /* contorizeaza bitii 1 din n */ unsigned n;
A int b; for (b = 0; n != 0; n >>= 1) if (n & 01) b++; return(b);
S

Lasind la o parte conciziunea, operatorii de asignare au avantajul ca ei corespund cel mai bine modului de gindire al oamenilor. Noi spunem "adunam 2 la i" sau " incrementam pe i cu 2" si nu "ia-l pe i, aduna 2, apoi pune rezultatul inapoi in i". Deci i += 2. In plus, pentru o expresie complicata, de tipul:

yyvalayypvap3 + p4i + yypvap1 + p2ii += 2

operatorul de asignare face codul mai usor de inteles, deoarece cititorul nu trebuie sa verifice sirguincios ca cele doua expresii sint intr-adevar o aceeasi sau sa se intrebe de ce nu sint. In plus, un operator de asignare ajuta chiar compilatorul sa produca un cod mai eficient.

Am folosit deja faptul ca o instructiune de asignare are o valoare si ca poate sa apara in expresii; exemplul cel mai comun:

while ((c = getchar()) != EOF)
...

Asignarile folosind alti operatori de asignare (+=, -=, etc) pot deasemenea sa apara in expresii, cu toate ca acestea se intim pla mai rar.
Tipul unei expresii de asignare este tipul operandului sau sting.

Exercitiul 2.9. Intr-un sistem cu numere cu complement fata de 2, x & (x-1) sterge bitul 1 cel mai departe de x. (De ce
?). Folositi aceasta observatie pentru a scrie o versiune mai rapida a lui bitcount.

2.11. Expresii conditionale

Instructiunile

if (a < b) z = a; else z = b;

calculeaza desigur in z maximul dintre a si b. Expresia conditio nala, scrisa cu operatorul ternar "? :" ofera un mod alternativ pentru a scrie acest lucru precum si constructii similare. In expresia:

e1 ? e2 : e3

expresia e1 se evalueaza prima. Daca ea este nonzero (adevara ta) atunci se evalueaza expresia e2 si aceasta este valoarea expresiei conditionale. Altminteri, se evalueaza e3 si aceasta este valoarea. Numai una din expresiile e2 si e3 se evalueaza.
Deci, pentru a pune in z maximul dintre a si b:

z = (a > b) ? a : b; /* z = max(a, b) */

Trebuie sa notam ca expresia conditionala este intr-adevar o expresie si ca ea poate fi folosita exact ca oricare alta expre sie. Daca e2 si e3 sint expresii de tipuri diferite, tipul reultatului se determina dupa regulile de conversie discutate mai inainte in acest capitol. De exemplu, daca f este un float si n este un int, atunci expresia

(n > 0) ? f : n

este de tipul double, indiferent daca n este pozitiv sau nu.
Parantezele nu sint necesare in jurul primei expresii a unei expresii conditionale, deoarece ponderea lui ? : este foarte mica, chiar deasupra asignarii. Ele sint totusi recoman date, pentru a face partea de conditie a expresiei mai usor de vazut.
Expresiile conditionale conduc adesea la un cod succint.
De exemplu, bucla urmatoare tipareste N elemente ale unui tablou,
10 pe linie, cu fiecare coloana separata printr-un blanc si cu fiecare linie (inclusiv ultima) terminata cu un singur caracter
"linie noua".

for (i = 0; i << N; i++) printf("%6d%c", aaii, (i % 10 == 9 || i == N-1) ? '\n' : ' ');

Un caracter "linie noua" se tipareste tot dupa al zecelea element si dupa al N-lea element. Toate celelalte elemente sint urmate de un blanc. Cu toate ca seamana cu un truc, este instruc tiv sa incercati sa scrieti lucrul acesta fara a folosi expresia conditionala.

Exercitiul 2.10. Rescrieti functia lower, care converteste literele mari in litere mici, cu o expresie conditionala in locul lui if-else.

2.12. Ponderea si ordinea de evaluare

Tabelul de mai jos rezuma regulile de pondere si asociativi tate pentru toti operatorii, inclusiv pentru aceia pe care nu i am discutat inca. Operatorii de pe aceeasi linie au aceeasi pondere; liniile sint in ordine de pondere descrescatoare, astfel ca, de exemplu, "*", "/" si "%" au o aceeasi pondere, care este mai mare decit a lui "+" "-".

+------------------------------+--------------------------------+
| () ai -> . | de la stinga la dreapta |
+------------------------------+--------------------------------+
| ! I ++ -- - (tip) * & sizeof | de la dreapta la stinga |
+------------------------------+-------------------------------+
| * / % | de la stinga la dreapta |
+------------------------------+--------------------------------+
| + - | de la stinga la dreapta |
+------------------------------+--------------------------------+
| << >> | de la stinga la dreapta |
+------------------------------+--------------------------------+
| < <= > >= | de la stinga la dreapta |
+------------------------------+--------------------------------+
| == != | de la stinga la dreapta |
+------------------------------+--------------------------------+
| & | de la stinga la dreapta |
+------------------------------+--------------------------------+
| ^ | de la stinga la dreapta |
+------------------------------+--------------------------------+
| | | de la stinga la dreapta |
+------------------------------+--------------------------------+
| && | de la stinga la dreapta |
+------------------------------+--------------------------------+
| || | de la stinga la dreapta |
+------------------------------+--------------------------------+
| ? : | de la dreapta la stinga |
+------------------------------+--------------------------------+
+------------------------------+--------------------------------+
| = += -= etc | de la dreapta la stinga |
+------------------------------+--------------------------------+
| , (Capitolul 3) | de la stinga la dreapta |
+------------------------------+--------------------------------+

Operatorii -> si. sint folositi pentru a accede membrii structuri lor; ei vor fi iscutati in Capitolul 6, impreuna cu sizeof (mari mea unui obiect). Capitolul 5 discuta * (indirectarea) si &
(adresa lui ...).

Sa notam ca ponderea operatorilor logici pe biti &, | si ^ este sub == i |=. Aceasta implica faptul ca expresiile care testeaza biti, ca de exemplu

if (( x & MASK) == 0) ...

trebuie sa fie cuprinse in intregime intre paranteze, pentru a da rezultatele steptate.
Asa cum am mentionat mai inainte, expresiile ce implica operatori asociativi si comutativi (+, *, &, ^, |) pot fi rearanjate chiar daca sint cuprinse in paranteze. In marea majoritate a cazurilor, aceasta nu da diferente; in situatia in care ar da, se pot folosi variabile temporare explicite pentru a forta o ordine de evaluare particulara.

Limbajul C, ca si majoritatea celorlalte limbaje, nu specifica in ce ordine se evalueaza operanzii unui operator. De exemplu, in instructiuni de tipul

x = f() + g();

f poate fi evaluat inaintea lui g sau viceversa; deci, daca sau f sau g altereaza o variabila externa de care depinde si cealalta, x poate depinde de ordinea de evaluare. Din nou, rezultatele intermediare pot fi stocate in variabile temporare pentru a fi siguri de o anumita secventa.
In mod similar, ordinea in care sint evaluate argumentele unei functii nu este specficata, asa ca instructiunea

printf("%d %d\n", ++n, power(2, n)); /* GRESIT */

poate (si o si face) produce rezultate diferite, pe diferite calculatoare, depinzind de faptul daca n este incrementat sau nu inainte de apelul lui power. Solutia, desigur, este sa scriem:

++n; printf("%d %d\n", n, power(2, n));

Apelurile de functii, instructiunile de asignare imbricate, operatorii de incrementare si decrementare provoaca "efecte secu ndare" - o anumita variabila este modificata ca un produs al unei evaluari de expresie. In orice expresie implicind efecte secundare, pot exista subtile dependente de ordinea in care sint stocate variabilele ce iau parte in expresie. O situatie neferi cita este ilustrata de instructiunea:

aaii = i++;

Chestiunea consta in a sti daca indicele este noua valoare a lui i sau daca este vechea. Compilatorul poate face aceste lucruri in moduri diferite, depinzind de interpretarea sa.
Cind efectele secundare (asignare la variabile efective) au loc, sint lasate la discretia compilatorului, caci cea mai buna ordine depinde puternic de arhitectura calculatorului.
Morala acestei discutii este aceea, ca scrierea de cod ce depinde de ordinea de evaluare, este o proasta practica de programare in orice limbaj. Natural, e necesar sa stim ce lucruri trebuie evitate, dar daca nu stim cum sint ele facute pe diferite calculatoare, aceasta inocenta ne va ajuta sa ne protejam. (Verificatorul lui C, lint, detecteaza majoritatea de pendentelor de ordinea de evaluare).


Colt dreapta
Creeaza cont
Comentarii:

Nu ai gasit ce cautai? Crezi ca ceva ne lipseste? Lasa-ti comentariul si incercam sa te ajutam.
Esti satisfacut de calitarea acestui document, eseu, cometariu? Apreciem aprecierile voastre.

Nume (obligatoriu):

Email (obligatoriu, nu va fi publicat):

Site URL (optional):


Comentariile tale: (NO HTML)


Noteaza documentul:
In prezent fisierul este notat cu: ? (media unui numar de ? de note primite).

2345678910

 
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite
Colt dreapta