|
Politica de confidentialitate |
|
• domnisoara hus • legume • istoria unui galban • metanol • recapitulare • profitul • caract • comentariu liric • radiolocatia • praslea cel voinic si merele da aur | |
TIPURI, OPERATORI SI EXPRESII IN C | ||||||
|
||||||
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 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 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 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. '\ddd' unde 'ddd' reprezinta 1 - 3 cifre octale, ca in #define FORMFEED '\014' /* ASCII formfeed */ Constanta caracacter '\0' reprezinta caracterul ce are valoarea #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 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. strlen(s) /* returneaza lungimea lui s */ char sai; Trebuie distins intre o constanta caracter si un sir care contine un singur caracter: 'x' si "x" nu sint acelasi lucru. 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. 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 +. 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. 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. 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. atoi(s) /* converteste un sir s in intreg */ char sai; 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. lower(c) /* conversie ASCII litere mari in litere mici */ int c; 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. 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. 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 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. (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. sqrt((double) n) il converteste pe n in double inainte de a-l pasa lui sqrt. 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 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') 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++). 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. 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; 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++; 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; 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 */ ++ 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 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). 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. 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; 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 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 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 +=. + - * / % << >> & ^ | 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; 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. 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. 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 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. 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. 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 "+" "-". +------------------------------+--------------------------------+ 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 & 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. 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. 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. |
||||||
|
||||||
|
||||||
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite |
|