You are on page 1of 278

CUPRINS

Capitolul I INTRODUCERE N ARHITECURA SISTEMELOR DE CALCUL............................... 1

1.1. Importan a limbajului C....................................................1 1.2 Arhitectura de baz a unui calculator ................................4 1.2.1 Microprocesorul .........................................................6 1.2.2 Memoria .....................................................................8 1.2.3 Echipamentele periferice..........................................10 1.3. Programarea calculatorului .............................................13 1.3.1. Sistemul de operare .................................................14 1.3.2. Tipuri de fiiere .......................................................17 1.3.3. Construirea fiierului executabil .............................18
Capitolul II REPREZENTAREA DATELOR N CALCULATOR................. 23 2.1. Reprezentarea intern/extern a numerelor...........................24 2.2. Reprezentarea extern a numerelor ......................................25

2.2.1. Reprezentarea extern a numerelor ntregi..............26 2.2.2. Reprezentarea extern a numerelor reale ................29 2.3 Reprezentarea intern a numerelor ..................................31 2.3.1. Reprezentarea intern a numerelor ntregi ..............31 2.3.2 Adunarea, scderea i nmul irea numerelor ntregi.33 2.3.3 Reprezentarea intern a numerelor reale..................35 Game de reprezentare pentru numerele reale....................46 2.3.5. Codificare BCD.......................................................47
Capitolul III ELEMENTELE DE BAZ ALE LIMABJULUI C...................... 49

3.1. Crearea i lansarea n execu ie a unui program C...........49 3.2. Structura unui program C ...............................................51 3.3. Mul imea caracterelor .....................................................53 I

3.3.1. Litere i numere.......................................................53 3.3.2. Caractere whitespace...............................................53 3.3.3. Caractere speciale i de punctua ie..........................54 3.3.4. Secven e escape.......................................................54 3.4. Identificatori ...................................................................56 3.5. Cuvintele cheie ale limbajului C.....................................56 3.6. Constante ........................................................................56 3.6.1. Constante caracter ...................................................57 3.6.2. Constante ntregi .....................................................57 3.6.3. Constante n virgul mobil ....................................57 3.6.4. Constante ir............................................................58 3.6.5. Constanta zero .........................................................59 3.6.6. Obiecte constante ....................................................59 3.6.7. Enumerri ................................................................60
Capitolul IV OPERANZI I OPERATORI N C................................................ 61

4.1. Operanzi..........................................................................61 4.2. Operatori .........................................................................61 4.2.1. Operatori aritmetici .................................................62 4.2.2. Operatori de incrementare i decrementare.............63 4.2.3. Operatori rela ionali ................................................63 4.2.4. Operatori logici .......................................................64 4.2.5. Operatori logici la nivel de bit.................................65 4.2.6. Operatorul de atribuire ............................................70 4.2.7. Operatorul sizeof .....................................................70 4.2.8. Operatorul ternar ? .................................................71 4.2.9. Operatorul virgul ...................................................72 4.2.10. Operatorul de for are a tipului sau de conversie explicit (expresie cast) ........................................72 4.2.11. Operatorii parantez ..............................................73 4.2.12. Operatorul adres ..................................................73 4.2.13. Al i operatori ai limbajului C ................................74 4.2.14. Regula conversiilor implicite i preceden a operatorilor ...........................................................74 II

Capitolul V INSTRUC IUNI .............................................................................. 76

5.1. Instruc iuni etichetate (instruc iunea goto) .....................76 5.2. Instruc iuni expresie........................................................77 5.3. Instruc iuni compuse.......................................................77 5.4. Instruc iuni de selec ie ....................................................78 5.4.1. Instruc iunea if.........................................................78 5.4.2. Instruc iuni de selec ie multipl: if - else if.............79 5.4.3. Instruc iunea switch.................................................80 5.5. Instruc iuni repetitive......................................................82 5.5.1. Instruc iunea for ......................................................82 5.5.2. Instruc iunea while ..................................................86 5.5.3. Instruc iunea do-while.............................................87 5.5.4. Bucle ncuibate........................................................89 5.5.5. Instruc iunea break ..................................................91 5.5.6. Instruc iunea continue .............................................92
Capitolul VI TIPURI DE DATE STRUCTURATE ............................................ 93

6.1. Tablouri unidimensionale ...............................................93 6.1.1. Constante ir............................................................94 6.1.2. Ini ializarea vectorilor de caractere .........................95 6.1.3. Func ii pentru prelucrarea irurilor (fiierul antet string.h).................................................................97 6.2. Tablouri cu dou dimensiuni (matrice).........................100 6.2.1. Ini ializarea matricelor ..........................................100 6.2.2. Tablouri bidimensionale de iruri .........................101 6.3. Tablouri multidimensionale..........................................101 6.4. Structuri ........................................................................102 6.4.1. Tablouri de structuri ..............................................104 6.4.2. Introducerea structurilor n func ii ........................110 6.4.3. Tablouri i structuri n structuri.............................114 6.5. Uniuni ...........................................................................114 6.6. Enumerri .....................................................................116

III

Capitolul VII POINTERI ...................................................................................... 118

7.1. Operatori pointer...........................................................118 7.1.1. Importan a tipului de baz.....................................120 7.1.2. Expresii n care intervin pointeri...........................120 7.2. Pointeri i tablouri.........................................................125 7.2.1. Indexarea pointerilor .............................................126 7.2.2. Pointeri i iruri .....................................................128 7.2.3. Preluarea adresei unui element al unui tablou.......129 7.2.4. Tablouri de pointeri...............................................129 7.2.5. Pointeri la pointeri.................................................130 7.2.6. Ini ializarea pointerilor..........................................131 7.2.7. Alocarea dinamic a memoriei..............................132 7.2.8. Pointeri la structuri ................................................134 7.2.9. Structuri dinamice liniare de tip list ...................137
Capitolul VIII FUNC II ......................................................................................... 150

8.1. Forma general a unei func ii .......................................150 8.2. Rentoarcerea dintr-o func ie ........................................152 8.3. Valori returnate.............................................................153 8.4. Domeniul unei func ii ...................................................154 8.4.1. Variabile locale .....................................................155 8.4.2. Parametri formali...................................................156 8.4.3. Variabile globale ...................................................157 8.5. Apelul func iilor............................................................161 8.6. Apelul func iilor avnd ca argumente tablouri .............163 8.7. Argumentele argc i argv ale func iei main() ...............166 8.8. Func ii care returneaz valori nentregi ........................168 8.9. Returnarea pointerilor...................................................169 8.10. Func ii de tip void .......................................................172 8.11. Func ii prototip ...........................................................173 8.12. Func ii recursive .........................................................174 8.13. Clase de memorare (specificatori sau atribute)...........176 8.14. Pointeri la func ii ........................................................181 IV

Capitolul IX PREPROCESAREA ...................................................................... 183

9.1. Directive uzuale ............................................................183 9.2. Directive pentru compilare condi ionat ......................185 9.3. Modularizarea programelor .........................................189
Capitolul X INTRRI/IEIRI ........................................................................... 194

10.1. Func ii de intrare i ieire - stdio.h ...............................194 10.2. Opera ii cu fiiere .......................................................197 10.3. Nivelul inferior de prelucrare a fiierelor ...................199 10.3.1. Deschiderea unui fiier........................................200 10.3.2. Scrierea ntr-un fiier...........................................204 10.3.3. Citirea dintr-un fiier...........................................206 10.3.4. nchiderea unui fiier...........................................208 10.3.5. Pozi ionarea ntr-un fiier....................................208 10.3.6 tergerea unui fiier .............................................210 10.3.7. Exemple de utilizare a func iilor de intrare/ieire de nivel inferior .......................................................211 10.4. Nivelul superior de prelucrare a fiierelor ..................216 10.4.1. Func ia fopen() ....................................................216 10.4.2. Func ia fclose()....................................................218 10.4.3. Func iile rename() i remove()...........................219 10.4.4. Func ii de tratare a erorilor..................................219 10.4.5. Func ii cu acces direct .........................................220 10.4.6. Func ii pentru pozi ionare ...................................221 10.4.7. Ieiri cu format ....................................................223 10.4.8. Intrri cu format ..................................................226 10.4.9. Func ii de citire i scriere a caracterelor..............228
Capitolul XI UTILIZAREA ECRANULUI N MOD TEXT ........................... 233

11.1. Setarea ecranului n mod text .....................................233 11.2. Definirea unei ferestre ................................................234 11.3. tergerea unei ferestre ................................................234 11.4. Deplasarea cursorului .................................................235 V

11.5. Setarea culorilor..........................................................235 11.6. Func ii pentru gestiunea textelor ................................236


Capitolul XII UTILIZAREA ECRANULUI N MOD GRAFIC....................... 240

12.1. Ini ializarea modului grafic.........................................240 12.2. Gestiunea culorilor......................................................242 12.3. Setarea ecranului.........................................................244 12.4. Utilizarea textelor n mod grafic.................................244 12.5. Gestiunea imaginilor...................................................246 12.6. Desenarea i colorarea figurilor geometrice ...............247
Capitolul XIII FUNC II MATEMATICE............................................................ 253

13.1 Func ii trigonometrice .................................................253 13.2 Func ii trigonometrice inverse.....................................254 13.3 Func ii hiperbolice .......................................................254 13.4 Func ii exponen iale i logaritmice..............................254 13.5 Generarea de numere aleatoare....................................255 13.6 Alte tipuri de func ii matematice .................................256
Capitolul XIV ELEMENTE DE PROGRAMARE AVANSAT ....................... 257

14.1 Gestionarea memoriei ..................................................257 14.1.1 Memoria conven ional........................................257 14.1.2 Memoria expandat ..............................................260 14.1.3 Memoria extins...................................................260 14.1.4 Stiva......................................................................260 14.2 Servicii DOS i BIOS ..................................................261 14.2.1 Serviciile BIOS ....................................................262 14.2.2 Serviciile DOS......................................................266 14.3 Bibliotecile C ...............................................................270 14.3.1 Reutilizarea unui cod obiect.................................270 14.3.2 Lucrul cu fiiere bibliotec...................................270 14.3 Fiierele antet...............................................................271
BIBLIOGRAFIE ............................................................................ 272

VI

Capitolul I INTRODUCERE N ARHITECURA SISTEMELOR DE CALCUL

1.1. Importan a limbajului C


Creat n anul 1972 de programatorii de sistem Dennis M. Ritchie i Brian W. Kernighan de la Bell Laboratories cu scopul de a asigura implementarea portabil a sistemului de operare UNIX, C-ul este astzi unul din cele mai cunoscute i puternice limbaje de programare. Eficient, economic i portabil, C-ul este o alegere bun pentru realizarea oricrui tip de programe, de la editoare de texte, jocuri cu facilit i grafice, programe de gestiune i pentru calcule tiin ifice, pn la programe de sistem, constituind unul dintre cele mai puternice instrumente de programare. Adesea referit ca limbaj portabil, C-ul permite transferul programelor ntre calculatoare cu diferite procesoare i n acelai timp faciliteaz utilizarea caracteristicilor specifice ale mainilor particulare, programele scrise n C fiind considerate cele mai portabile. Dac evolu ia limbajelor de programare a adus n prim plan nume ca FORTRAN, LISP, COBOL, ALGOL-60 sau PASCAL, unele cu rspndire mai mult academic fiind folosite pentru a prezenta conceptele de baz sau conceptele avansate de programare ca de pild ALGOL-60 sau PASCAL, altele cu rspndire industrial masiv ca de pild FORTRAN i COBOL limbajul C a ptruns mai lent, dar foarte sigur. Realitatea arat clar c, n momentul de fa , pia a productorilor de programe este dominat net de C i de variantele evoluate ale acestuia. Elementele principale care au contribuit la succesul C-ului sunt urmtoarele: - modularizarea programelor ce d posibilitatea unui singur programator s stpneasc relativ uor programe de zeci de mii de linii de surs;

- capacitatea de programare att la nivel nalt ct i la nivel sczut ceea ce d posibilitatea utilizatorului de a programa fie fr a sim i sistemul de operare i maina de calcul, fie la un nivel apropiat de sistemul de operare ceea ce permite un control foarte bun al eficien ei programului din punct de vedere vitez/memorie; - portabilitatea programelor ce permite utilizarea programelor scrise n C pe o mare varietate de calculatoare i sisteme de operare; - facilit ile de reprezentare i prelucrare a datelor materializate printr-un numr mare de operatori i func ii de bibliotec ce fac programarea mult mai uoar. Prin anii 80 interesul pentru programarea orientat pe obiecte a crescut, ceea ce a condus la apari ia de limbaje care s permit utilizarea ei n scrierea programelor. Limbajul C a fost dezvoltat i el n aceast direc ie i n anul 1980 a fost dat publicit ii limbajul C++, elaborat de Bjarne Stroustrup de la AT&T. La ora actual, majoritatea limbajelor de programare moderne au fost dezvoltate n direc ia programrii orientate pe obiecte. Limbajul C++, ca i limbajul C, se bucur de o portabilitate mare i este implementat pe o gam larg de calculatoare ncepnd cu microcalculatoare i pn la cele mai mari supercalculatoare. Limbajul C++ a fost implementat pe microcalculatoarele compatibile IBM PC n mai multe variante. Cele mai importante implementri ale limbajelor C++ pe aceste calculatoare sunt cele realizate de firmele Microsoft i Borland. Conceptele programrii orientate pe obiecte au influen at n mare msur dezvoltarea limbajelor de programare n ultimul deceniu. De obicei, multe limbaje au fost extinse astfel nct ele s admit conceptele mai importante ale programrii orientate pe obiecte. Uneori s-au fcut mai multe extensii ale aceluiai limbaj. De exemplu, limbajul C++ are ca extensii limbajul E ce permite crearea i gestiunea obiectelor persistente, lucru deosebit de important pentru sistemele de gestiune a bazelor de date, limbajul O ce ncearc s mbine facilit ile de nivel nalt cu cele ale programrii de sistem, limbajul Avalon/C++ destinat calculului distribuit, i nu n ultimul rnd binecunoscutul de acum limbaj Java, specializat n aplica ii Internet. Interfe ele utilizator au atins o mare dezvoltare datorit facilit ilor oferite de componentele hardware ale diferitelor calculatoare. n principiu, ele simplific interac iunea dintre programe

i utilizatorii acestora. Astfel, diferite comenzi, date de intrare sau rezultate pot fi exprimate simplu i natural utiliznd diferite standarde care con in ferestre, bare de meniuri, cutii de dialoguri, butoane, etc. Cu ajutorul mouse-ului toate acestea pot fi accesate extrem de rapid i uor fr a mai fi nevoie s cunoti i s memorezi o serie ntreag comenzi ale sistemului de operare sau ale limbajului de programare. Toate acestea conduc la interfe e simple i vizuale, accesibile unui segment foarte larg de utilizatori. Implementarea interfe elor este mult simplificat prin utilizarea limbajelor orientate spre obiecte, aceasta mai ales datorit posibilit ii de a utiliza componente standardizate aflate n biblioteci specifice. Importan a aplicrii conceptului de reutilizare a codului rezult din faptul c interfe ele utilizator adesea ocup 40% din codul total al aplica iei. Firma Borland comercializeaz o bibliotec de componente standardizate care pot fi utilizate folosind limbajul C++, bibliotec cunoscut sub numele Turbo Vision. De obicei, interfe ele utilizator gestioneaz ecranul n mod grafic. O astfel de interfa utilizator se numete interfa utilizator grafic. Una din cele mai populare interfe e utilizator grafice pentru calculatoarele IBM PC este produsul Windows oferit de firma Microsoft. Windows este un mediu de programare ce amplific facilit ile oferite de sistemul de operare MS-DOS. Aplica iile Windows se pot dezvolta folosind diferite medii de dezvoltare ca: Turbo C++ pentru Windows, Pascal pentru Windows, Microsoft C++, Microsoft Visual Basic, Visual C i Visual C++. Componentele Visual permit specificarea n mod grafic a interfe ei utilizator, a unei aplica ii, folosind mouse-ul, iar aplica ia propriu-zis se programeaz ntr-un limbaj de tip Basic, C sau C++. Dac n ani 70 se considera c o persoan este rezonabil s se poat ocupa de o aplica ie de 4-5 mii de instruc iuni, n prezent, n condi iile folosirii limbajelor de programare orientate pe obiecte, aceast medie a ajuns la peste 25 de mii de instruc iuni. Un limbaj de programare trebuie privit nu doar la suprafa a sa sintax i mod de butonare a calculatorului pentru o implementare particular ci mai ales n profunzime, prin conceptele pe care se bazeaz, prin stilul de programare, prin modul de structurare a aplica iei i, implicit, a programului, prin filozofia de rezolvare a

problemelor folosind limbajul. Din aceste puncte de vedere, C-ul nu poate lipsi din cultura unui programator, iar pentru un profesionist Cul este, i mai mult, o necesitate vital, acesta fiind piatra de temelie pentru n elegerea i utilizarea eficient a limbajelor de nivel nalt orientate pe obiecte i Visual.

1.2 Arhitectura de baz a unui calculator


Calculatoarele de tip PC (calculatoare personale) reprezint cele mai rspndite i mai utilizate dintre calculatoare, datorit gradului de accesibilitate i pre ului relativ sczut. Indiferent de tipul calculatorului, modul general de concep ie, de alctuire i func ionare este acelai. Calculatorul este o main programabil. Dou dintre principalele caracteristici ale unui calculator sunt: 1. Rspunde la un set specific de instruc iuni ntr-o manier bine definit. 2. Calculatorul poate executa o list prenregistrat de instruc iuni, numit program. Calculatoarele moderne sunt electronice i numerice. Partea de circuite electrice i electronice precum i conexiunile fizice dintre ele se numete hardware. Totalitatea programelor precum i datele aferente acestor programe poart denumirea de software.
Echipamente de ieire

Echipamente de stocare date (HDD, FDD, CD-ROM, etc.)

UPC Unitatea de procesare i control

Echipamente de intrare

Fig.1.1 Configura ia standard pentru utilizator

Partea hardware a unui calculator este format din totalitatea componentelor sale fizice. Toate calculatoarele de uz general necesit urmtoarele componente hardware: memorie: Permite calculatorului s stocheze, cel pu in temporar, date i programe. dispozitive de stocare externe: Permit calculatoarelor s stocheze permanent programe i mari cantit i de date. Cele mai uzuale dispozitive de stocare extern sunt HDD (hard disk drives), FDD (floppy disk drive) i CD-ROM (Compact Disk-Read Only Memory) sau CD-R/W (Compact Disk-Read/Write). dispozitive de intrare : n mod uzual sunt reprezentate de tastatur (keyboard) i de mouse. Aceste dispozitive reprezint calea uzual de introducere a datelor i instruc iunilor care gestioneaz func ionarea unui calculator. dispozitive de ieire: Reprezint modalitatea prin care calculatorul transmite utilizatorului uman rezultatele execu iei programelor. Ecranul monitorului sau imprimanta sunt astfel de dispozitive uzuale. unitatea de procesare i control (UPC) : Este partea principal a unui calculator deoarece este componenta care execut instruc iunile. n mod uzual aceast unitate de procesare i control este reprezentat de un microprocesor care se plaseaz pe placa de baz (mainboard) a calculatorului mpreun cu memoria intern RAM. n plus fa de aceste componente orice calculator este prevzut cu o magistral (bus) prin care se gestioneaz modalitatea de transmitere a datelor ntre componentele de baz ale calculatorului. Magistrala reprezint o colec ie de trasee electrice care leag microprocesorul de dispozitivele de intrare/ieire i de dispozitivele interne/externe de stocare a datelor. Putem distinge magistrala de date, magistrala de adrese i magistrala de comand i control. n figura 1.2 este prezentat interac iunea dintre componentele HARD principale ale unui calculator. Calculatoarele pot fi n general clasificate dup dimensiuni sau putere de calcul. Nu se poate face ns la ora actual o distinc ie net ntre urmtoarele categorii de calculatoare:

PC (Personal Computer): Un calculator de dimensiuni mici, monoutilizator (single-user), bazat pe un microprocesor. n plus 5

acesta este dotat standard cu tastatur, mouse, monitor i dispozitive periferice de stocare a datelor.
Memoria secundar Echipament de intrare UNITATEA CENTRAL Echipament de ieire

Memoria principal Fig. 1.2 Arhitectura minimal a unui sistem de calcul

sta ii de lucru (workstation): Un calculator monoutilizator de mare putere. Aceasta este asemntor unui PC dar are un microprocesor mai puternic i un monitor de nalt calitate (rezolu ie mai mare). minicalculator (minicomputer): Un calculator multiutilizator (multi-user) capabil s lucreze simultan cu zeci sau chiar sute de utilizatori. mainframe: Un calculator multiutilizator capabil s lucreze simultan cu sute sau chiar mii de utilizatori. supercomputer: Un computer extrem de rapid care poate executa sute de milioane de opera ii ntr-o secund.

1.2.1 Microprocesorul
Microprocesorul este cea mai important i cea mai scump component a unui calculator de performan ele acesteia depinznd n mare msur rezultatele ntregului sistem. Din punct de vedere fizic, microprocesorul este un cip ce con ine un circuit integrat complex ce i permite s prelucreze informa ii prin executarea unor opera ii logice i matematice diverse (adunri, scderi, nmul iri, mpr iri, comparri de numere). El este compus din dou pr i importante: unitatea de execu ie (EU Execution Unit) i unitatea de interfa a magistralei de date (BIU Bus Interface Unit). Prima component realizeaz efectiv

opera iile, iar cea de-a doua are func ia de transfer a datelor de la i nspre microprocesor. Microprocesorul reprezint de fapt unitatea central a unui calculator i ndeplinete o serie de activit i specifice cum ar fi: execut opera ii aritmetice i logice, decodific instruc iuni speciale, transmite altor cipuri din sistem semnale de control. Toate aceste opera ii sunt executate cu ajutorul unor zone de memorie ale microprocesorului, numite registre. Orice microprocesor are un set finit de instruc iuni pe care le recunoate i pe care le poate executa. Calculatoarele IBM PC folosesc procesoare INTEL sau compatibile, realizate de alte companii cum ar fi: AMD, NexGen, CYRIX. Numele microprocesorului este folosit la identificarea calculatorului. Se folosesc frecvent expresii de tipul calculator 386, calculator 486, calculator Pentium II, etc. n 1971 firma Intel a fost abordata de o companie Japoneza, acum disprut, pentru a construi un circuit dedicat pentru un nou calculator. Designerul Ted Hoff a propus o solu ie programabil, de uz general, i astfel s-a nscut circuitul Intel 4004. Au urmat la scurt timp chipurile 4040 si 8008 dar lor le lipseau multe din caracteristicile microprocesoarelor aa cum le tim noi azi. n 1974 Intel a prezentat pentru prima oar circuitul Intel 8080 care a fost folosit in sistemele Altair i IMSAI. Curnd dup aceea au aprut procesoarele Motorola 6800 i 6502 de la MOS Technology. Doi dintre proiectan ii de la Intel au prsit firma, crend corpora ia ZILOG care a produs chipul Z80 (compatibil cu 8080 dar cu set de instruc iuni mai puternic i de dou ori mai rapid. Cipul Intel 4004 a fost primul procesor comercial, lansat la sfritul anului 1971. La un pre de circa 200$ i nglobnd 2300 de tranzistori, cipul 4004 dezvolta mai mult putere de calcul dect ENIAC, primul calculator electronic, cu 25 de ani n urma. Fa de cele 18.000 de tuburi cu vacuum ce ocupau 900 metri cubi, procesorul 4004 putea dezvolta 60.000 de opera ii pe secund. Aceast inven ie a contribuit la revolu ionarea domeniilor de aplica ii ale computerelor, dnd startul unui adevrat galop de inova ii tehnologice. Urmtorul pas a fost n 1980, cnd IBM a inclus un procesor Intel n arhitectura primului PC. Astzi PC-urile sunt pretutindeni n jurul nostru. Un copil care lucreaz la o maina ce incorporeaz un procesor Pentium Pro beneficiaz de mult mai mult putere de calcul dect dispunea

guvernul SUA n perioada lansrii primelor echipaje umane ctre Lun. ntr-un numr aniversar al publica iei Communications of the ACM, Gordon Moore, co-fondator al companiei Intel, era optimist n privin a evolu iei PC-urilor i a microprocesoarelor: "complexitatea microprocesoarelor, care se msoar prin numrul de tranzistori pe cip, s-a dublat aproape constant la fiecare 18 luni, de la apari ia primului prototip 4004. Aceasta evolu ie exponen ial a determinat o continu cretere a performan elor PC-urilor i o scdere a costului procesului de calcul. Pe cnd n 1991 un PC bazat pe procesorul Intel 486 costa aproape 225$ pentru o performan de un milion de instruc iuni pe secund (MIPS), astzi, un sistem desktop ce utilizeaz un cip Pentium Pro este evaluat la circa 7$ pe MIPS. Nu se ntrevede nici o dificultate care s frneze aceast rat de dezvoltare".

1.2.2 Memoria
Microprocesorul are capacitatea de a memora date care urmeaz a fi prelucrate, ct i rezultatele intermediare. Se observ c rolul su principal este de a prelucra i transmite informa iile i rezultatele i deci capacitatea sa de memorare este mic neputnd stoca programe. De aceea, un calculator necesit i o memorie care s gzduiasc date i programe. Memoria este format din punct de vedere fizic din cipuri ce stocheaz informa ia sub forma a dou niveluri de tensiune ce corespund valorilor 0 i 1 din sistemul de numera ie. Celulele de baz ale memoriei (ce pot avea valoarea 0 sau 1) se numesc bi i i ele reprezint particulele cele mai mici de informa ie din calculator. Pentru citirea informa iilor nu se folosesc bi i n mod individual ci acetia sunt grupa i ntr-o succesiune. Astfel o succesiune de 8 bi i formeaz un octet (sau un byte) aceasta reprezentnd unitatea de msur a capacit ii de memorie. Deoarece reprezentarea numerelor n calculator se face n baza 2 i nu n baza 10, aa cum suntem obinui i n mod normal s lucrm, i multiplii unui byte vor fi puteri ale lui 2, astfel: 1 KB=210B=1024 B 1 MB=210KB=1 048 576 B 1 GB=210MB=230 B Abrevierile K (kilo), M (mega), G (giga) se scriu de obicei cu litere mari i reprezint mii, milioane i, respectiv, miliarde.

Memoria unui calculator are dou componente: memoria principal (intern) i memoria secundar (extern). Memoria intern este memoria ce poate fi accesat n mod direct de ctre microprocesor i n care sunt ncrcate programele nainte de a fi executate de ctre microprocesor. Dac primele procesoare puteau accesa doar 1 MB de memorie astzi un procesor Pentium poate accesa peste 256 MB. Memoria principal este format din dou tipuri de circuite: cip-uri ROM i cip-uri RAM. Circuitele de tip ROM (Read Only Memory) au memorate programele care controleaz ini ial calculatorul (sistemul de operare). Aceste memorii pot fi doar citite (con inutul lor nu poate fi modificat). nscrierea con inutului acestor memorii se face de ctre fabricant, iar opera iunea de nscriere cu programe se mai numete arderea memoriilor. Circuitele de tip RAM (Random Acces Memory ) sunt memorii la care utilizatorul are acces i al cror con inut se terge la deconectarea calculatorului. n memoria RAM informa ia este stocat temporar. De exemplu, programele de aplica ii curente i datele asociate acestor aplica ii sunt ncrcate n memoria RAM nainte de a fi prelucrate de ctre microprocesor. Deoarece capacitatea de memorie a unui calculator nu poate fi att de mare nct s poat pstra toate programele pe vrem s le executm, a aprut necesitatea existen ei unor memorii externe, care s fie solicitate la nevoie. Rolul acestora l joac discurile i ele pot fi asemnate cu cr ile dintr-o bibliotec pe care le putem consulta ori de cte ori avem nevoie de anumite informa ii. Primele discuri aprute pentru PC-uri, numite i dischete, floppy disk-uri sau discuri flexibile, permiteau stocarea a maximum 160 KB de informa ie. Astzi mai exist doar dischete cu diametrul de 3.5 inch cu capacitatea de 1,44 MB. Existen a acestora pare a fi pus ns n pericol de apari ia CD-urilor reinscriptibile a cror capacitate de memorare depete 700 MB iar evolu ia tehnologic, din ce n ce mai rapid, d semne c lucrurile nu se vor opri aici. Dischetele folosesc metode magnetice de memorare a informa iei motiv pentru care ele se mai numesc i suporturi magnetice de informa ie. Principala component a unui calculator utilizat pentru memorarea programelor o reprezint hard discul. Acesta poate fi asemnat cu o dischet de mare capacitate, integrat ntr-o unitate

ncapsulat. Ini ial, pu ine PC-uri prezentau hard discuri, dar cum pre urile acestora au sczut considerabil, iar performan ele i capacit ile au crescut, n prezent toate calculatoarele prezint acest dispozitiv. n clipa de fa , capacitatea de memorare a unui hard disc a depit valoarea de 40 de GB.

1.2.3 Echipamentele periferice


Comunicarea om-main se realizeaz cu ajutorul echipamentelor periferice prin intermediul crora utilizatorul poate programa sau da anumite comenzi calculatorului sau poate vizualiza rezultatele ob inute de ctre anumite programe. Principalele echipamente periferice ale unui calculator sunt urmtoarele: tastatura, mouse-ul, scanner-ul, monitorul i imprimanta. Ele pot fi grupate n echipamente de intrare cele prin care calculatorul primete informa ii sau comenzi (tastatur, mouse, scanner) - i echipamente de ieire cele prin care calculatorul transmite informa ii n exterior (monitor, imprimant). n continuare sunt prezentate cteva caracteristici ale fiecrui echipament. Tastatura este principalul dispozitiv de intrare al calculatorului prin intermediul cruia se transmit comenzi ctre unitatea central. Cuplarea la calculator a tastaturii se face prin intermediul unui cablu de conectare. Din punct de vedere al dispunerii tastelor, tastatura se aseamn destul de mult cu cea a unei maini de scris dar are i pr i care o individualizeaz. Primele tastaturi au avut 83/84 de taste, pentru ca, ulterior, ele s fie mbog ite prin dublarea tastelor existente sau adugarea altora noi, ajungndu-se la 101/102 taste. Din punct de vedere al func ionalit ii lor ele pot fi mpr ite n patru categorii: - taste alfanumerice; - taste cu scopuri speciale; - taste direc ionale i numerice; - taste func ionale. Tastele alfanumerice con in literele, cifrele i semnele de punctua ie i ocup partea central a tastaturii. Ac ionarea unei astfel de taste determin apari ia caracterului corespunztor pe ecranul calculatorului. Tastele cu scopuri speciale sunt aezate n acelai bloc cu tastele alfanumerice i determin efectuarea anumitor ac iuni fr

10

nscrierea de caractere pe ecran. Tastele de micare se afl situate n partea dreapt a tastaturii i ele func ioneaz n dou moduri, care pot fi comutate prin ac ionarea altei taste, aflate deasupra lor, pe care scrie NumLock. Dac ledul corespunztor acestei taste este aprins, modul de lucru este numeric, n caz contrar fiind comutat pe semnifica ia direc ional. Tastele func ionale sunt un grup de 12 taste situate n partea de sus a tastaturii avnd pe ele litera F urmat de un numr ntre 1 i 12. Ac ionarea acestor taste determin efectuarea unor opera ii specifice de la program la program. Mouse-ul este tot un echipament de intrare mai uor de manevrat dect tastatura dar care poate efectua mai pu ine opera ii. Totui, foarte multe aplica ii (n special aplica iile grafice) nu mai pot fi concepute fr mouse. Un mouse are aspectul unei buc i de spun, uor manevrabil, avnd dedesubt o bil pozi ionabil, cu sensibilitate i vitez reglabile. Micarea maouse-ului pe o suprafa plan este corelat cu deplasarea pe ecran a unui cursor cu o form deosebit: cruciuli , sgeat, etc. Declanarea unei ac iuni se face prin pozi ionarea cursorului n zona corespunztoare i apsarea unuia dintre butoanele aflate pe partea posterioar. Ini ial un mouse avea dou sau trei butoane. Acum exist mouse-uri cu 5 butoane i 2 roti e ce ndeplinesc o serie de func ii corespunztoare unor taste speciale. Folosirea mouse-ului uureaz mult munca utilizatorilor, nemaifiind necesar ca acetia s memoreze numrul relativ mare de comenzi corespunztor fiecrui produs, ca n situa ia n care se folosete numai tastatura. Utilitatea mouse-ului este i mai evident n cazul aplica iilor grafice. De altfel, WINDOWS este un sistem de operare creat special pentru lucrul cu mouse-ul. Scanner-ul reprezint dispozitive care se cupleaz la un PC i cu care, prin intermediul unui software adecvat, se pot capta imagini, fotografii, texte etc., n vederea unei prelucrri ulterioare. Astfel se pot manevra imagini foto, se pot crea efecte grafice speciale, care nu se pot ob ine prin metode tradi ionale. Dup captare, imaginea poate fi prelucrat, mutat, mrit, micorat, rotit, colorat, umbrit, suprapus cu alt imagine etc. Cu un software de recunoatere optic a caracterelor datele sau documentele tiprite pe coli de hrtie pot fi transformate n fiiere, putndu-se realiza chiar o stocare a lor sub form de arhiv.

11

Monitorul - Sistemul video este format din dou pr i: un adaptor (plac) video i un monitor sau display. Adaptorul video reprezint dispozitivul care realizeaz legtura (interfa a) cu calculatorul i se afl n interiorul acestuia. El va fi corespunztor tipului de monitor video care i este ataat. Adaptorul video realizeaz o rezolu ie orizontal i una vertical. Rezolu ia reprezint numrul de elemente, n cazul de fa puncte pixeli care pot fi afiate pe ecran. De exemplu, un monitor VGA, n mod video, are o rezolu ie de 640 x 480 pixeli. Standardul VGA (Video Graphics Array) a fost introdus de ctre IBM o dat cu calculatoarele PS/2, iar modurile video VGA reprezint un superset al standardelor video anterioare, CGA (Color Graphics Adapter) i EGA (Enhanced Graphics Adapter). Cea mai important mbunt ire adus de standardul VGA a fost rezolu ia superioar a caracterelor n modul text, precum i posibilitatea de a afia 256 de culori la un moment dat. Monitorul, denumit uneori i display, permite vizualizarea datelor introduse de la tastatur sau rezultate n urma execu iei unor comenzi sau programe, fiind ncadrat n categoria echipamentelor periferice de ieire. Ca pies principal, monitorul con ine un tub de vacuum, similar cu cel de la televizor i trei tunuri de electroni (corespunztoare celor trei culori fundamentale). Standardele video MDA, CGA i EGA folosesc monitoare digitale. Datele care descriu culorile pixelilor sunt trimise de adaptorul video la monitor sub forma unor serii de semnale digitale care sunt echivalente unor serii de bi i. Standardul VGA a introdus un nou tip de monitor care utilizeaz semnale analogice pentru transferul informa iilor privind culoarea de la adaptorul video la monitor. Dac semnalele digitale prezint niveluri care indic prezen a sau absen a unui bit, semnalele analogice pot prezenta orice valoare ntre una minim i una maxim. Imprimanta - Reprezint un dispozitiv care poate fi ataat unui calculator, cu scopul tipririi de texte i grafic, putnd fi considerat un fel de main de scris automat. Pn n prezent au fost realizate un numr destul de mare de tipuri de imprimante pentru PC-uri, ele diferind att prin performan e, ct i prin modalit ile tehnice de ralizare. Fiecare dintre ele prezint avantaje i dezavantaje, ideal fiind a o folosi pe cea care corespunde cel mai bine tipului de lucrri

12

executate. n func ie de modul n care este realizat imprimarea se disting urmtoarele tipuri de imprimante: - imprimante matriceale cu 9, 18 sau 24 de ace realizeaz imprimarea prin impactul acelor peste o band de hrtie; - imprimante cu jet de cerneal func ioneaz prin pulverizarea fin a unor picturi de cerneal pe hrtia de imprimat; - imprimante laser ce utilizeaz o raz laser sau mici diode luminiscente care ncarc electrostatic un tambur de imprimare, corespunztor caracterului care urmeaz a fi imprimat; - imprimante rapide de linii ce imprim mai multe linii odat, fiind folosite mai mult la sisteme de calcul de dimensiuni mari. Calitatea imprimrii crete de la primul la ultimul tip prezentat, dar n mod corespunztor i pre ul echipamentului.

1.3. Programarea calculatorului


Programele de calculator, cunoscute sub numele de software, sunt constituite dintr-o serie de instruc iuni pe care le execut calculatorul. Cnd se creeaz un program, trebuie specificate instruc iunile pe care calculatorul trebuie s le execute pentru a realiza opera iile dorite. Procesul de definire a instruc iunilor pe care le execut calculatorul se numete programare. Programele executate pe un calculator pot fi mpr ite n trei categorii: programe de aplica ie sunt acele programe care interac ioneaz direct cu utilizatorul, specializate n realizarea unei categorii de prelucrri. Editoarele de texte, programele pentru gestiunea bazelor de date, programele de tehnoredactare asistat de calculator, de grafic etc. sunt programe de aplica ie. utilitare programe, care la fel ca programele de aplica ie, interac ioneaz direct cu utilizatorul, dar, spre deosebire de acestea, realizeaz prelucrri de uz general. Utilitarele realizeaz o serie de opera ii de gospodrie cum ar fi: copierea fiierelor, pregtirea discurilor magnetice pentru utilizare, crearea de copii de salvare, testarea echipamentului, etc. programe de sistem realizeaz legtura ntre componentele electronice ale calculatorului i programele de aplica ie i utilitare. Rolul programului de sistem este acela de a uura sarcina programatorului, simplificnd ndeplinirea acelor sarcini care sunt

13

comune marii majorit i a programelor de aplica ie: alocarea memoriei, afiarea caracterelor pe ecran i la imprimant, citirea caracterelor de la tastatur, accesul la informa iile stocate pe disc magnetic, etc.

1.3.1. Sistemul de operare


Sistemul de operare este o parte component a software-ului unui calculator, care mai cuprinde un numr variabil de programe utilitare selectate conform cu necesit ile programatorilor. Sistemul de operare este un program cu func ii de coordonare i control asupra resurselor fizice ale calculatorului i care intermediaz dialogul om-calculator. Sistemul de operare permite rularea programelor i pstrarea informa iilor pe disc. n plus, fiecare sistem de operare pune la dispozi ia aplica iilor o serie de servicii care permit programelor s aloce memorie, s acceseze diferite echipamente periferice, cum ar fi imprimanta, i s gestioneze alte resurse ale calculatorului. Un sistem de operare trebuie s aib capacitatea de a se adapta rapid la modificrile tehnologice, rmnnd n acelai timp compatibil cu hardware-ul anterior. Lan ul de comunicare utilizator calculator este prezentat n Figura 1.3: Sistemul de operare este cel mai important program care ruleaz pe un calculator. Orice calculator de uz general este dotat cu un sistem de operare care permite execu ia altor programe. Sistemele de operare execut opera iuni de baz precum: recunoaterea unei intrri de la tastatur (preluare caracter), trimiterea unui caracter pentru afiare pe ecranul monitorului, gestionarea fiierelor i a directoarelor pe disc (floppy-disk sau hard-disk), controlul fluxului de date cu echipamentele periferice ca drivere de disc sau imprimante.

C A LC U LA T O R S IS T E M D E O PER AR E A P LIC A II U T ILIZ A T O R

Fig. 1.3. Comunicarea utilizator - calculator

14

Aplica ie

Disk-drive Sistem de operare Mouse Monitor

Tastatur

Imprimant

Fig. 1.4 Rolul sistemului de operare

Sistemul de operare al unui calculator este partea de software necesar i suficient pentru execu ia oricror alte aplica ii dorite de utilizator. Un calculator nu poate func iona dect sub gestiunea unui sistem de operare. Orice aplica ie lansat n execu ie de ctre un utilizator apeleaz la resursele puse la dispozi ie de ctre sistemul de operare. Sistemul de operare interfa eaz calculatorul cu operatorul uman de o manier ct mai transparent cu putin astfel nct utilizatorul nu trebuie s fac eforturi mari de adaptare dac lucreaz cu arhitecturi hardware diferite. Pentru sisteme mai mari, sistemele de operare au responsabilit i i capabilit i i mai mari. Ele ac ioneaz ca un gestionar al traficului de date i al execu iei programelor. n principal sistemul de operare asigur ca diferite programe i diferi i utilizatori s nu interfereze unele cu altele. Sistemul de operare este de asemenea responsabil cu securitatea, asigurnd inaccesibilitatea persoanelor neautorizate la resursele sistemului. Sistemele de operare se pot clasifica dup cum urmeaz: multi-user: Permit ca doi sau mai mul i utilizatori s ruleze n acelai timp programe (utilizatori concuren i). Anumite sisteme de operare permit sute sau chiar mii de utilizatori concuren i. multiprocesor: Permit execu ia unui program pe mai mult de un microprocesor. multitasking: Permit mai multor programe s ruleze n acelai timp (execu ie concurent).

15

multithreading: Permit diferitelor pr i ale unui program s fie executate concurent. timp real (real time): Rspund instantaneu la diferite intrri. Sistemele de operare de uz general, ca DOS sau UNIX nu sunt sisteme de operare de timp real. Sistemele de operare furnizeaz o platform software pe baza creia alte programe, numite programe de aplica ie, pot rula (pot fi executate). Programele de aplica ie trebuie s fie scrise pentru a rula pe baza unui anumit sistem de operare. Alegerea unui anumit sistem de operare determin n consecin mul imea aplica iilor care pot fi rulate pe calculatorul respectiv. Pentru PC-uri, cele mai populare sisteme de operare sunt DOS, OS/2 sau Windows, dar mai sunt disponibile i altele precum Linux. Ca utilizator se interac ioneaz cu sistemul de operare prin intermediul unor comenzi. Spre exemplu, sistemul de operare DOS accept comenzi precum COPY sau RENAME pentru a copia fiiere sau pentru a le redenumi. Aceste comenzi sunt acceptate i executate de o parte a sistemului de operare numit procesor de comenzi sau interpretor de linie de comand. Interfa ele grafice cu utilizatorul (GUI, Graphical user interfaces) permit introducerea unor comenzi prin selectarea i ac ionarea cu mouse-ul a unor obiecte grafice care apar pe ecran. Spre exemplu, sistemul de operare Windows are un desktop ca intefa garfic cu utilizatorul. Pe acest desktop (birou) se afl diferite simboluri grafice (icoane, icons) ataate diferitelor aplica ii disponibile pe calculatorul respectiv. Utilizatorul are multiple posibilit i de configurare a acestei intefe e grafice. Primul sistem de operare creat pentru calculatoare a fost CP/M (Control Program for Microcomputers), realizat pentru calculatoarele pe 8 bi i. O dat cu perfec ionarea componentelor HARD s-a impus i necesitatea dezvoltrii unui SOFT adecvat. Astfel, n 1981, a aprut prima versiune a sistemului de operare MS-DOS. Sistemul de operare MSDOS (MicroSoft Disk Operating System) este destinat gestionrii resurselor software si hardware ale microcalculatoarelor cu o arhitectura de tip IBM PC sau compatibil cu aceasta i echipate cu procesoare 8086 sau 80x86, Pentium. Odat cu creterea capabilit ilor hardware ale calculatoarelor, acesta s-a transformat, prin dezvoltri succesive, n Windows.

16

Indiferent de sistemul de operare utilizat, din punctul de vedere al utilizatorului, informa iile sunt scrise pe disc sub forma unor fiiere. Un fiier este o colec ie de informa ii grupate sub acelai nume. Un fiier poate fi un program executabil, un text, o imagine, un grup de comenzi sau orice altceva. Un fiier este identificat prin numele su. Numele unui fiier este format dintr-un ir de caractere (care n func ie de sistemul de operare este limitat la un anumit numr maxim de caractere), urmate eventual de semnul punct (.) i de nc maximum 4 caractere, numite extensie, ca de exemplu: nume.ext. Pentru a putea avea acces rapid la fiiere, sistemul de operare creeaz nite fiiere speciale, numite directoare, care pot fi asemnate cu cuprinsul unei cr i, deoarece ele con in numele fiierelor i adresa de nceput a acestora. De asemenea, un director poate con ine la rndul su alte directoare crendu-se astfel o structur arborescent de directoare n care poate fi gsit foarte repede un anumit fiier.

1.3.2. Tipuri de fiiere


Fiierele se pot mpr i n dou categorii executabile i neexecutabile. n prima categorie intr acele fiiere al cror nume scris n dreptul prompterului (n cazul sistemului de operare DOS) determin executarea unor activit i de ctre sistemul de operare. O parte dintre fiierele executabile sunt programe i sunt recunoscute prin extensia lor care poate fi EXE sau COM, altele fiind constituite n fiiere de comenzi proprii sistemului de operare, a cror extensie este BAT. Fiierele COM, numite adesea i comenzi, con in informa ii n formatul imagine de memorie. Ele sunt mai compacte i mai rapide dect fiierele EXE, dar lungimea lor nu poate s depeasc 64 K. Fiierele EXE pot s ajung la dimensiuni mai mari prin segmentarea programului n fragmente a cror dimensiune s fie de maximum 64K. Dintre fiierele neexecutabile vom aminti cteva mai importante: fiiere text ; fiiere cu extensia SYS sau DRV, cunoscute sub numele de driver-e i care con in instruc iuni despre modul n care sistemul de operare trebuie s controleze diferite componente hardware; surse de programe scrise n diferite limbaje (cu extensiile PAS limbajul Pascal, C limbajul C, CPP limbajul C++, etc.);

17

fiiere care con in informa ii intermediare ntre cele n limbaj surs i cele executabile (extensiile OBJ, OVL); fiiere ce con in imagini (extensiile JPEG, GIF, BMP); fiiere ce con in sunete (extensiile WAV, MIDI, MP3) etc.

1.3.3. Construirea fiierului executabil


Instruc iunile pe care le execut un calculator sunt de fapt grupuri de 1 0 (cifre binare) care reprezint semnale electronice produse n interiorul calculatorului. Pentru a programa primele calculatoare (n anii 1940-1950), programatorii trebuiau s n eleag modul n care calculatorul interpreta diferitele combina ii de 0 i 1, deoarece programatorii scriau toate programele folosind cifre binare. Cum programele deveneau din ce n ce mai mari, acest mod de lucru a devenit foarte incomod pentru programatori. De aceea au fost create limbaje de programare care permit exprimarea instruc iunilor calculatorului ntr-o form mai accesibil programatorului. Dup ce programatorul scrie instruc iunile ntr-un fiier - numit fiier surs, un al doilea program numit compilator, convertete instruc iunile limbajului de programare n irurile 1 i 0 cunoscute sub numele de cod main. Pentru a ob ine un program executabil, orice program surs trebuie eventual translatat (tradus) n limbaj cod main sau cod obiect pe care l poate n elege microprocesorul. n urma acestui proces, alturi de fiierul surs apare i fiierul cod obiect (object file.) Aceast translatare sau traducere este efectuat de ctre compilatoare, interpretoare sau asambloare. Compilatorul este folosit pentru transformarea codului surs, adic a programului scris ntr-un limbaj de programare de nivel nalt, n cod obiect (object code). Acest cod obiect va fi transformat n faza de editare de legturi n cod main executabil de microprocesorul sistemului de calcul. Programatorii scriu programe ntr-o form numit cod surs. Acest cod surs parcurge apoi c iva pai nainte de a deveni program executabil. Pe scurt, un compilator este un program special care proceseaz instruc iuni scrise ntr-un limbaj de programare particular i le transform n limbaj main sau cod main pe care l poate executa microprocesorul.

18

La ora actual un limbaj de programare este inclus ntr-un mediu de programare mai complex care include un editor de texte pentru introducerea instruc iunilor n limbajul de programare de nivel nalt, un compilator i un editor de legturi folosite pentru translatarea codului surs n cod main. n mod tipic, un programator scrie declara ii ntr-un limbaj precum Pascal, C sau MATLAB folosind un editor. Se creeaz astfel un fiier numit fiier cod surs ce con ine o colec ie de instruc iuni i declara ii scrise n limbajul respectiv. Primul pas este prelucrarea codului surs de ctre compilator, care translateaz instruc iunile de nivel nalt ntr-o serie de instruc iuni cod obiect. Cnd este lansat n execu ie compilatorul acesta, ntr-o prim etap, lanseaz un analizor sintactic, gramatical, numit parser. Acesta parcurge i analizeaz sintactic, secven ial, n ordinea n care au fost introduse, toate instruc iunile scrise n limbajul de nivel nalt. O instruc iune de nivel nalt se translateaz ntr-una sau mai multe instruc iuni specifice microprocesorului pentru care a fost conceput compilatorul. Aceste instruc iuni ale microprocesorului sunt nlocuite cu codurile lor binare, fiecare instruc iune a microprocesorului fiind codificat de ctre constructor. Codurile binare ale instruc iunilor microprocesorului mpreun cu reprezentrile interne ale datelor manipulate formeaz codul obiect. Deci n unul sau mai multe faze (parserul este una dintre faze) din codul surs de intrare se produce un cod de ieire, numit n mod tradi ional cod obiect. Este foarte important ca referiri la alte module de cod s fie corect reprezentate n acest cod obiect. Pasul final n producerea programului executabil, dup ce compilatorul a produs codul obiect, este prelucrarea codului obiect de ctre un editor de legturi (link-editor sau linker). Acest linker combin diferitele module (le leag) i d valori reale, efective, tuturor adreselor simbolice existente n codul obiect. n urma acestei prelucrri se ob ine codul main, salvat ntr-un fiier cu extensia .exe. Acest cod main poate fi executat secven ial, instruc iune cu instruc iune, de ctre microprocesor. Cu alte cuvinte, un program executabil (executable program aflat pe disc cu extensia .exe) se ob ine prin salvarea pe disc a codului main ob inut prin prelucrarea succesiv a fiierului cod surs de ctre compilator (compiler) i apoi de ctre link-editor (linker).

19

Fig. 1.5 Procesul de elaborare a unui program executabil

Procesul de ob inere a unui executabil este prezentat n figura de mai jos. Blocurile tridimensionale reprezint entit ile principale ale mediului de programare: editorul de texte, compilatorul (compiler) i editorul de legturi (linker). Blocurile dreptunghiulare reprezint fiierele rezultate n urma aplicrii celor trei utilitare de sistem: n urma utilizrii editorului de texte ob inem fiierul text surs cod cu numele generic nume. Dac folosim limbajul de programare C spre exemplu, se ob ine fiierul nume.c care se va salva pe disc. n urma lansrii n execu ie a compilatorului, acesta preia fiierul surs i l prelucreaz corespunztor, semnalizndu-se toate erorile fatale pentru program sau avertismente utile programatorului n procesul de depanare. n cazul n care compilarea se efectueaz cu succes, se ob ine un fiier cod obiect, salvat pe disc sub numele nume.obj n urma lansrii n execu ie a editorului de legturi, se preia fiierul cod obiect nume.obj i se leag cu toate modulele necesare (inclusiv func ii de bibliotec sau alte module externe), ob inndu-se un program executabil (cod main) cu numele nume.exe la care adresele nu mai sunt simbolice ci absolute relativ la adresa de nceput a programului. La lansarea n execu ie a programului fluxul de informa ie este complet controlat de ctre microprocesor, toate salturile de adres fiind fcute corespunztor. Interpretorul (interpreter) este un program care execut instruc iuni scrise ntr-un limbaj de nivel nalt. Numai anumite limbaje

20

de nivel nalt, spre exemplu BASIC, LISP sau MATLAB, sunt prevzute cu un interpretor. Exist dou modalit i de a executa un program scris n limbaj de nivel nalt. Cel mai comun mod este acela de a compila programul. Cealalt modalitate este pasarea programului unui interpretor. Un interpretor translateaz instruc iunile de nivel nalt ntr-o form intermediar care este apoi executat. Prin contrast, un compilator translateaz instruc iunile de nivel nalt direct n limbaj main (cod main). Programele compilate ruleaz n general mai rapid dect cele interpretate. Un alt avantaj al programelor compilate este acela al desprinderii din context n sensul c programele executabile generate n urma procesului de compilare pot fi executate direct sub sistemul de operare al calculatorului. Un program interpretat se execut sub mediul n care a fost creat. Spre exemplu, pentru a rula un program scris n limbajul BASIC se lanseaz n execu ie mediul BASIC, apoi se deschide fiierul surs-BASIC corespunztor i se lanseaz interpretorul de BASIC pentru execu ia sa. Avantajul unui interpretor este acela al evitrii procesului de compilare consumator de timp n cazul n care avem programe de mari dimensiuni. Interpretorul poate executa imediat programele surs. Pentru acest motiv interpretoarele se folosesc mai ales n procesul de dezvoltare al programelor, cnd programatorul dorete adugarea unor mici por iuni de program pe care s le testeze rapid. De asemenea, interpretoarele permit o programare interactiv fiind des folosite n procesul de instruc ie. n mediul de programare MATLAB, mediu interpretor, orice comand utilizator se execut imediat. Se pot edita i fiiere script, care con in secven e de comenzi care se execut secven ial. Programele de descriere a paginii (Page Description Languages) ca PostScript spre exemplu folosesc un interpretor. Fiecare imprimant PostScript are incorporat un interpretor care execut instruc iuni PostScript. Asamblorul (assembler) este un program care face transla ia unui program scris n limbaj de asamblare (limbaj de nivel sczut, corespunztor microprocesorului sistemului de calcul) n limbaj cod main. Putem spune c asamblorul reprezint pentru limbajul de asamblare ceea ce reprezint compilatorul pentru limbajele de nivel nalt. Cum limbajul de asamblare con ine instruc iuni mai pu in

21

complexe dect cele de nivel nalt, asamblorul face practic o convertire biunivoc ntre mnemonicele limbajului de asamblare i codurile binare corespunztoare acestor mnemonice (instruc iuni).
I n s tru c iu n ile n lim b a ju l d e n iv e l n a lt s e in tr o d u c d e la ta s ta tu r .

T o t c e s e in tr o d u c e d e la ta s ta tu r e s te v izib il p e m o n ito r

E d it o r d e te x te ( e v e n tu a l in c o r p o r a t n m e d iu )

F i ie r te x t ( f i ie r s u rs ) c u e x te n s ia a d e c v a t : n u m e .p a s ( lim b a j P a s c a l) n u m e .c ( lim b a j C ) n u m e .c p p ( lim b a j C + + ) n u m e .b a s ( lim b a j B A S I C ) , e t c .

F i ie ru l s u r s c u n u m e le n u m e i e x te n s ia c o r e s p u n z to a r e s e s a lv e a z d in m e m o r ia R A M p e h a r d d is k

C o m p ila t o r S e c o m p ile a z fi ie ru l s u r s

S e o b in e fi ie ru l c o d o b ie c t : n u m e .o b j S e s a lv e a z p e h a rd d is k fi ie r u l n u m e .o b j L in k - e d it a re ( le g a r e a tu tu r o r m o d u le lo r n e c e s a re )

S e o b in e fi ie r u l c o d m a in ( e x e c u t a b il): n u m e .e x e

S e s a lv e a z p e h a r d d is k fi ie r u l n u m e .e x e

L a n s a r e a n e x e c u ie d e c tr e s is te m u l d e o p e r a r e a e x e c u ta b ilu lu i n u m e .e x e

Fig. 1.6 Detalierea procesului de generare a unui executabil

22

Capitolul II REPREZENTAREA DATELOR N CALCULATOR


Se tie c un calculator numeric prelucreaz numere binare. Acest lucru ine de suportul fizic de manipulare, transport i stocare a datelor interne, mai bine zis este legat de faptul c semnalul fizic purttor de informa ie este o tensiune continu cu dou valori: una nalt (High) i una joas (Low). Acestor dou valori li se asociaz natural dou valori logice: T (true, adevrat) i F (false, fals) sau cele dou cifre binare1 i 0.
T e nsiune H igh= 1

L o w = 0 tim p Ca urmare a acestei asocieri spunem, prin abuz de limbaj, c un calculator numeric prelucreaz numere binare. Ca i un numr zecimal, un numr binar are mai multe cifre binare. Sistemul de numera ie binar folosit pentru reprezentarea informa iei n calculatoare este un sistem de numera ie ponderal, ntocmai ca sistemul de numera ie zecimal. Reprezentarea natural a numerelor la nivelul percep iei umane este cea zecimal, pe cnd reprezentarea proprie mainilor de calcul este cea binar. De aici rezult necesitatea compatibilizrii sau interfa rii ntre aceste dou moduri de reprezentare a numerelor. Cum cele dou sisteme de numera ie sunt ponderale, o prim diferen este aceea c sistemul zecimal folosete ca ponderi puterile ntregi (pozitive sau negative) ale lui 10 (zece) iar sistemul binar va folosi puterile ntregi (pozitive sau negative) ale lui 2.

23

n alt ordine de idei, dac pentru reprezentarea extern sunt semnificative simbolurile de reprezentare (cifre, semnele + sau -, punct zecimal sau binar, mantis sau exponent), pentru reprezentarea intern sunt necesare conven ii de reprezentare: indiferent de tipul datelor, acestea vor fi colec ii sau iruri de cifre binare crora, prin conven ie, li se atribuie semnifica ii. ntr-o prim instan , este foarte important s facem o distinc ie ntre tipurile de date recunoscute de un calculator (sau mai bine zis de microprocesorul cu care este dotat calculatorul personal) i formatele de reprezentare ale acestor date ce reprezint conven ii pentru reprezentarea tipurilor de date, att la nivel intern (n memoria calculatorului) ct i la nivel extern, al percep iei umane. Din punctul de vedere al tipurilor de date care sunt implementate n limbajul C putem spune c distingem dou mari categorii, date de tip ntreg (integer) i date de tip real (float). Formatele de reprezentare intern/extern vor fi prezentate n cele ce urmeaz. Cel mai simplu de reprezentat sunt numerele naturale. Se face apoi trecerea la numerele ntregi negative i apoi la numerele reale care au o parte ntreag i una frac ionar.

2.1. Reprezentarea intern/extern a numerelor Reprezentarea intern a numerelor se refer la modul n care se stocheaz datele n memoria RAM a calculatorului sau n regitrii microprocesorului. n acest format se prelucreaz numerele pentru implementarea diverselor opera ii aritmetice. La nivelul calculatorului informa ia nu poate fi dect binar. n aceast reprezentare putem scrie numere ntregi pozitive sau negative sau numere reale. Exist un standard IEEE care reglementeaz modul de reprezentare intern a datelor. Reprezentarea extern este reprezentarea numerelor la nivelul utilizatorului uman, deci n principiu se poate folosi orice baz de numera ie pentru reprezentarea numerelor. La nivel de reprezentare extern se folosete semnul - n fa a unui numr n cazul n care acesta este negativ sau punctul care separ partea ntreag de cea frac ionar. De asemenea, numerele ntregi interpretate fr semn se pot afia i n format binar, octal sau hexazecimal, deci n bazele 2, 8 sau 16. n cele ce urmeaz ne vom pune urmtoarele probleme: - cum se reprezint extern un numr natural

24

cum se reprezint intern un numr natural cum se reprezint extern un numr ntreg negativ cum se reprezint intern un numr ntreg negativ cum se face conversia de la reprezentarea extern la cea intern cum se face conversia de la reprezentarea intern la cea extern

2.2. Reprezentarea extern a numerelor


n ceea ce privete reprezentarea extern, nu sunt nici un fel de dificult i deoarece fiecare este familiarizat cu reprezentarea zecimal a numerelor naturale sau reale. Trebuie men ionat de la nceput c orice tip de reprezentare pe care o vom folosi este ponderal n sensul c pozi ia cifrelor n numr nu este ntmpltoare ci conform cu o pondere corespunztoare unei puteri a bazei de numera ie. O caracteristic a reprezentrilor externe este folosirea unor conven ii de format unanim acceptate i de altfel foarte naturale pentru un utilizator uman. Spre exemplu, pentru a exprima numere negative se folosete semnul - iar pentru reprezentarea numerelor reale se folosete punctul . pentru delimitarea pr ii ntregi de cea frac ionar. De asemenea, suntem familiariza i i cu nota ia tiin ific n care intervine mantisa i exponentul (n virgul mobil). Reprezentarea zecimal este cea mai natural pentru utilizatorul uman. Vom oferi n continuare cteva exemple de reprezentri zecimale externe: Numr Reprezentare Reprezentare normal tiin ific
37 -37 0.375 -0.375 0.00375 -0.00375 12.375 -12.375 37 -37 0.375 -0.375 0.00375 -0.00375 12.375 -12.375 0.37x10 2 -0.37x10 0.375x100 0 -0.375x10 -2 0.375x10 -0.375x10-2 2 0.12375x10 2 -0.12375x10
2

n general dorim s ob inem rezultatele numerice ale programelor pe care le concepem ntr-o form de reprezentare accesibil. Totui, calculatorul trebuie informat asupra formatului de reprezentare n care dorim s se afieze datele necesare. Aceasta

25

nseamn c va trebui s specificm cte cifre se vor folosi la partea ntreag i cte la partea frac ionar sau dac dorim reprezentare tiin ific sau nu. De altfel i operatorul uman face aceleai conven ii de reprezentare. Spre exemplu tim c numrul
1 nu poate fi exact 3

reprezentat ca un numr zecimal, deci fixm un format de reprezentare. Dac formatul ale se limiteaz la 4 cifre zecimale, atunci vom scrie
1 0.3333 3

Limbajul C are o serie de func ii de reprezentare cu format a datelor numerice sau alfanumerice prin care programatorul poate impune un format extern cu care se manipuleaz datele.

2.2.1. Reprezentarea extern a numerelor ntregi


Numerele naturale se pot reprezenta fie n baza de numera ie 10, fie n orice alt baz. n general, un numr ntreg n baza b se poate reprezenta cu un numr predeterminat de cifre ci B = {0,1,2,....., b 2, b 1} . Mul imea B reprezint mul imea cifrelor sau simbolurilor de reprezentare. Spre exemplu: b = 2 B = {0,1} b = 7 B = {0,1,2,3,4,5,6} b = 10 B = {0,1,2,3,4,5,6,7,8,9} Noi suntem obinui i s folosim mul imea cifrelor zecimale. Dac totui se folosete o baz de reprezentare mai mare dect 10, atunci mul imea cifrelor zecimale nu mai este suficient pentru reprezentarea numerelor n acea baz. Spre exemplu s considerm baza b = 16 care va folosi 16 cifre hexazecimale (sau mai simplu hexa). Prin conven ie, cele 16 cifre hexazecimale vor fi: Cifra Simbol Cifra Simbol
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8 9 A B C D E F

26

Forma general de reprezentare extern a numerelor ntregi este de forma:

N b = c n 1c n 2 ......c 2 c1c 0 c k B = {0,1,2,....., b 2, b 1}


Valoarea numeric zecimal a numrului N b va fi:

N b = (c n 1 b n 1 + c n 2 b n 2 + ... + c1 b1 + c 0 b 0 ) = c k b k
k =0

n 1

n continuare vom studia urmtoarele probleme: - cum se face conversia unui numr din baza b = 10 n baza
b=2

cum se face conversia invers, din baza b = 2 n baza b = 10 cum se face conversia dintr-o baz oarecare b1 n alt baz b2 Pentru a reprezenta un numr natural din baza 10 n baza 2, se mparte succesiv numrul la 2 i se utilizeaz resturile la aceste mpr iri n ordinea invers de cum au fost ob inute. a) Conversia din baza 10 n baza 2 i invers Fie de exemplu numrul zecimal 37. Reprezentarea sa binar va fi ob inut astfel: 3710 = 1001012
37 36 1 2 18 18 0 2 9 2 8 4 2 1 4 2 2 0 2 1 0

Conversia invers, din baza 2 n baza 10 este simpl i utilizeaz ponderea 2:

1001012

25 24 23 22 21 20 = 1 0 0 1 0 1 = 1x25 + 1x22 + 1x20 =37

Cu aceste numere naturale putem face o serie de opera ii aritmetice. Adunarea numerelor naturale binare se face ntocmai ca la cele n reprezentare n baza 10, dup regula: 0+0=0

27

0+1=1 1+0=1 1+1=0, transport 1 spre rangul urmtor Astfel, s facem adunarea 37+25 n binar: 37 1 0 0 1 0 1+ 25 1 1 0 0 1 62 1 1 1 1 1 0 Se observ cum se ob ine rezultatul corect. nmul irea se face n mod asemntor, ca o adunare repetat. Spre exemplu, s calculm 37x25

37 25

925

1 0 0 1 0 1x 11001 100101 100101 100101 1110 011101

11100111012 = 1x20 + 1x22 + 1x23 +1x24 +1x27 +1x28+1x29 = 1+4+8+16+128+256+512 = 92510 b) Conversia dintr-o baz oarecare b1 ntr-o alt baz b2 . Fie spre exemplu numrul 4911 care se dorete scris n baza 13. Pentru a realiza aceast conversie, vom folosi baza intermediar 10. Vom converti mai nti 4 A11 n baza 10 i apoi numrul zecimal ob inut l vom trece n baza 13. Se observ cum un numr n baza 11 poate con ine i cifra A=10 iar un numr n baza 13 poate con ine cifrele A=10, B=11, C=12.

4 A11 = 10 110 + 4 111 = 44 + 10 = 5410


54 52 2 13 4 0 4 13 0

5310 = 4213 4 A11 = 4213


28

2.2.2. Reprezentarea extern a numerelor reale


Semnificativ pentru utilizatorul uman este reprezentarea zecimal (n baza b=10) a numerelor reale, cu care suntem obinui i. Fa de reprezentarea numerelor ntregi, la numerele reale intervine simbolul punct . care delimiteaz partea ntreag de partea frac ionar. Cu alte cuvinte, cu ajutorul numerelor reale putem reprezenta i numere care nu sunt ntregi. Forma general a unui numr real reprezentat ntr-o baz oarecare b este:

N b = c n 1c n 2 ...c1c 0 c 1c 2 ...c m +1c m c k B = {0,1,2,..., b 2, b 1}


Valoarea zecimal a numrului de mai sus va fi:
N10 = cn1bn1 +cn2bn2 +c1b1 +c0b0 +c1b1 + c2 b2+cm+1bm+1 +cmbm =

) ck bk k =m

n1

Se observ cum punctul delimiteaz partea ntreag (exprimat printr-o combina ie de puteri pozitive ale bazei b) i partea frac ionar (exprimat printr-o combina ie de puteri negative ale bazei b). Semnifica ie pentru programator i pentru productorii de software sau microprocesoare au bazele de reprezentare b = 10 i b = 2 , deoarece baza 10 este natural pentru reprezentarea extern a numerelor iar baza 2 este natural pentru reprezentarea binar, intern, a numerelor. n formulele de mai sus avem o reprezentare a unui numr real cu n cifre pentru partea ntreag i m cifre pentru partea frac ionar. Aa cum n sistemul zecimal reprezentm cu un numr finit de cifre zecimale numerele reale, acelai lucru se va ntmpla i n sistemul binar. Punctul binar va avea o semnifica ie asemntoare cu punctul zecimal, care face separarea ntre partea ntreag i cea frac ionar. Cifrele binare situate dup punctul binar vor corespunde puterilor negative ale lui 2. Astfel, n general, un numr real va avea reprezentarea binar:

N2 =bmbm1...1b0.b1b2... n =bm2m +bm12m1 +b 21 +b020 +b121 +b222 +...+bn2n b b 1


Spre exemplu, numrul 12.25 va avea reprezentarea binar:

12.2510 = 1100.01 = 2 3 + 2 2 + 2 2

29

Partea ntreag a unui numr real se reprezint binar precum numerele ntregi (cu sau fr semn). Pentru a determina partea frac ionar, se procedeaz n mod invers ca la partea ntreag. Astfel, dac partea frac ionar zecimal se reprezint binar, atunci aceasta se nmul ete succesiv cu 2. Dac rezultatul depete valoarea 1, atunci se nscrie un bit 1. Se continu mai departe cu dublarea valorii care depete 1. Dac rezultatul nu depete valoarea 1, atunci se nscrie un bit 0 i se continu multiplicarea cu 2. Spre exemplificare, vom vedea cum se ob ine reprezentarea binar a lui 12.25. Partea ntreag este 12. Ea se reprezint binar prin mpr iri succesive la 2 i considerarea resturilor. Partea frac ionar este 0.25 Partea P.F. x 2 Noua Bitul frac ionar P.F. nscris P.F.
0.25 0.5 0 0.5 1 0 0 1

Ob inem exact rezultatul cutat: 12.25 = 1100.01 S mai considerm un alt exemplu. S reprezentm numrul 5.37 Partea ntreag are reprezentarea 510 =1012
Partea frac ionar P.F. 0.37 0.74 0.48 0.96 0.92 0.84 0.68 0.36 0.72 Etc.. P.F. x 2 0.74 1.48 0.96 1.92 1.84 1.68 1.36 0.72 1.44 Noua P.F. 0.74 0.48 0.96 0.92 0.84 0.68 0.36 0.72 0.44 Bitul nscris 0 1 0 1 1 1 1 0 1 Etc..

Ob inem: 5.3710 = 101.010111101...2 Cu ct mai multe cifre binare vom re ine dup punctul binar, cu att vom fi mai aproape de valoarea exact 5.37. Ob inem un rezultat foarte important: Dei un numr zecimal poate avea un numr finit de cifre zecimale dup punctul zecimal, reprezentarea sa binar intern poate avea un numr infinit de cifre binare. Este valabil i reciproca: un numr real zecimal cu un numr

30

infinit de cifre se poate reprezenta ntr-o alt baz pe un numr finit 1 de cifre ( ex: = 0.3333...3...10 = 0.13 ). Cum orice reprezentare 3 binar intern este pe un numr finit de bi i, numrul poate s nu fie reprezentat exact n calculator, ci cu o anumit aproxima ie. Acest lucru este decisiv pentru a n elege importan a lungimii reprezentrii numerelor n calculator. Cu ct un numr binar se reprezint pe un numr mai mare de bi i, cu att precizia de reprezentare crete.

2.3 Reprezentarea intern a numerelor


Deoarece semnalul intern purttor de informa ie ntr-un calculator este de tip binar, un numr zecimal (ntreg sau real) se va reprezenta intern n baza 2 cu ajutorul unui numr binar. O cifr binar se numete bit (Binary Digit) i poate fi fie 0 fie 1. n reprezentarea extern a numerelor am vzut c se poate folosi orice baz de numera ie (cu cifrele corespunztoare). De asemenea, numerele pot fi prefixate cu un simbol de semn i pot include n reprezentare i punctul de separa ie ntre partea ntreag i cea frac ionar. n reprezentarea intern acest lucru nu mai este posibil deoarece semnele plus (+), minus (-) sau punct (.) nu au nici o semnifica ie pentru calculator. Orice numr (orice tip de dat) este reprezentat la nivel intern de un numr prestabilit de bi i. Specialitii din industria software au ajuns la un consens de reprezentare concretizat prin standardul IEEE 754 de reprezentare a intern a numerelor reale n computere. Reprezentarea intern a numerelor a impus n limbajul C definirea aa-numitelor tipuri de date. Tipul unei date reprezint modul n care microprocesorul stocheaz n memorie i prelucreaz cu ajutorul regitrilor interni o dat. Tipul unei date se refer la lungimea sa de reprezentare (pe c i bi i se reprezint data) precum i ce semnifica ie au anumite cmpuri de bi i din cadrul reprezentrii.

2.3.1. Reprezentarea intern a numerelor ntregi


Un numr binar este o colec ie de cifre binare ponderate fiecare cu o putere a lui 2. Bitul corespunztor ponderii celei mai mari, situat cel mai n stnga, se numete MSB (Most Significand Bit) iar cel

31

corespunztor ponderii celei mai mici, situat cel mai n dreapta, se numete LSB (Less Significand Bit). n cazul reprezentrii binare a numerelor naturale, reprezentarea extern (cea perceput de operatorul uman) i cea intern (cea prelucrat de procesorul calculatorului) sunt asemntoare. Cum pentru operatorul uman operatorii + sau - semnific faptul c un numr este pozitiv sau negativ, este necesar o conven ie pentru reprezentarea intern a numerelor ntregi negative. Aceast conven ie prevede folosirea MSB pentru reprezentarea semnului numerelor ntregi. Dac numrul este pozitiv, se adaug n pozi ia MSB bitul de semn 0, iar dac numrul este negativ se utilizeaz n pozi ia MSB bitul de semn 1. Mai mult, numerele negative se reprezint n aa numitul complement fa de 2. Reprezentarea numerelor ntregi negative n complement fa de 2 Aceast form de reprezentare a numerelor negative necesit parcurgerea urmtorilor pai: pas1. Se reprezint modulul numrului negativ, folosind bit de semn (egal cu 0, evident) pas2. Se complementeaz to i bi ii numrului astfel ob inut. Complementarea nseamn transformarea bitului 0 n bitul 1 i a bitului 1 n bitul 0. pas3. Numrul astfel ob inut se adun cu 1. De exemplu, s reprezentm numrul -37. pas1. |-37| = 37 3710 = 1001012 = [0] 100101
bit semn

pas2. 0100101---->1011010 pas3. 1011010 + 1 = 1011011 => -3710 = 10110112 Evident, MSB este bitul de semn i este egal cu 1. La o prim vedere, este posibil s credem c prin utilizarea complementului fa de 2 putem pierde semnifica ia numrului negativ. Pentru a vedea ce numr negativ este reprezentat, putem repeta procedeul de mai sus i ob inem reprezentarea numrului pozitiv dat de modulul su.
O modalitate mai simpl este alocarea ponderii corespunztoare bitului de semn dar pe care o considerm c reprezint un numr negativ. Astfel: 10110112 = -1x26 + 1x24 + 1x23 + 1x21 + 1x20 = -64 + 27 = -37

32

2.3.2 Adunarea, scderea i nmul irea numerelor ntregi


Aceste opera ii se execut folosind reprezentarea n complement fa de 2 a numerelor ntregi, sau, mai bine zis, se execut folosind n algoritmi bitul de semn ca pe un bit obinuit. De exemplu, dorim s calculm: 37-25 25-37 (-25)x37 (-25)x(-37) Pentru efectuarea acestor calcule, vom scrie reprezentrile cu bit de semn ale numerelor implicate: 2510 = 110012 = 011001 25 = 100111 10 2 3710 = 1001012 = 0100101 3710 = 1011011 Se observ c 25 i (-25) se reprezint pe 6 bi i iar 37 i (-37) pe 7 bi i. Deoarece am observat c bi ii unui ntreg cu semn nu au to i aceeai semnifica ie, este nevoie s reprezentm numerele cu care lucrm pe un acelai numr de bi i. La adunri sau scderi, bi ii de semn se vor afla n aceeai pozi ie (vor avea aceeai pondere) i vom ob ine astfel rezultate corecte. Pentru a avea o scriere pe un acelai numr de bi i, se adaug (completeaz) la stnga bitul de semn de un numr corespunztor de ori. Astfel:
2510 = 1001112 = 1100111 25 = 0110012 = 0011001

37 25 = 37 + ( 25) = 0100101 + 1100111 0100101 +

1100111 0001100 = 1210

25 37 = 25 + (37) = 0011001 + 1011011


37 = 1011011 25 = 0110012 = 0011001

0011001 + 1011011 1110100 = 64 + 52 = 12

33

n continuare vom pune n eviden importan a gamei de reprezentare, adic a domeniului de valori ale datelor. S considerm, spre exemplu, adunarea a dou numere cu semn reprezentate pe un octet (8 bi i). Aceste numere sunt cuprinse n gama

[ 2 , 2 1] = [ 128, 127].
7 7

Dac vom dori s adunm dou numere din acest domeniu i s reprezentm rezultatul tot pe un octet, putem avea surprize. De exemplu, s considerm opera iile (117-12) i (117+12). Se observ c operanzii sunt n gama de reprezentare a numerelor cu semn pe 8 bi i. Prin prima scdere, ne ateptm s ob inem un rezultat, 105, n aceeai gam de reprezentare. 117-12=117+(-12) = 01110101+11110100 = 01101001 = 10510, rezultat corect. 117+12 = 01110101+00001100 = 10000001 = -12710, rezultat evident incorect. Incorectitudinea provine de la faptul c rezultatul a depit gama de reprezentare. Dac rezultatul este interpretat pe 9 bi i de exemplu, gama de reprezentare devine [ 256, 255] i rezultatul va fi 117+12 = 001110101+000001100 = 010000001 = 12910, rezultat corect. Ca o concluzie preliminar, re inem c pentru a ob ine rezultate corecte este necesar s precizm dac se lucreaz sau nu cu bit de semn i pe c i bi i se face reprezentarea, pentru c numai n acest context interpretarea rezultatelor este corect. n ceea ce privete nmul irea numerelor ntregi cu semn (cu bit de semn), aici problema nu mai are o rezolvare asemntoare, n sensul c nu putem trata bi ii de semn la fel cu cei de reprezentare ai valorii. Astfel, procesorul studiaz bi ii de semn i ia o decizie n privin a semnului rezultatului. De fapt, se realizeaz func ia logic XOR a bi ilor de semn. Numerele negative se vor lua n modul, iar opera iile de nmul ire se vor face numai cu numere pozitive. La final, func ie de semnul rezultatului, se ia decizia reprezentrii corecte a rezultatului. Spre exemplu, s calculm (-25)x37. Pentru aceasta, procesorul va primi pentru procesare urmtoarele dou numere: 37 x(25) = [0]100101 [1]100111

34

Se analizeaz separat bi ii de semn i se ia decizia c rezultatul va fi negativ, deci, la final, se va reprezenta n complement fa de 2. Mai departe se va lucra cu 25, modulul numrului (-25), care se ob ine prin complementarea fa de 2 a numrului binar 1100111: 1100111 0011000+1=0011001 Se va re ine pentru procesare numai numrul (fr semn) 11001, care se va nmul i cu numrul (fr semn) 100101, ob innd, aa cum am artat mai sus, valoarea 1110011101. Mai departe, se adaug bitul de semn, 0 pentru numere pozitive, ob inndu-se 01110011101. Acest ultim numr se va complementa fa de 2, ob inndu-se 10001100010+1=[1]0001100011, adic valoarea -1024+99 = -925, valoarea corect. Ca o concluzie, pentru a furniza rezultate corecte, procesorul va trebui informat n permanen despre ce fel de numere prelucreaz (cu sau fr semn) i care este lungimea lor de reprezentare (toate trebuie s aib aceeai lungime). Reprezentarea n complement fa de 2 se poate folosi i pentru numerele reale negative, bitul de semn fiind MSB de la partea ntreag. Astfel, -12.25 poate avea reprezentarea: 12.2510 = 1100 .012 01100 .01

01100 .01 10011 .10 + 0.01 = 10011 .11 10011 .112 = 2 4 + 21 + 2 0 + 2 1 + 2 2 = 16 + 3 + 0.75 = 12.2510

Pentru nmul irea numerelor reale rmn valabile considerentele de la numere ntregi.
n cazul de mai sus, problema reprezentrii numrului negativ a fost rezolvat cu ajutorul bitului de semn dar problema reprezentrii punctului binar va avea alt rezolvare.

2.3.3 Reprezentarea intern a numerelor reale


Din considerentele de la reprezentarea extern a datelor putem trage alte concluzii importante din punct de vedere al reprezentrii interne. Numerele binare ntregi fr semn au aceeai reprezentare att extern ct i intern. Numerele ntregi cu semn (care n reprezentare extern sunt prefixate cu ) au ca reprezentare intern un bit de semn, dar care se trateaz deosebit de ceilal i bi i ai reprezentrii. To i ntregii cu semn, care au MSB=1, sunt reprezenta i intern n complement fa de 2.

35

Numerele reale se pot reprezenta identic cu cele ntregi cu semn, cu o precizare: nu se face o deosebire net ntre bi ii reprezentrii pr ii ntregi i cei ai reprezentrii pr ii frac ionare. Acest tratament nediferen iat provine de la reprezentarea tiin ific uzual cu mantis i exponent. Fie, spre exemplu, reprezentarea binar a numrului 12.25:

12.2510 = 1100.01 = 0.110001 x 2 4

Calculatorul poate reprezenta irul de bi i 110001 i re ine faptul c punctul se pune dup primii 4 bi i ai reprezentrii. Acest lucru se ntmpl i n realitate. Deci, singura deosebire ntre reprezentarea numerelor reale i a celor ntregi const n faptul c numerele reale necesit o informa ie suplimentar despre aa numitul exponent, n cazul nostru numrul pozitiv 4. n cele ce urmeaz, vom prezenta tipurile de baz pe care le pot avea datele n reprezentarea intern. Tipul unei date determin modul n care procesorul stocheaz i prelucreaz data respectiv. Cum primele procesoare care au condus la apari ia pe pia a primelor calculatoare pentru neprofesioniti (aa numitele Home Computers) au fost procesoare capabile s prelucreze i s transmit n paralel 8 bi i, a fost natural gruparea a 8 bi i ntr-o entitate numit byte. 1B = 8b (adic un byte reprezint 8 bi i) Procesoarele au evoluat, ajungndu-se n prezent la procesoare pe 64 de bi i. Cum evolu ia lor s-a fcut trecndu-se succesiv prin multipli de 8 bi i, s-au impus i alte entit i de reprezentare a informa iei, pe care le vom prezenta sintetic n tabelul de mai jos.

Denumire

Dimensiune
Nr. byte Nr. biti
8b 16 b 32 b 64 b 80 b

Denumire echivalent

Nota ie

Byte Word Double_Words Quad_Words Ten_Words

1B 2B 4B 8B 10B

octet cuvnt Cuvnt dublu Cuvnt cvadruplu

B W DW QW TW

A determina reprezentarea intern nseamn s determinm lungimea reprezentrii (de obicei n multipli de octe i), modul de interpretare al bi ilor ce compun reprezentarea i gama de

36

reprezentare, adic s determinm magnitudinea (valorile minime i maxime pozitive i negative) ce pot fi reprezentate n formatul respectiv. n limbajul C, exist dou tipuri de reprezentare pe care le putem numi principale: tipul ntreg i tipul real, fiecare avnd i anumite particularizri. Astfel, tipul ntreg (int) include i tipul caracter (char) iar tipul real (float) include i tipul real extins (double). Tipurile de date le vom reprezenta de la simplu la complex, n ordinea char, int, float, double. Tipurile de baz sunt char, int, float, double i cu ajutorul modificatorilor de tip putem ob ine diverse particularizri. Modificatorii pot fi signed, unsigned, short, long. Ca o generalitate, numerele sunt reprezentate intern lundu-se n considerare bitul de semn, deci implicit numerele ntregi sau reale au MSB bit de semn. Dac se specific explicit, prin modificatorul unsigned, nu se mai consider (interpreteaz) bitul de semn. 2.3.3.1 Tipul char Codul ASCII (American Standard Code for Information Interchange) este un cod de reprezentare a caracterelor. Prin caracter n elegem unit ile de baz care se pot tasta (intrri de la tastatur), tipri la imprimant sau afia pe ecran. Tastatura reprezint, de exemplu, dispozitivul de intrare care con ine de fapt o ntreag colec ie de caractere ce pot fi emise prin apsarea unei taste. Pentru a fi receptat, emis sau prelucrat de ctre calculator, fiecare caracter are asociat un cod binar (o combina ie de bi i) care l identific n mod unic. Cum cu un octet putem codifica 28 = 256 caractere, octetul s-a dovedit o entitate suficient pentru codificarea caracterelor utilizate n informatic. n 256 de coduri distincte se pot include literele mari i mici ale alfabetului anglo-saxon (inclusiv litere specifice diverselor alfabete precum cel chirilic sau particularit i ale diferitelor ri: , , , , ... n romn, de exemplu). Se mai pot include caractere ce reprezint numere, semne de punctua ie sau alte caractere de control. Codul ASCII a standardizat aceast codificare, astfel nct el este folosit n cvasitotalitatea calculatoarelor (doar mainframe-urile IBM mai folosesc un alt cod, mai vechi, numit EBCIDIC). Dac se declar o dat de tip char, ea este considerat explicit de tipul signed char (cu MSB bit de semn), deci reprezentarea intern este de forma:

37

b6

b5

b4

b3

b2

b1

b0

B it d e s e m n Gama de reprezentare este cuprins ntre max = 27 1 = 127 [ 128, 127] min = 27 = 128 Dac se declar tipul unsigned char, atunci nu se mai consider (interpreteaz) bitul de semn i data se consider ntreag pozitiv, n gama max = 2 8 1 = 255 [0, 255] min = 0 Tabelele de mai sus con in codurile ASCII ale primelor 128 de caractere. Coloana D semnific valoarea zecimal (decimal) a octetului, coloana H reprezint aceeai valoare reprezentat n format hexazecimal (baza 16) iar n coloana Sym se reprezint simbolul afiat pe monitoarele PC. ntregul alfabet al limbajului C se regsete n mul imea primelor 128 de caractere ASCII. Restul de 128 de caractere se mai numete i set de caractere extins ASCII i poate fi vizualizat printr-un program simplu. Trebuie men ionat faptul c reprezentarea datelor n format hexazecimal este foarte rspndit n tehnica programrii calculatoarelor. Avantajul reprezentrii interne a datelor n format hexazecimal const n folosirea unui numr mai mic de cifre (de 4 ori mai mic dect numrul de cifre binare). Reprezentarea unui numr natural n format hexazecimal se realizeaz cu metoda mpr irii succesive la 16 sau, mai simplu, pornind de la reprezentarea binar a numrului. Cum mul imea cifrelor hexa con ine 16 simboluri (09 i AF), pentru codificarea celor 16 cifre avem nevoie de 4 cifre binare ( 2 4 = 16 ). Pentru a reprezenta un octet vom avea nevoie de 2 cifre hexazecimale i vom proceda astfel: - se divide octetul n dou grupe de cte 4 bi i

38

se nlocuiete fiecare grup de 4 bi i cu cifra hexazecimal pe care o codific. De exemplu, s presupunem c avem numrul 217.
21710 = 110110012 = 1101.10012 = D916 = 13 161 + 9 16 0 = 208 + 9 = 217

n acest mod, dac un numr are o reprezentare intern pe un numr de k octe i, se poate reprezenta simplu cu ajutorul a 2k cifre hexazecimale. n tabelele de mai jos se prezint codificarea ASCII a caracterelor. Codurile corespunztoare simbolurilor alfanumerice din tabel sunt exact semnalele binare care se transmit n reprezentarea intern. Cu alte cuvinte, dac la tastatur se tasteaz simbolul a, atunci circuitele corespunztoare transmit spre calculator semnale binare corespunztoare codului 1010 0001, adic 61H sau 97 n zecimal. La fel se ntmpl cnd se lucreaz cu procesoare de text sau cnd se tiprete un document la imprimant. Sistemul de calcul manevreaz codurile ASCII corespunztoare literelor i cifrelor pe care utilizatorul le poate interpreta.

D 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

H 0 1 2 3 4 5 6 7 8 9 a b c d e f

Sym Null

LF CR

D 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

H 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f

Sym

D 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

H 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f

Sym D 48 ! 49 " 50 # 51 $ 52 % 53 & 54 ' 55 ( 56 ) 57 * 58 + 59 , 60 61 . 62 / 63

H 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f

Sym 0 1 2 3 4 5 6 7 8 9 : ; < = > ?

39

D 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

H 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f

Sym D @ 80 A 81 B 82 C 83 D 84 E 85 F 86 G 87 H 88 I 89 J 90 K 91 L 92 M 93 N 94 O 95

H 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f

Sym P Q R S T U V W X Y Z [ \ ] ^ _

D 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

H 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f

Sym ` a b c d e f g h i j k L M n o

D 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

H 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f

Sym p q r s t u v w x y z { | } ~

2.3.3.2 Tipul int Acest tip se folosete pentru reprezentarea numerelor ntregi cu sau fr semn. Odat cu standardizarea ANSI C din 1989, s-a trecut la modul de reprezentare a ntregilor impus de noul procesor Intel 80386 dotat i cu coprocesorul matematic Intel 80387.
M SB S b30 O ctetul 1 O ctetul 2 O ctetul 3 b0 LS B O ctetul 4

Tipul int este identic cu signed int i utilizeaz o reprezentare pe 4B a numerelor ntregi cu semn. Reprezentarea pe 4 octe i duce la posibilitatea mririi gamei de reprezentare astfel:
max = 2 31 1 3 3 ; 2 31 = 2 2 30 = 2 210 2 10 3 2 10 9 31 min = 2

( )

( )

40

Rezult c putem reprezenta numere ntregi n gama:

[ 2.1475 10 ] [ 2 10 , 2 10 ]
9 9 9

unsigned int nu va mai lua n considerare bitul de semn, astfel nct reprezentarea intern este de forma din figura de mai jos. Evident,
3 3 max = 2 32 1 ; 2 32 = 4 2 30 = 4 210 4 10 3 4 10 9 min = 0

( )

( )

Gama de reprezentare se modificatorilor short sau long.


MSB S b 14

poate

schimba

cu

ajutorul

b0 LSB

short int se va reprezenta pe 2B, sub forma

max = 215 1 ; 215 = 2 5 210 = 32 210 [ 32768, 32767] . min = 215


unsigned short int va schimba gama de reprezentare n [0, 65535] long int se va reprezenta pe 8B i va conduce la o gam imens de reprezentare a numerelor ntregi, lucru dovedit de
6 2 63 = 2 3 210 8 1018 = 9.2234 1018

( )

unsigned long int va considera numai numere ntregi pozitive n gama 0, 1.844 1019 . 2.3.3.2 Tipul float Acest tip de reprezentare este de tip real, fiind cunoscut i ca reprezentare n virgul mobil (floating point). Acest tip descrie mecanismul de baz prin care se manipuleaz datele reale. Conceptul fundamental este acela de nota ie tiin ific, prin care orice numr se poate exprima ca un numr zecimal (deci, cu punct zecimal) multiplicat cu o putere a lui zece sau ca un numr real binar (cu punct binar) multiplicat cu o putere a lui 2.

5.2510 = 101.01 = 1.0101 x 2 2 = 0.10101 x 2 3 41

Se observ cum stocarea n calculator a unei date floating-point necesit trei pr i: - bitul de semn (sign) - mantisa, frac ia (significand) - exponent (exponent) Folosind formatul specific I80386, n limbajul C se disting trei tipuri de date reale: - float , cu reprezentare pe 4 octe i (32 bi i, double word) - double, cu reprezentare pe 8 octe i (64 bi i, quad word) - long double, cu reprezentare pe 10 octe i (80 bi i, ten word)
MSB b31 b30

b0 LSB
S 31 30 S Exponent biased 23 22 Exponent = 8b Bias = 7FH=127 Significand = 23b Significand

0 float

63 62 52 51 Exponent = 11b S Bias = 3FFH=1023 79 78 S 64 63

0 Significand = 52b double

0 Significand = 52b long double

Exponent = 15b Bias = 3FFFH=16383

Tipurile float i double sunt formate pentru numere reale ce exist numai n memorie. Cnd un astfel de numr este ncrcat de procesor n stiva pentru numere reale (flotante) pentru prelucrare sau

42

ca rezultat al prelucrrii, el este automat convertit la formatul long double (sau extended). n cazul n care acest numr se stocheaz n memorie, el se convertete la tipul float sau double. Toate cele trei subtipuri reale au un format comun, care va fi prezentat n continuare. Ceea ce le deosebete este numrul de bi i aloca i pentru exponent i pentru mantis, precum i interpretarea bi ilor mantisei (significand). Semnul are alocat n toate formatele un singur bit: 0 pentru numere pozitive i 1 pentru numere negative. Mrimea cmpului exponent variaz cu formatul i valoarea sa determin c i bi i se mut la dreapta sau la stnga punctului binar. Cmpul significand este analogul mantisei n nota ia tiin ific. El con ine to ii bi ii semnificativi ai reprezentrii, deci bi ii semnificativi att ai pr ii ntregi ct i ai pr ii frac ionare cu singura restric ie ca aceti bi i s fie consecutivi. Deoarece punctul binar este mobil, cu ct sunt mai mul i bi i aloca i pr ii ntregi, cu att vor fi mai pu ini pentru partea frac ionar i invers. Cu ct formatul este mai larg, cu att se vor reprezenta mai precis numerele. Pentru a salva un spa iu pre ios de stocare, nici unul dintre cele trei formate float nu stocheaz zerouri nesemnificative. De exemplu, pentru numrul 0.0000101 = 0.101x 2 4 cmpul significand va stoca numrul 101, nu i cele 4 zerouri nesemnificative ale pr ii frac ionare. Pentru a salva i mai mult spa iu, pentru formatele float i double cmpul significand nu va con ine primul bit semnificativ care obligatoriu este 1. Ctignd acest bit (numit bit phantom), se dubleaz gama de reprezentare. Formatul long double va con ine totui bitul de semn 1 cel mai semnificativ. Punctul binar se pune exact naintea primului bit din cmpul significand, adic dup bitul 1 implicit (phantom). n cazul long double, se aplic dup primul bit 1. Pentru a uura operarea cu aceste numere, cmpul exponent nu este stocat ca un numr ntreg cu semn, ci este decalat (normalizat, cu bias) pentru a reprezenta numai numere pozitive (deci exponentul este interpretat ca numr natural fr semn). Biasul adugat se scade pentru a afla exponentul exact. Avantajul exponentului decalat const, pe lng faptul c nu mai are nevoie de bit de semn, n faptul c pentru a compara dou numere reale putem ncepe prin compararea bi ilor pornind de la MSB ctre LSB, cel mai mare fiind cel care are 1 la primul bit diferit. Se decide astfel foarte rapid care numr este cel mai mare. Ca exemplu, s considerm un format float n care se stocheaz: Sign = 0

43

Exponent = 10000010 = 13010 Significand = 100100000 Valoarea real a exponentului va fi 130 - 127 = 3 Bi ii cmpului significand se ob in adugnd MSB phantom, deci acetia vor fi 11001000...00 Numrul real care s-a stocat este: 0.110010...00 x 24 = 1100.1 =12.5 Reprezentarea intern a numrului 12.5, pe 4 octe i (float), este urmtoarea: Semn
0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0

LSB Cu alte cuvinte, putem spune c reprezentarea intern a numrului real 12.5 este (n format hexazecimal):

12 . 5 10 = 41480000 16 n cazul n care dorim s reprezentm numrul negativ 12.5, singurul bit care se va modifica va fi bitul de semn, care devine 1. Astfel, reprezentarea intern n format float a numrului negativ real 12.5 este:
Semn 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 LSB

12 . 5 10 = C 1480000 16 Dac numrul 12.5 se reprezint n formatul double, deci pe 8 octe i, atunci reprezentarea sa intern se va realiza astfel: - bitul de semn va fi 0

44

exponentul nu va mai fi pe 8 bi i ca la tipul float, ci pe 11 bi i, deci se va schimba i bias, care va fi 1023. Atunci:

exponent 1

+ 1023 = 1026 = 1024 + 2 = 1000000001 0


bias

significand va fi acelai ca la tipul float, dar reprezentat pe 52 de bi i


Semn 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 LSB

12 .510 = 4029000000 000000 16


Re inem c la numere reale numai bitul de semn indic dac numrul este pozitiv sau negativ, mantisa i exponentul se reprezint ca numere naturale fr bit de semn. Formatele prezentate mai sus respect standardul IEEE 754 de reprezentare a intern a numerelor reale n computere. Se poate pune o ntrebare legitim: de ce bias-ul n cazul float spre exemplu este 127? Pentru a rspunde la aceast ntrebare, putem face urmtorul ra ionament: - exponentul cu semn este reprezentat pe 8 bi i, deci este n gama de reprezentare [ 128, + 127] . - pentru a ob ine un exponent pozitiv, adugm numrul 128. - deoarece bitul phantom nu este reprezentat, exponentul trebuie micorat cu o unitate pentru a indica unde anume se pozi ioneaz exact punctul binar. - Exponent pozitiv = exponent +128 1 = exponent + bias de unde rezult evident faptul c bias = 127 n cazul tipului float. n final s analizm un exemplu de procesare a produsului a dou numere reale. Vrem s calculm valoarea 5.25 x 1.5. Pentru aceasta, vom scrie cei doi factori ai produsului n forma:

45

.10101

5.25 = 101.01 = .10101 23 10 2 1 ; 1.510 = 1.12 = .11 2 3+1 5.25 1.5 = [(.10101) (.11)] 2
4

.11 10101 10101 .0111111

5.25 1.5 = .0111111 2 = 111.111 = 7.875


Se observ cum cmpurile exponent i significand sunt procesate separat, n final corelndu-se forma de reprezentare intern.

Game de reprezentare pentru numerele reale


Gama de reprezentare pentru fiecare din tipurile reale prezentate mai sus se calculeaz lund n considerare cel mai mare numr i cel mai mic numr posibil a fi scris n respectiva reprezentare. Astfel, exponentul este decisiv pentru gama de reprezentare. La tipul float, avem exponent _ biasmax = 255 exponent _ realmax = 255 127 = 128

nmax = 2128 = 28 210 = 256 (1024)12 256 1036 = 2.56 1038 Valoarea maxim exact, calculat fr a aproxima ca mai sus:
210 = 1024 1000 = 103 este n max = 3.4028 10 38 exponent_ biasmin = 0 exponent_ realmin = 0 127 = 127
12 12 13 nmin = 2127 = 27 210 = 23 210 210 = 8 210 8 1039

( )12
( )

( )

( )

Valoarea pozitiv minim exact este n min = 5.8775 10 39 La tipul double vom ob ine: exponent _ biasmax = 2047 exponent _ realmax = 2047 1023 = 1024
102 nmax = 21024 = 24 210 = 16 (1024)306 16 10306 = 1.6 10307

( )

Valoarea maxim exact este n max = 1.7 10 308

46

exponent _ biasmin = 0 exponent _ realmin = 0 1023 = 1023


102 nmin = 21023 = 23 210 .125 10306

( )

Valoarea pozitiv minim exact este n min = 1.1125 10 308 Efectund aceleai considera ii i calcule pentru tipul long double, 4932 n max = 1.1 10 vom ob ine nmin = 3.4 10 4932

2.3.5. Codificare BCD


Procesorul I80386 este considerat primul procesor care are capacitatea de a procesa opera ii aritmetice asupra unor numere reprezentate n zecimal codificat binar (BCD, binary-coded decimal) n locul formatelor binare standard. Reprezentarea numerelor n cod BCD este folosit pentru a face numerele binare mai accesibile operatorului uman. Neajunsul acestei reprezentri este faptul c numerele BCD ocup spa iu de stocare mai mare dect numerele binare. Ele sunt mai uor de interpretat de ctre programatorul uman, pentru computer neavnd nici un fel de relevan . Procesorul 80386 poate manevra dou tipuri de formate BCD: mpachetat i nempachetat (packed BCD i unpacked BCD). n formatul unpacked BCD, o cifr zecimal se stocheaz pe un octet. Spre exemplu, cifra zecimal 5 va fi reprezentat intern sub forma 00001001. Formatul packed BCD stocheaz dou cifre zecimale pe un octet, crescnd capacitatea de stocare intern precum i gama de reprezentare pe un acelai numr de octe i. Ambele codificri folosesc reprezentarea pe 4 bi i a cifrelor zecimale. Spre exemplu, numrul 9817 se stocheaz pe 4 octe i n format unpacked BCD i pe 2 octe i n format packed BCD: unpacked BCD: 9817 = 0000 1001 0000 1000 0000 0001 0000 0111 packed BCD: 9817 = 1001 1000 0001 0111 Se observ cum valoarea maxim care se poate stoca pe un octet este 9 pentru unpacked BCD, 99 pentru packed BCD i 255 pentru codificarea binar fr semn standard. Toate formatele reale prezentate se conformeaz standardului IEEE 754 pentru reprezentarea numerelor n virgul mobil n format binar.

47

Ca o concluzie la acest capitol, decisiv pentru n elegerea dezvoltrilor ulterioare, putem sintetiza urmtoarele: Reprezentarea extern a numerelor se refer la modul n care operatorul uman accept schimbul de date cu calculatorul. Acest schimb de date are dublu sens: de la operatorul uman ctre calculator i invers. Reprezentarea extern este de obicei zecimal i are un format aproape identic cu formatul matematic uzual: simbol de semn prefixat, punct zecimal, mantis sau exponent. Numerele naturale se mai pot reprezenta i n format octal sau hexazecimal. n format extern se introduc datele de la tastatur pentru prelucrare i se ob in pe monitor sau la imprimant rezultatele oferite de calculator. Reprezentarea intern a numerelor se refer la modul n care se stocheaz datele n memoria RAM a calculatorului i respectiv n regitrii interni ai microprocesorului. Aceast reprezentare intern este legat de no iunea de tip de dat. Tipul de dat ntreg (integer) se reprezint intern pe 2, 4 sau 8 octe i n complement fa de 2, cu cel mai semnificativ bit (MSB) bit de semn: 1 pentru numere ntregi negative i 0 pentru numere ntregi pozitive. Un caz particular de dat de tip ntreg este tipul character, interpretat ca ntreg pe un octet. Tipul de dat real (float) se reprezint intern pe 4, 8 sau 10 octe i i con ine 3 cmpuri de bi i distincte: bit de semn, cmp mantis i cmp exponent, de lungimi corespunztoare. Dac se specific explicit, toate numerele se pot defini fr semn (unsigned), caz n care calculatorul nu mai interpreteaz bitul de semn (MSB) diferit ci l include n cmpul de reprezentare al mrimii, crescnd gama de reprezentare.

48

Capitolul III ELEMENTELE DE BAZ ALE LIMABJULUI C


3.1. Crearea i lansarea n execu ie a unui program C
Prezentm cteva comenzi simple pentru a lansa n execu ie un program C folosind compilatorul BORLANDC v3.1 (versiunea pentru sistemul de operare DOS): dup setarea pe directorul corespunztor se tasteaz - bc pentru a intra n mediul BorlandC; - <Alt>-F pentru a selecta meniul File; - N pentru a deschide un fiier nou. se editeaz programul surs folosind editorul mediului BorlandC; Exemplu:
#include <stdio.h> void main (void) { printf("Primul program in C!"); }

se ac ioneaz tasta F2 i se indic numele fiierului (cu extensia .c sau .cpp) pentru salvarea programului surs pe disc de exemplu mesaj.c - (se recomand salvarea pe disc dup efectuarea oricrei modificri n programul surs pentru evitarea pierderii accidentale a acesteia); se realizeaz compilarea, link-editarea (realizarea legturilor) i lansarea n execu ie a programului executabil mesaj.exe tastnd <CTRL>-F9; pentru a vizualiza rezultatele execu iei programului se tasteaz <Alt>-F5; se revine n fereastra de editare a mediului ac ionnd o tast oarecare; pentru a nchide un fiier surs se tasteaz <Alt>-F3 iar pentru a iei din program n mediul de operare se tasteaz <Alt>-X.

49

La fel de simplu poate fi utilizat i varianta pentru sistemul de operare WINDOWS a compilatorului BORLANDC v3.1: se lanseaz n execu ie programul bcw.exe; se selecteaz din meniul File op iunea New, crendu-se fiierul noname00.cpp; n fereastra noname00.cpp se introduce codul programului; din meniul File se selecteaz op iunea Save As sau Save iar n csu a de dialog care apare se va salva fiierul program cu extensia .cpp; din meniul Compile se selecteaz op iunea Build All ce va afia caseta de dialog Compiling (compilare); dac opera ia de compilare se ncheie cu succes (nu exist erori de sintax n program) compilatorul va afia mesajul Press any key, caz n care compilatorul va crea fiierul executabil; lansarea n execu ie a fiierului executabil se poate realiza folosind op iunea Run din meniul Run sau combina ia de taste <CTRL>-F9. Fiecare limbaj de programare are un set de reguli, denumite reguli sintactice, reguli ce trebuie respectate la editarea unui cod surs. Dac este nclcat o regul sintactic, programul nu va fi compilat cu succes. n acest caz, pe ecran va fi afiat un mesaj de eroare ce specific linia ce con ine eroarea, precum i o scurt descriere a erorii. n exemplul urmtor programului i lipsete caracterul punct i virgul dup utilizarea func ie printf:
#include <stdio.h> void main (void) { printf("Primul program in C!") }

La compilare pe ecran vor apare urmtoarele mesaje de eroare:


Compiling NONAME00.CPP: Error NONAME00.CPP 5: Statement missing ; Error NONAME00.CPP 5: Compound statement missing }

Cu toate c n codul surs exist doar o eroare, compilatorul de C va afia dou mesaje de eroare. Lipsa caracterului punct i virgul provoac o serie de erori n cascad. Pentru a corecta erorile sintactice se merge cu ajutorul cursorului n linia indicat de ctre mesajul de eroare i se corecteaz instruc iunea respectiv.

50

3.2. Structura unui program C


Conceptul de baz folosit n structurarea programelor scrise n limbajul C este func ia. Astfel, un program n C este compus din cel pu in o func ie i anume func ia main() sau func ia principal. La rndul ei, func ia main() poate apela alte func ii definite de utilizator sau existente n bibliotecile ce nso esc orice mediu de dezvoltare de programare n C. Structura general a unei func ii C este de forma: tip nume_func ie (param_1, param_2, ...,param_n)

Instruc iuni declarare tip parametri

Corp func ie=secven de instruc iuni sau apel de func ii

} unde: - tip reprezint tipul de dat returnat de func ie (n mod implicit o func ie returneaz tipul int); - nume_func ie reprezint numele sub care func ia este cunoscut n program; - param_1,...,param_n - parametrii cu care func ia este apelat i al cror tip poate fi declarat direct n aceast list, sau prin instruc iuni separate plasate imediat dup lista parametrilor. Corpul func iei este definit ca o secven de instruc iuni i/sau apeluri de func ii i este delimitat de restul func iei prin paranteze acolade. n limbajul C exist dou categorii de func ii. O prim categorie este format de func iile ce returneaz o valoare la revenirea din ele n punctul de apel, tipul acestei valori fiind definit de de tipul func iei. Cealalt categorie con ine func iile ce nu returneaz nici o valoare la revenirea din ele , pentru aceste func ii fiind utilizat cuvntul cheie void n calitate de tip. El semnific lipsa unei valori returnate la revenirea din func ie. Exemple:
1) void f(void) { }

51

2) double g(int x) { }

Func ia f nu are parametri i nu returneaz nici o valoare.

Func ia g are un parametru x de tipul int i returneaz la revenirea n programul principal o valoare flotant n dubl precizie. Func iile C sunt n general unit i independente, compilabile separat. Instruc iunile, la rndul lor, pot defini tipul unor date folosite n program, sau opera ii ce trebuie executate prin program. Din punct de vedere sintactic, orice instruc iune trebuie terminat cu caracterul ";", iar grupurile de instruc iuni pot fi delimitate prin caracterele { i } pentru a forma unit i sintactice noi de tip bloc. Func iile apelate vor primi valori pentru argumentele (parametrii) lor i pot returna ctre func ia apelant valori de un anumit tip. Cu aceste precizri generale, dac avem un program compus din dou func ii, i anume func ia principal i o func ie apelat f(), atunci structura acestuia va fi de forma:
tip main( ) { -------f( ); / * apelul -------} functiei f() * /

Functia

principala

___________
f( ) { }

Functia

f( )

Programul ncepe cu execu ia func iei main(). Aceasta func ie este folosit n general fr parametri. La rndul lor, func iile apelate pot fi scrise n limbaj C, sau realizate n alte limbaje de programare: asamblare, Fortran, Pascal etc.

52

3.3. Mul imea caracterelor


n programele C pot fi utilizate dou mul imi de caractere: mul imea caracterelor C i mul imea caracterelor C reprezentabile. Mul imea caracterelor C se compune din litere, cifre, semne de punctua ie care au o semnifica ie specific pentru compilatorul C. Programele C sunt formate din combina ii ale caracterelor din mul imea de caractere C constituite n instruc iuni semnificative. Mul imea caracterelor C este o submul ime a mul imii caracterelor C reprezentabile. Mul imea caracterelor reprezentabile este format din totalitatea literelor, cifrelor i simbolurilor grafice. Dimensiunea mul imii de caractere reprezentabile depinde de tipul de terminal, consol etc. Fiecare caracter din mul imea caracter din mul imea caracterelor C are un n eles explicit pentru compilatorul C. Compilatorul d mesaje de eroare cnd ntlnete caractere ntrebuin ate greit sau caractere care nu apar in mul imii caracterelor C. n continuare sunt descrise caracterele i simbolurile din mul imea caracterelor C i utilizarea acestora.

3.3.1. Litere i numere


Mul imea caracterelor C include literele mari i mici ale alfabetului englez i cifrele zecimale din sistemul de numere arabe. Literele mari i mici ale alfabetului englez sunt urmtoarele: ABCDEFGHIJKLMNOPRSTUVWXYZ abcdefghijklmnoprstuvwxyz iar cifrele zecimale: 0 1 2 3 4 5 6 7 8 9. Aceste litere i cifre pot fi folosite pentru a forma constante, identificatori i cuvinte cheie. Compilatorul C prelucreaz litere mari i mici n mod distinct.

3.3.2. Caractere whitespace


Spa iul, tab-ul, linefeed (linie nou), carriage return (revenire la captul rndului), form feed, tab-ul vertical i newline sunt numite caractere whitespace deoarece servesc pentru spa iere ntre cuvinte, aliniere la o nou coloan, salt la linie nou. Aceste caractere separ instruc iuni definite de utilizator, constante i identificatori, de celelalte instruc iuni dintr-un program. Compilatorul C ignor caracterele whitespace dac nu sunt folosite ca separatori sau

53

drept componente de constante, sau ca iruri de caractere. Caracterele whitespace sunt utilizate pentru a face programele mai lizibile. Comentariile sunt de asemenea tratate ca whitespace.

3.3.3. Caractere speciale i de punctua ie


Caracterele speciale i de punctua ie din mul imea caracterelor C sunt folosite pentru mai multe scopuri. Tabelul urmtor prezint aceste caractere. Aceste caractere au o semnifica ie special pentru compilatorul de C. Caracterele de punctua ie din setul de caractere reprezentabile C care nu apar n acest tabel pot fi utilizate numai n iruri, constante caracter i comentarii. Caracter Nume Caracter Nume
, . ; : ? ( ) [ ] { } > < Virgul Punct Punct i virgul Dou puncte Semnul ntrebrii Apostrof Ghilimele Parantez stnga Parantez dreapta Parantez dreapt stnga Parantez dreapt dreapta Acolad stnga Acolad dreapta Mai mare Mai mic ! | / \ ~ _ # % & ^ * = + Semnul exclamrii Bar vertical Slash Backslash Tilda Underscore Diez Procent Ampersand Sgeat sus Asterisc Minus Egal Plus

3.3.4. Secven e escape


Secven ele escape sunt combina ii speciale de caractere formate din whitespace i caractere negrafice constituite n iruri i constante caracter. Ele sunt n mod tipic utilizate pentru a specifica

54

ac iuni precum carriage return i tab pe terminale i imprimante i pentru a furniza reprezentarea caracterelor care normal au n eles special, cum ar fi ghilimelele (). O secven escape const dintr-un backslash urmat de o liter sau combina ii de cifre. Setul complet de secven e escape cuprinde: \a caracterul BEL - activare sunet \b caracterul BS (backspace) - revenire cu un spa iu \f caracterul FF (form feed) - salt de pagin la imprimant \n caracterul LF (line feed) - rnd nou \r caracterul CR (carriage return) - revenire la coloana 1 \t caracterul HT (horizontal tab) - tab orizontal \v caracterul VT (vertical tab) - tab vertical \\ caracterul \ (backslash) \" caracterul " (double qoute) - ghilimele \' caracterul ' (single qoute) - apostrof \0 caracterul NULL \ooo - constant octal \xhh - constant hexazecimal Backslash-ul care precede un caracter neinclus n lista de mai sus este ignorat i acest caracter este reprezentat ca un literal. De exemplu, forma \c reprezint caracterul c ntr-un literal sau ntr-o constant caracter. Secven ele \ooo i \xdd permit scrierea oricrui caracter din setul ASCII ca un numr octal format din trei cifre sau ca un numr hexagesimal format din dou cifre.
Exemplu: '\6' '\60' '\137' '\x6' '\x30' '\x5f' 6 ASCII 48 ASCII 95 ASCII

Numai cifrele octale (de la 0 la 7) pot apare ntr-o secven escape octal i trebuie s apar cel pu in o cifr. De exemplu, caracterul backspace poate fi scris ca \10 n loc de \010. Similar, o secven hexagesimal poate s con in cel pu in o cifr, iar a doua cifr poate fi omis. Totui, cnd se utilizeaz secven e escape n iruri, este indicat s se scrie toate cele trei cifre ale secven ei. Altfel, caracterul care urmeaz dup secven a escape ar putea fi interpretat ca o parte a secven ei, dac se ntmpl s fie o cifr octal sau hexagesial. De exemplu, secven a \0331 este interpretat drept ESC i 1. Dac am scrie \331, omi nd primul zero, atunci am avea o interpretare greit.

55

Secven ele escape permit caractere de control negrafice pentru a fi transmise ctre display. Caracterele negrafice trebuie totdeauna reprezentate ca secven e escape. Plasnd necorespunztor un caracter negrafic n programe C, el are rezultat imprevizibil.

3.4. Identificatori
Identificatorii sunt nume ce sunt date variabilelor, func iilor i etichetelor utilizate n program. Un nume este o succesiune de litere i eventual cifre, primul caracter fiind liter. n calitate de litere se pot utiliza literele mici i mari ale alfabetului englez, precum i caracterul subliniere (_). Numrul de caractere care intr n componen a unui nume nu este limitat. Numele sunt utilizate pentru a defini diferite variabile sau func ii ntr-un program C. n mod implicit, numai primele 32 de caractere dintr-un nume sunt luate n considerare, adic dou nume sunt diferite dac ele difer n primele 32 de caractere ale lor. Exemple de nume: a, b1, a1b2c3, Fs, _hG, Nume, nUME, Se recomand ca numele s fie sugestive, adic ele s sugereze pe ct posibil scopul alegerii lor sau a datei pe care o reprezint.

3.5. Cuvintele cheie ale limbajului C


n limbajul C exist un numr de cuvinte care au o utilizare predefinit, numite cuvinte cheie. Utilizatorul nu poate s utilizeze aceste cuvinte pentru a denumi variabile sau func ii ntr-un program. Tabelul urmtor prezint cuvintele cheie ale limbajului C: Cuvintele cheie ale limbajului C
auto break case else int static default do double if sizeof void float for goto signed unsigned register return short union continue struct switch typedef const extern volatile while char enum long

3.6. Constante
n C, constantele se refer la valori fixe pe care programul nu le poate modifica. Constantele pot fi: ntregi, n virgul mobil sau reale, constante-caracter, constante-ir sau enumerri. Zero poate fi folosit ca o constant pentru tipurile pointer, iar irurile de caractere sunt de fapt constante de tip char[]. Este posibil, de asemenea, s se specifice

56

constante simbolice. O constant simbolic este un nume a crui valoare nu poate fi modificat n domeniul su. n C exist trei feluri de constante simbolice: 1. orice valoare de orice tip poate fi folosit ca i constant prin adaugarea cuvntului cheie const la definirea sa; 2. un set de constante ntregi definite ca o enumerare; 3. orice nume de vector sau func ie.

3.6.1. Constante caracter


O constant caracter este un caracter inclus ntre apostrofuri. De exemplu, 'a', 'A' i '%' sunt constante caracter. Valoarea unei constante caracter este chiar valoarea numeric corespunztoare caracterului dat n setul de caractere al mainii. De pild, dac pe un calculator caracterele se reprezint n cod ASCII, atunci constanta '1' are valoarea 0618, 4910 sau 3116. Constantele caracter pot fi folosite n opera ii de calcul exact ca i ntregii. Caracterele pot fi reprezentate i prin secven e escape (de exemplu, prin constanta '\n' se introduce caracterul newline).

3.6.2. Constante ntregi


Constantele ntregi se reprezint n 4 forme: zecimale, octale, hexazecimale i constante caracter. Constantele zecimale sunt cel mai frecvent folosite i se reprezint ca iruri de cifre zecimale. O constant care ncepe cu zero urmat de x (0x) este un numr hexazecimal, iar o constant care ncepe cu zero este un numr octal. Pentru a reprezenta cifrele hexazecimale 10,...,15 se folosesc literele a,...,f sau literele mari corespunztoare. Nota iile octale i hexazecimale sunt utile n exprimarea succesiunilor de bi i.
Exemplu: int int hex = 0xFF; oct = 011 ; /* numrul 255 n zecimal */ /* numrul 9 n zecimal */

3.6.3. Constante n virgul mobil


O constant n virgul mobil are tipul float, double sau long double. Compilatorul, ca i n cazul constantelor ntregi, trebuie s semnaleze eroare n cazul n care constantele sunt prea mari pentru a putea fi reprezentate. Exemple de constante n virgul mobil: 123.23 .23 0.23 1.0 1. 1.2e10 1.256-15

57

Observa ie. n interiorul constantelor ntregi sau reale nu pot apare spa ii albe. De exemplu, 56.62 e - 17 nu este o constant n virgul mobil, ci sunt de fapt 4 atomi lexicali: 56.62, e, -, 17 i se va genera o eroare de sintax. Dac se dorete o constant de tip float, aceasta se poate defini astfel: const float pi8 = 3.14159265;

3.6.4. Constante ir
Constantele ir constau din caractere cuprinse ntre ghilimele, ca n faimosul Hello, world\n. Constantele ir, spre deosebire de altele, au o loca ie n memoria calculatorului. Caracterele dintr-un ir sunt stocate n memorie, iar valoarea numeric a constantei este adresa acestei memorii. n plus, compilatorul stocheaz caracterul null \0 la sfritul irului, marcnd astfel sfritul su. n cazul setului de caractere ASCII, constanta ir 0 arat astfel n memorie: 3000 48 (30H) 0 0 \0

iar valoarea constantei este adresa sa din memorie (n exemplul de mai sus valoarea 3000), pe cnd valoarea caracterului 0 este 48 sau 30H. Cea mai scurt constant ir este irul null scris drept i este stocat n memorie ca un singur caracter null \0. De exemplu, dac avem constanta ir ABC atunci, la o anumit adres de memorie vom avea: 61 A Adresa+1 62 B Adresa+2 63 C Adresa+3 0 /0 Valoarea constantei ir ABC va fi Adresa, adic valoarea adresei loca iei n care se stocheaz primul caracter din ir. Ca o ultim remarc, vom face precizarea c din punctul de vedere al reprezentrii, constanta caracter A, spre exemplu, este

Adresa

58

diferit de consta ir A, care se stocheaz n memorie la o anumit adres i se termin cu caracterul null, deci are aloca i doi octe i. Fiecare constant ir con ine cu un caracter mai mult dect numrul de caractere din ir deoarece aceasta se termin totdeauna cu caracterul \0 care are valoarea 0. De exemplu, sizeof("asaf") va fi 5. Tipul unui ir este vector de un numr de caractere a.i. "asaf" are tipul char[5]. irul vid se noteaz prin " " i are tipul char[1]. De notat c, pentru fiecare ir s, strlen(s) == sizeof(s) - 1, deoarece func ia strlen() nu numr i terminatorul \0. n interiorul unui ir se poate folosi conven ia de nota ie cu \. Aceasta face posibil reprezentarea caracterului ghilimele (") i \ n interiorul unui ir. Cel mai frecvent caracter folosit este caracterul '\n'=newline (NL). De exemplu, instruc iunea:
printf ("beep at end of message\007\n");

determin scrierea unui mesaj, a caracterului BEL i a caracterului NL. O secven de forma \n ntr-un ir nu determin introducerea unui caracter NL n ir, ci este o simpl nota ie (\n este caracter neafiabil). Nu este permis continuarea irurilor de caractere de pe o linie pe alta. Atunci cnd se include o constant numeric ntr-un ir de caractere utiliznd nota ia octal sau hexazecimal este recomandat s se foloseasc 3 cifre pentru numr. Exemplu:
char v1[] = "a\x0fah\0129";//'a' 'x0f' 'a' 'h' '\012' '9' char v2[] = "a\xfah\ 129"; /* 'a' 'xfa' 'h' '\12' '9' */ char v3[] = "a\xfad\127"; /* 'a' 'xfa' 'd' '\127' */

3.6.5. Constanta zero


Zero poate fi utilizat ca o constant pentru tipurile ntregi, n virgul mobil sau pointer. Nu se recomand alocarea unui obiect la adresa zero. Tipul lui zero va fi determinat de context.

3.6.6. Obiecte constante


Cuvntul cheie const poate fi inclus ntr-o declara ie a unui obiect pentru a determina ca tipul acestui obiect s fie constant i nu variabil.
Exemplu : const const int int model = 145; v[ ] = {1, 2, 3, 4};

59

Deoarece nu i se poate atribui o valoare, o constant poate fi doar ini ializat. Declarnd ceva ca fiind constant, ne asigurm c valoarea sa nu se modific n domeniul su. Astfel instruc iunile:
model = 165; model++; /* eroare */ /* eroare */

vor determina apari ia unor mesaje de eroare corespunztoare. De notat c const modific un tip ceea ce nseamn c restric ioneaz felul n care se poate utiliza un obiect, i nu modul de alocare. Pentru o constant, compilatorul nu rezerv memorie deoarece i se cunoate valoarea (precizat la ini ializare). Mai mult, ini ializatorul pentru o expresie constant este, de obicei (dar nu ntotdeauna), o expresie constant. Dac este aa, aceasta poate fi evaluat n timpul compilrii.

3.6.7. Enumerri
Folosirea cuvntului cheie enum este o metod alternativ pentru definirea constantelor ntregi, ceea ce este uneori mult mai util dect utilizarea lui const. De exemplu, enum {ASM , AUTO , BREAK }; definete 3 constante ntregi denumite enumeratori i le atribuie valori. Deoarece valorile enumeratorilor sunt atribuite implicit, ncepnd cu 0, aceasta este echivalent cu:
const const const ASM = 0; AUTO = 1; BREAK = 2;

O enumerare poate avea nume. De exemplu,


enum Keyword {ASM , AUTO , BREAK };

definete o enumerare cu numele Keyword. Numele enumerrii devine sinonim cu int i nu cu un nou tip. Declararea unei variabile Keyword n loc de int poate oferi att utilizatorului, ct i compilatorului, o sugestie asupra modului de utilizare. De exemplu,
enum Keyword Key; //declara var. Key de tip enum Keyword switch (Key) { case ASM: ........... break; case BREAK: ........... break; }

determin compilatorul s ini ieze un avertisment deoarece sunt folosite numai dou din cele trei valori ale lui Key.

60

Capitolul IV OPERANZI I OPERATORI N C


4.1. Operanzi
O expresie, n limbajul C, este format dintr-un operand sau mai mul i lega i prin operatori. Un operand poate fi: - o constant; - o constant simbolic; - numele unei variabile; - numele unui tablou; - numele unei structuri; - numele unui tip; - numele unei func ii; - elementele unui tablou; - elementele unei structuri; - o expresie inclus ntre paranteze rotunde. Unui operand i corespunde un tip i o valoare. Dac tipul operandului este bine precizat la compilare, valoarea operandului se determin fie la compilare, fie la execu ie.

Exemple: 1. 6353 este o constant ntreag zecimal de tip int i reprezint un operand constant de tip int. 2. float x2 reprezint declara ia variabilei x2, iar numele x2 reprezint un operand de tipul float. 3. 0xa13d este o constant ntreag hexazecimal de tip unsigned i reprezint un operand de tipul unsigned. 4. produs(a,b) este un apel al func iei produs. Aceast func ie reprezint un operand al crui tip coincide cu tipul valori returnate de func ia produs.

4.2. Operatori
Operatorii pot fi unari sau binari n func ie de numrul de operanzi crora li se aplic. Un operator unar se aplic unui singur operand, iar un operator binar se aplic la doi operanzi. Operatorul

61

binar se aplic la operandul care l precede imediat i la care l urmeaz imediat. Operatorii limbajului C nu pot avea ca operanzi constante ir (iruri de caractere). C are mai multe clase generale de operatori: aritmetici, rela ionali i logici, operatori pentru prelucrare bi i, precum i c iva operatori speciali pentru sarcini particulare. La scrierea unei expresii se pot utiliza operatori din toate clasele. La evaluarea unei astfel de expresii este necesar s se in seama de priorit ile operatorilor care apar in diferitelor clase de operatori, de asociativitatea operatorilor de aceeai prioritate i de regula conversiilor implicite.

4.2.1. Operatori aritmetici


Lista operatorilor aritmetici este urmtoarea: reprezint operatorul plus unar sau binar, n func ie de context reprezint operatorul minus unar sau binar, n func ie de context reprezint operatorul de nmul ire (binar) reprezint operatorul de mpr ire (binar) reprezint operatorul modulo (binar) Operandul operatorului unar plus trebuie s fie de tip aritmetic sau pointer, iar rezultatul este valoarea operandului. Un operand ntreg presupune o promovare a ntregilor. Operandul operatorului unar minus trebuie s fie de tip aritmetic, iar rezultatul este numrul negativ corespunztor. Un operand ntreg presupune promovarea ntregilor. Operanzii operatorilor * i / trebuie s fie de tip aritmetic, iar ai lui % trebuie s fie de tip ntreg. Operatorul binar / reprezint ctul, iar % ofer restul mpr irii primului operand la al doilea. Dac al doilea operand al operatorului / sau % este zero, rezultatul este nedefinit. Pentru operanzi de tip ntreg este adevrat egalitatea: (a / b) * b + a % b = a n expresii operatorii binari + i - au aceeai preceden , care ns este mai mic dect a grupului *, / i %. Preceden a ultimului grup este mai mic dect cea a operatorilor unari + i -. Folosirea parantezelor n expresii poate schimba preceden a ntre operatori n timpul evalurii acestora. Exemplu: Dac a, b, c, d sunt variabile de tip int, atunci: - expresia d * b % a este echivalent cu (d * b) % a; - expresia -a / d este echivalent cu (-a) / d; + * / %

62

- expresia a=b=c=d-15
-15)));

este echivalent cu a=(b=(c=(d

- expresia a%-b*c

este echivalent cu (a%(-b))*c;

4.2.2. Operatori de incrementare i decrementare


n C, opera iile de forma i = i+1 i j = j-1 pot fi programate folosind doi operatori unari specifici i anume ++ pentru incrementare cu 1 i -- pentru decrementare cu 1. Aceti operatori pot fi folosi i att ca prefix pentru variabile (de exemplu, ++i, --j) sau ca sufix (i++, j--). ntre aceste moduri de utilizare exist diferen e. Astfel, n expresia ++i, variabila i este incrementat nainte de a-i folosi valoarea, n timp ce n expresia i++, variabila i este incrementat dup ntrebuin area valorii acesteia. Exemplu: Considerm secven a:
x = 10; y = ++x;

Dac se afieaz y, atunci vom gsi y = 11 deoarece mai nti se incrementeaz x i apoi se atribuie valoarea lui y. Dac scriem: x = 10;
y = x++;

vom gsi y=10 (mai nti se face atribuirea lui x la y i apoi incrementarea lui x). Preceden a tuturor operatorilor aritmetici este: nalt Sczut ++ -+ - (unari) * / % + - (binari)

Operatorii de aceeai preceden sunt evalua i de al stnga la dreapta.

4.2.3. Operatori rela ionali


Operatorii rela ionali permit compararea a dou valori i luarea unei decizii dup cum rezultatul comparrii este adevrat sau fals. Dac rezultatul opera iei este fals, atunci valoarea returnat este zero, iar dac este adevrat, valoarea returnat este 1. Operatorii rela ionali folosi i n C sunt:

63

== != < <= > >=

egal diferit mai mic strict mai mic sau egal mai mare strict mai mare sau egal

Operatorii rela ionali au o preceden mai mic dect operatorii aritmetici, astfel o expresie de forma a < b + c este interpretat ca
a<(b+c).

4.2.4. Operatori logici


Operatorii logici binari && (I, AND) i || (SAU, OR) precum i operatorul logic unar de negare ! (NOT), atunci cnd sunt aplica i unor expresii, conduc la valori ntregi 0 i 1, cu semnifica ia fals i adevrat. Semantica acestor operatori se deduce din tabelul urmtor, unde e1 i e2 sunt dou expresii: e1 zero zero diferit de zero diferit de zero e2 zero diferit de zero zero diferit de zero e1&&e2 0 0 0 1 e1||e2 0 1 1 1 ! e1 1 1 0 0

Expresiile legate prin operatori logici binari sunt evaluate de la stnga la dreapta. Preceden a operatorilor logici i rela ionali este urmtoarea: nalt ! > >= < <= == != && ||

Sczut

Astfel, expresia: 10>5 && !(10<9) || 3<4 este adevarat; expresia: 1 && !0 || 1 este adevarat; expresia; 1 && ! (0 ||1) este fals. Programul urmtor tiprete numerele pare cuprinse ntre 0 i 100.
# include <stdio.h> main ()

64

{ int i; for (i = 0; i <= 100; i++) if (! (i%2)) printf ("%d" , i); }

Operatorii logici i rela ionali sunt utiliza i n formarea instruc iunilor repetitive precum i a instruc iunii if.

4.2.5. Operatori logici la nivel de bit


Ne rentoarcem la cei trei operatori de tip booleean & (AND, I), | (OR, SAU) i ~ (NOT) precum i la un al patrulea operator, denumit SAU-EXCLUSIV ^ (EXCLUSIVE-OR). Aceti operatori se aplic la nivel de bit sau grupuri de bi i, dup tabelele:
AND &
x

OR |
y 0 1

x 0 1 0 1 11

NOT ~
y 0 1 1 0

EXCLUSIVE-OR ^
y 0 1

x 0 1 0 1 1 0

0 1 y 0 0 0 1 0 1

n C, aceti operatori se aplic n paralel bi ilor corespunztori afla i n orice pozi ie. Din aceast cauz ei se mai numesc i operatori logici pe bit. Trebuie fcut distinc ia fa de operatorii logici, care folosesc nota ii dublate: &&, ||, sau !. Operatorii logici au aceleai denumiri, dar ei trateaz ntregul operator ca pe o singur valoare, adevrat sau fals. n scriere, se mai folosete i denumirea bit-and, bit-or, bit-negate sau exclusive-or. Ca exemplu, considerm opera ia bit-not. Fie numrul binar: N2 = 0000000000000111 = 0x0007 = 710 Negarea sa pe bit se realizeaz cu instruc iunea ~0x7 sau ~07 sau ~7 i valoarea sa va fi ~N2 = 1111111111111000 = 0xFFF8 sau 0177770 pe un computer cu ntreg pe 16 bi i sau 0xFFFFFFF8 pe un computer cu ntreg pe 32 de bi i. Exemplul urmtor realizeaz un SAU i un I pentru dou caractere: a | c = 0110 0001 | 0110 0011 = 0110 0011 = c a & c = 0110 0001 & 0110 0011 = 0110 0010 = a

65

Deoarece limbajul C a fost gndit s nlocuiasc limbajul de asamblare n majoritatea opera iilor de programare, acesta trebuie s aib capacitatea s suporte to i (sau cel pu in mul i) operatorii utiliza i n asamblare. Operatorii pentru prelucrarea bi ilor se aplic bi ilor dintr-un byte sau cuvnt, ambele variabile de tip char i short int. Aceti operatori nu se aplic tipurilor float, double, long double, void sau altor tipuri mai complexe. Operatorii pentru prelucrarea bi ilor utiliza i n C sunt: & | ^ ~ >> << AND OR exclusive OR (XOR) complement fa de unu (NOT) deplasare dreapta deplasare stnga

Opera iile pe bi i sunt utilizate de obicei n drivere, pentru testarea i mascarea anumitor bi i. De exemplu, opera ia AND poate fi folosit pentru tergerea unor bi i dintr-un byte sau dintr-un cuvnt, OR poate fi folosit pentru setarea unor bi i, iar XOR pentru complementarea unor bi i i testarea egalit ii a 2 bytes. Observa ie: Operatorii rela ionali i logici (&&, ||, !,...) produc totdeauna un rezultat care este fie 0, fie 1, pe cnd operatorii similari destina i prelucrrii bi ilor pot produce orice valoare arbitrar, n concordan cu opera ia specific. Operatorii >> i << deplaseaz to i bi ii dintr-o variabil la dreapta, respectiv la stnga. Forma general a opera iilor de deplasare este: variabil >> numr_de_pozi ii_bit - pentru deplasare dreapta. variabil << numr_de_pozi ii_bit - pentru deplasare stnga. n pozi iile rmase libere, dup deplasare, se introduc zerouri. Opera iile de deplasare pot fi utile cnd se decodific perifericele de intrare cum ar fi convertoarele D/A (digital/analogice) i cnd se citesc informa ii de stare. Operatorii de deplasare se pot utiliza i pentru realizarea cu rapiditate a opera iilor de nmul ire i mpr ire. Se tie c o deplasare stnga cu 1 bit realizeaz nmul irea cu 2, iar o deplasare dreapta cu 1 bit realizeaz o mpr ire cu 2. Exemplu:
x = 7; 0 0 0 0 0 1 1 1 7

66

x x x x x

<< << << >> >>

1; 3; 2; 1; 2;

0 0 1 0 0

0 1 1 1 0

0 1 0 1 0

0 1 0 0 1

1 0 0 0 1

1 0 0 0 0

1 0 0 0 0

0 0 0 0 0

14 112 192 96 24

Urmtorul exemplu eviden iaz efectul operatorilor de deplasare:


# include <stdio.h> void disp_binary(); /* prototipul functiei disp_binary() */ void main() { int i = 1, t; for (t=0;t<8;t++) { disp_binary(i); i=i<<1;} printf (" \n"); for (t=0;t<8;t++) { i=i>>1; disp_binary(i);}} void disp_binary(int i) /* se defineste functia disp_binary() */ /* care afiseaza bitii dintr-un byte */ {register int t; for (t=128;t>0;t=t/2) if (i&t) printf("1"); else printf("0"); printf("\n");}

Programul produce urmtoarea ieire:


0 0 . 1 1 0 . 0 0 0 . 0 0 1 . 0 0 0 . 0 0 0 . 0 0 0 . 0 0 0 . 0 0 0 . 0 0 0 . 0 0 0 . 0 0 0 . 0 0 1 . 0 0 0 . 0 1 0 . . . . . 0 0 0 . . . . . 1

Dei limbajul C nu con ine un operator de rotire, se poate realiza o func ie care s efectueze aceast opera ie. De exemplu rotirea la stnga cu o pozi ie a numrului 10101010 ne conduce la numrul 01010101 i se realizeaz dup schema:
1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1

67

O posibilitate de realizare a opera iei de rotire necesit utilizarea unei uniuni cu dou tipuri de date diferite. De exemplu utiliznd uniunea:
union rotate { char ch[1]; unsigned int i; } rot;

Urmtoarea func ie realizeaz o rotire cu 1 bit.


void rotate_bit(union rotate *rot) {rot->ch[1]=0; rot->i=rot->i<<1; if (rot->ch[1]) rot->i=rot->i|1;}

Att ntregul i ct i cele dou caractere ch[0] i ch[1] partajeaz primii doi octe i din cei 4 rezerva i de uniune. Numrul de rotit se introduce (pe 8 bi i) n ch[0]. Se rotete apoi ntregul i (deci se rotesc to i cei 4 octe i care i corespund). Se testeaz MSB al lui ch[0] care se gsete n urma rotirii n pozi ia LSB din ch[1]. Dac este 1, atunci se seteaz la 1 LSB din ch[0], realizndu-se astfel opera ia de rota ie. Un exemplu de program care s utilizeaze aceast func ie:
# include <stdio.h> union rotate { char ch[1]; unsigned int i; } rot; void disp_binary(); void rotate_bit(); void main() { register int t; rot.ch[0]=147; for (t=0;t<7;t++) { disp_binary(rot.i); rotate_bit(&rot);}} /* se defineste functia rotate_bit() */ void rotate_bit(union rotate *rot) {rot->ch[1]=0; rot->i=rot->i<<1; if (rot->ch[1]) rot->i=rot->i|1;} /* se defineste functia disp_binary() */ void disp_binary(int i) {register int t; for (t=128;t>0;t=t/2) if (i&t) printf("1"); else printf("0"); printf("\n");}

68

Acest program realizeaz rotirea numrului 14710=100100112 cu 6 pozi ii


10010011 00100111 01001110 10011100 00111001 01110010 11100100

Programul de mai sus func ioneaz pentru numere reprezentabile pe un octet (mai mici de 255). Dac dorim s facem o rotire pe doi octe i, atunci se poate modifica programul de mai sus dup cum urmeaz:
# include <stdio.h> union rotate { char ch[3]; unsigned int i; } rot; void disp_binary(); void rotate_bit(); void main() { register int t; rot.i=17843; for (t=0;t<7;t++) { disp_binary(rot.i); rotate_bit(&rot);}} /* se defineste functia rotate_bit() */ void rotate_bit(union rotate *rot) {rot->ch[2]=0; rot->i=rot->i<<1; if (rot->ch[2]) rot->i=rot->i|1;} /* se defineste functia disp_binary() */ void disp_binary(int i) {register int t; for (t=32768;t>0;t=t/2) if (i&t) printf("1"); else printf("0"); printf("\n");}

Operatorul " ~ " realizeaz complementul fa de 1. O utilizare interesant a complementului fa de 1 este aceea c ne permite s vedem setul caracterelor extinse implementate n calculator:
# include <stdio.h> # include <conio.h> void main() {char ch; do {ch = getch(); printf ("%c %c\n", ch, ~ch);} while (ch != 'q');}

69

4.2.6. Operatorul de atribuire


n C, operatorul de atribuire (asignare) este semnul egal (=). Valoarea expresiei din dreapta se atribuie variabilei din stnga operatorului "=". n C, forma:
suma = a + b + c;

trebuie privit ca o nou expresie, numit expresie de asignare. Valoarea ei este chiar valoarea expresiei din dreapta operatorului de atribuire. Dac ntr-o expresie se fac mai multe atribuiri, atunci evaluarea se face de la dreapta la stnga:
x = y = z = 0 este echivalent cu (x=(y=(z=0)));

O expresie de atribuire de forma x = x + 5 n care variabila din stnga apare imediat dup operatorul = se poate scrie ntr-o form compact de tipul x += 5, unde operatorul += este tot un operator de atribuire. Majorit ii operatorilor binari le corespund operatori de atribuire de forma "op = " unde op poate fi : +, -, *, %, <<, >>, &, ^ , Operatorii de atribuire (asignare) sunt:

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


Deci, o expresie de asignare de forma :
var = (var) op (expr)

unde var este o variabil i expr este o expresie, admite o reprezentare compact de forma:
var op= expresie

ntr-o form compact, ca mai sus, var este evaluat o singur dat.

4.2.7. Operatorul sizeof


Operatorul sizeof returneaz numrul de octe i necesar memorrii variabilei sau tipului datei care este operandul su. Dac sizeof opereaz asupra unui tip de date, atunci tipul trebuie s apar ntre paranteze. De exemplu, sizeof(char) va fi 1, sizeof(int) va fi 4 etc., deci rezultatul este un numr ntreg, fr semn. Fiierul standard stddef.h definete tipul size_t al rezultatului oferit de operatorul sizeof. Dac sizeof se aplic unui tablou, rezultatul este numrul total de octe i din tablou. Exemplul din programul urmtor prezint dimensiunea principalelor tipuri de date:
# include <stdio.h> # include <stddef.h>

70

void main(){ printf("\nTip printf("\nTip int)); printf("\nTip printf("\nTip printf("\nTip printf("\nTip printf("\nTip double));}

caracter pe %d octet",sizeof(char)); short int pe %d octeti",sizeof(short int pe %d octeti",sizeof(int)); long int pe %d octeti",sizeof(long int)); float pe %d octeti",sizeof(float)); double pe %d octeti",sizeof(double)); long double pe %d octeti\n", sizeof(long

n urma execu iei acestui program, se va afia (rezultatele depind de tipul de procesor sau de compilator):
Tip Tip Tip Tip Tip Tip Tip caracter pe 1 octet short int pe 2 octeti int pe 4 octeti long int pe 4 octeti float pe 4 octeti double pe 8 octeti long double pe 8 octeti

4.2.8. Operatorul ternar ?


Operatorul " ? " poate fi utilizat pentru a nlocui instruc iunea if / else avnd forma:
if (conditie) expresie1 else expresie2

Operatorul ternar " ? " necesit trei operanzi i are forma general:
Expr1 ? Expr2 : Expr3

unde Expr1, Expr2 i Expr3 sunt expresii. Se evalueaz expresia Expr1. Dac este adevrat, se evalueaz Expr2, care devine valoarea ntregii expresii. Dac Expr1 este fals, se evalueaz Expr3, iar valoarea acesteia devine valoarea ntregii expresii: Exemplu:
x = 10; y = x > 9 ? 100 : 200;

Cum 10 > 9, valoarea lui y va fi 100. Dac x ar fi mai mic dect 9, y va primi valoarea 200. Acelai program scris cu if /else va fi:
x = 10; if (x > 9) y = 100; else y = 200;

71

n alctuirea expresiilor din declara ia operatorului ternar " ? " pot fi folosite i func ii: Exemplu:
# include <stdio.h> f1(); f2(); // prototipurile functiilor f1() si f2() void main() { int t; printf (": "); scanf("%d",&t); // se introduce numarul intreg t t?f1()+f2(t): printf(" S-a introdus zero\n");} f1() {printf ("S-a introdus "); } f2(int n) {printf ("%d\n", n);}

Dac se introduce zero, atunci va fi apelat printf() i va afia " S-a introdus zero". Dac se introduce alt numr, atunci programul va executa att func ia f1(), ct i func ia f2().

4.2.9. Operatorul virgul


Operatorul virgul se utilizeaz ntr-un ir n care se introduc mai multe expresii. Astfel, instruc iunea:
x = (y = 3, y+1),

are c efect atribuirea valorii 4 variabilei x. Deci expresiile separate prin virgul sunt evaluate de la stnga la dreapta, prima expresie evaluat cptnd valoarea void. Dac se utilizeaz un operator de atribuire, valoarea atribuit variabilei din stnga operatorului de atribuire este valoarea ultimei expresii din dreapta, dup evaluare. Exemplu:
y = 10; x = (y = y - 5, 30 / y);

Variabila x va cpta valoarea 6. Observa ie Deoarece operatorul virgul are o preceden mai mic dect operatorul de atribuire, pentru ca atribuirile s se fac corect, trebuie utilizate paranteze.

4.2.10. Operatorul de for are a tipului sau de conversie explicit (expresie cast)
Adesea se dorete specificarea conversiei valorii unui operand spre un tip dat. Acest lucru este posibil folosind o construc ie de forma: (tip) operand Printr-o astfel de construc ie valoarea operandului se convertete spre tipul indicat n paranteze. n construc ia de mai sus (tip) se consider c este un operator unar. Acest operator este

72

cunoscut sub numele de operator de for are a tipului sau de conversie explicit. De cele mai multe ori ns este utilizat denumirea englez a operatorului i anume expresie cast. Exemplu: Presupunem c o func ie oarecare f are un parametru de tip double. Pentru ca aceast func ie s poat fi apelat cu un parametru int n (n este un parametru de tip ntreg) acesta trebuie mai nti convertit la tipul double. Acest lucru se poate realiza printr-o atribuire:
double x f(x=n)

Un alt mod mai simplu de conversie a parametrului ntreg spre tipul double este utilizarea unei expresii cast: f((double)n) Operatorul de for are a tipului fiind unar, are aceeai prioritate ca i ceilal i operatori unari ai limbajului C.

4.2.11. Operatorii parantez


Parantezele rotunde se utilizeaz fie pentru a include o expresie, fie la apelul func iilor. O expresie inclus n paranteze rotunde formeaz un operand. n acest mod se poate impune o alt ordine n efectuarea opera iilor, dect cea care rezult din prioritatea i asociativitatea operatorilor. Operanzii ob inu i prin includerea unei expresii ntre paranteze impun anumite limite asupra operatorilor. De exemplu, la un astfel de operand nu se pot aplica operanzii de incrementare i decrementare sau operatorul adres. Astfel construc iile:
(a-5+b)++ --(a+b) &(a*b)

sunt eronate. La apelul unei func ii, lista parametrilor efectivi se include ntre paranteze rotunde. n acest caz se obinuiete s se spun c parantezele rotunde sunt operatori de apel de func ie. Parantezele ptrate include expresii care reprezint indici. Ele se numesc operatori de indexare. Parantezele sunt operatori de prioritate maxim. Operatorii unari au prioritatea imediat mai mic dect parantezele.

4.2.12. Operatorul adres Operatorul adres este unar i se noteaz prin caracterul &. El se aplic pentru a determina adresa de nceput a zonei de

73

memorie alocat unei date. n forma cea mai simpl, acest operator se utilizeaz n construc ii de forma: &nume

unde nume este numele unei variabile simple sau al unei structuri. n cazul n care nume este numele unui tablou, atunci acesta are ca valoare chiar adresa de nceput a zonei de memorie alocat tabloului respectiv i, n acest caz, nu se mai utilizeaz operatorul adres &.

4.2.13. Al i operatori ai limbajului C


n limbajul C se mai utilizez i operatorii: * , . i -> Operatorul * unar (a nu se confunda cu operatorul aritmetic binar de nmul ire) se utilizeaz pentru a face acces la con inutul unei zone de memorie definit prin adresa ei de nceput. Se obinuiete s se spun c operatorul de adres & este operator de referen iere, iar operatorul * este operator de dereferen iere. Operatorii . i -> se utilizeaz pentru a se accesa componentele unei structuri. Ei au prioritate maxim, avnd aceeai prioritate cu parantezele.

4.2.14. Regula conversiilor implicite i preceden a operatorilor


Regula conversiilor implicite se aplic la evaluarea expresiilor. Ea ac ioneaz atunci cnd un operator binar se aplic la doi operanzi de tipuri diferite. n acest caz, operandul de tip inferior se convertete spre tipul superior al celuilalt operand i rezultatul este de tip superior. nainte de toate se convertesc operanzii de tip char i enum n tipul int. Dac operatorul curent se aplic la operanzi de acelai tip, atunci se execut operatorul respectiv, iar tipul rezultatului coincide cu tipul comun al operanzilor. Dac rezultatul aplicrii operatorului reprezint o valoare n afara limitelor tipului respectiv, atunci rezultatul este eronat (are loc o depire). Exemplu: Rezultatul mpr iirii 7/3 este 2 i nu 2.5 deoarece cei doi operanzi sunt de tip ntreg i prin urmare rezultatul (care este de tip real) este i el convertit la tipul ntreg. Dac operatorul binar se aplic la operanzi de tipuri diferite, atunci se face o conversie nainte de execu ia operatorului, conform algoritmului umtor: 1. Dac unul din operanzi este de tip long double, atunci cellalt operand se convertete spre tipul long double iar tipul rezultatului aplicrii operatorului este de asemenea de tip long double.

74

2. Altfel, dac unul din operanzi este de tip double atunci cellalt operand se convertete spre tipul double iar tipul rezultatului aplicrii operatorului este de asemenea de tip double. 3. Altfel, dac unul din operanzi este de tip float atunci cellalt operand se convertete spre tipul float iar tipul rezultatului aplicrii operatorului este de asemenea de tip float. 4. Altfel, dac unul din operanzi este de tip unsigned long atunci cellalt operand se convertete spre tipul unsigned long iar tipul rezultatului aplicrii operatorului este de asemenea de tip unsigned long. 5. Altfel, dac unul din operanzi este de tip long atunci cellalt operand se convertete spre tipul long iar tipul rezultatului aplicrii operatorului este de asemenea de tip long. 6. Altfel, unul din operanzi trebuie sa fie de tip unsigned, cellalt de tip int i acesta se convertete spre tipul unsigned, iar tipul rezultatului aplicrii operatorului este de tip unsigned.

Preceden ele operatorilor C sunt prezentate n tabelul urmtor. Operatorii afla i pe aceeai linie au aceeai prioritate. Ei se asociaz de la stnga la dreapta, exceptnd operatorii unari, condi ionali i de atribuire, care se asociaz de la dreapta la stnga.
Preceden a nalt Operatorul () [ ] -> . ! ~ ++ -- - (type) * & sizeof * / % + << >> < <= > >= == != & ^ | && || ?: = += -= *= /= ,

Sczut

75

Capitolul V INSTRUC IUNI

Limbajul C posed un set variat de instruc iuni, set care i permite s realizeze principalele compuneri de opera ii: secven ierea, repeti ia cu test final, repeti ia cu test ini ial, repeti ia cu numr cunoscut de pai, decizia i selec ia, saltul necondi ionat, ieirea prematur dintr-un ciclu. Instruc iunile pot fi clasificate n: instruc iuni etichetate, instruc iuni expresie, instruc iuni compuse, instruc iuni de selec ie, instruc iuni repetitive, instruc iuni de salt.

5.1. Instruc iuni etichetate (instruc iunea goto)


Instruc iunile etichetate posed etichete ca prefixe i au forma:
etichet: instruc iune

Eticheta format dintr-un identificator definete identificatorul ca destina ie pentru o instruc iune de salt, singura utilizare a sa fiind ca destina ie a unei instruc iuni goto. Etichetele nu pot fi redeclarate. Etichetele sunt locale n corpul func iei n care sunt definite. Instruc iunea goto are urmtorul format:
goto etichet

La ntlnirea instruc iunii goto, se realizeaz un salt la instruc iunea prefixat de eticheta aflat dup instruc iunea goto. Deoarece o etichet este local n corpul unei func ii rezult c ea este nedefinit n afara corpului func iei respective, deci, o instruc iune goto nu poate face salt la o instruc iune din afara corpului func iei n care este definit. Nu este recomandat utilizarea abuziv a acestei instruc iuni deoarece programul devine mai pu in lizibil i pot apare erori logice n program foarte greu de detectat. Instruc iunea goto se utilizeaz n special pentru ieirea din mai multe cicluri imbricate. Exemplu: Urmtorul program utilizeaz instruc iunea goto pentru a afia numerele de la 1 la 100:

76

#include <stdio.h> void main(void) { int nr=1; eticheta: printf(%d, nr++); if (nr<=100) goto eticheta; }

5.2. Instruc iuni expresie


Cele mai multe instruc iuni sunt instruc iunile expresie, care au forma: [ expresie ]; unde expresie este op ional. Majoritatea instruc iunilor expresie sunt atribuiri sau apeluri de func ii. Deci, o instruc iune expresie const dintr-o expresie urmat de caracterul ";".
Exemplu: a = b * c + 3; printf ("FAC. DE AUTOMATICA");

Dac expresia expresie de mai sus lipsete, construc ia ; se numete instruc iune vid. Aceasta nu are nici un efect, dar este utilizat pentru a nlocui un corp vid al unei itera ii sau pentru a plasa o etichet.

5.3. Instruc iuni compuse


O instruc iune compus este o posibil list de declara ii i/sau instruc iuni nchise ntre acolade. Exemplu:
{ a = b + 2; b++; }

O instruc iune compus se numete bloc. Un bloc ne permite s tratm mai multe instruc iuni ca pe una singur. Corpul unei func ii este o instruc iune compus. Domeniul de vizibilitate al unui identificator declarat ntr-un bloc se ntinde din punctul declara iei pn la sfritul blocului. Identificatorii utiliza i ntr-un bloc pot fi ascuni prin declara ii de acelai nume n blocurile interioare blocului ini ial. Exemplu:
# include <stdio.h> int x = 34; /* x este global */ void main(void) { int *p = &x; /*p preia adresa variabilei globale*/ int x1, x2; printf("x = %d\n", x);

77

{int x; /*x este local si il ascunde pe cel global */ x = 1; /* atribuirea se face la cel local */ x1 = x; printf("x = %d\n", x1);} { int x; /* se ascunde prima variabila locala */ x = 2; /* se atribuie valoarea 2 acestui x */ x2 = x; printf("x = %d\n",x2); } printf("x = %d %d %d \n",x,x1,x2); }

5.4. Instruc iuni de selec ie


5.4.1. Instruc iunea if
O instruc iune if cu care n C se implementeaz o structur de control de selec ie sau o structur alternativ, are urmtorul format general:
if (conditie) instructiune1; else instructiune2;

unde conditie este orice expresie care prin evaluare conduce la o valoare ntreag. Dac valoarea expresiei este diferit de zero (condi ie adevrat), atunci se execut instructiune1; altfel, dac valoarea expresiei este zero (condi ie fals), se execut instructiune2. n ambele cazuri, dup executarea lui instructiune1 sau instructiune2, controlul este transferat la instruc iunea ce urmeaz dup if. Aici, prin instructiune1 sau instructiune2 se n elege o instruc iune simpl, o instruc iune compus (un bloc) sau o instruc iune vid. Por iunea else instructiune2; este op ional, n acest fel putnduse ob ine o structur de selec ie cu o ramur vid de forma:
if (conditie) instructiune;

Exemplu: Urmtorul program citete dou numere i afieaz pe cel mai mare dintre ele.
# include <stdio.h> void main (void) { int x, y; printf("Introduceti doua numere intregi: \n"); scanf ("%d %d", &x, &y); if (x > y) printf ("Cel mai mare este : %d\n",x); else printf("Cel mai mare este : %d\n", y); }

Deoarece partea else dintr-o instruc iune if este op ional, apare o ambiguitate atunci cnd else este omis dintr-un if inclus (ncuibat).

78

n C acest lucru se rezolv prin asocierea lui else cu cel mai apropiat if. De exemplu, n secven a:
if (x) if (y) else printf ("1"); printf ("2");

else este asociat cu instruc iunea if(y). Dac dorim ca else s fie asociat cu if(x) trebuie s utilizm acolade, astfel:
if (x) { if (y) printf ("1"); } else printf ("2");

Secven a anterioar este echivalent cu:


if (x) { if (y) printf ("1"); else ;} else printf ("2");

5.4.2. Instruc iuni de selec ie multipl: if - else if


ntr-o instruc iune if se poate include, pe o ramur, o alt instruc iune if. n acest fel se creeaz posibilitatea de a codifica structuri de selec ie multipl, folosindu-se perechi else if. O asemenea construc ie este de forma:
if (conditie1) instructiune1; else if (conditie2) instructiune2; else if (conditie3) instructiune3; . . . . . . . . . . . . . . . . else if (conditieN) instructiuneN; else instructiuneN+1;

n acest caz, condi iile sunt testate n ordine. Dac una din ele este adevrat, atunci este executat instruc iunea corespunztoare, dup care controlul este transferat la instruc iunea urmtoare din program. Codul pentru fiecare alternativ poate fi format dintr-o instruc iune simpl (inclusiv instruc iunea vid) sau dintr-un bloc delimitat prin { i }. Dac nici una dintre expresii nu este adevrat, atunci se execut secven a corespunztoare ultimei alternative introdus prin else. Aceast ultim alternativ nu este obligatorie, structura putndu-se ncheia dup secven a notat cu instructiuneN.

79

Exemplu: Considerm un program care realizeaz conversiile inch-cm i cm-inch. Presupunem c indicm unitatea intrrii cu i pentru inch i c pentru centimetru:
# include <stdio.h> # include <conio.h> void main(void) { const float fact = 2.54; float x,in,cm; char ch = 0; printf ("\nIntroduceti numarul: \n"); scanf("%f",&x); printf("\nIntroduceti unitatea: \n"); ch = getche(); /* se introduce un caracter de la tastatura care se afiseaza pe ecran */ if (ch == 'i') { in = x; cm = x * fact;} else if(ch == 'c') { in = x/fact; cm = x; } else in = cm = 0; printf("\n%5.2f in = %5.2f cm \n",in,cm); }

5.4.3. Instruc iunea switch


ntr-o instruc iune de selec ie switch, se compar, pe rnd, o valoare cu constantele dintr-o mul ime i n momentul gsirii unei coinciden e se execut instruc iunea sau blocul de instruc iuni asociate acelei constante. Forma general a instruc iunii switch este:
switch (variabila) { case constanta1 : secventa_instructiuni_1 break; case constanta2 : secventa_instructiuni_2 break; case constanta3 : secventa_instructiuni_3 break; . . . . . . . . . . . . . . . . . . . case constantaN : secventa_instructiuni_N break; default : secventa_instructiuni_N+1 }

80

Instruc iunea switch realizeaz transferul controlului la una din secven ele de instruc iuni dac valoarea variabila ce trebuie s aib tipul ntreg coincide cu una din constantele de dupa case. Secven a de instruc iuni se execut pna se ntlnete break, dup care se trece la instruc iunea imediat urmtoare dup switch. Dac nu se gsete nici o coinciden , se execut secven a de instruc iuni de dup default, iar dac default lipsete, deoarece prezen a acesteia este op ional, se trece la instruc iunea urmtoare.

Exemplu: Decizia din exemplul anterior poate fi realizat i astfel:


# include <stdio.h> # include <conio.h> void main(void) { const float fact = 2.54; float x, in, cm; char ch = 0; printf ("\nIntroduceti numarul: \n"); scanf("%f", &x); printf("\nIntroduceti unitatea: \n"); ch = getche(); switch(ch) { case 'i': in = x; cm = x * fact; break; case 'c': in = x/fact; cm = x; break; default: in = cm = 0; break; } printf("\n%5.2f in = %5.2f cm \n",in,cm); }

Observa ie: Constantele case trebuie sa fie distincte. Pentru a iei din instruc iunea switch se folosete instruc iunea break. Exemplu:
# include <stdio.h> void main (void) { int t; for (t = 0; t < 10; t++) switch (t) { case 1 : printf ("Now"); break; case 2 : printf (" is "); break; case 3 :

81

case 4 : printf (" printf (" break; case 5 : case 6 : printf (" break; case 7 : case 8 : case 9 : printf (" break; }

the "); time for all good men \n");

to ");

. "); }

Rulnd acest program, vom ob ine:


Now is the time for all good men the time for all good men to to . . .

Instruc iunea switch este foarte eficient n scrierea programelor care afieaz pe ecran o list de op iuni (un meniu) din care utilizatorul alege cte una i o execut. Instruc iunile switch pot fi i incluse (ncuibate) una n alta.

5.5. Instruc iuni repetitive


5.5.1. Instruc iunea for
Forma general a instruc iunii for este:
for (initializare; conditie; incrementare) instructiune;

- initializare este o instruc iune de atribuire utilizat pentru ini ializarea variabilei de control a ciclului. Nu exist nici o restric ie privitoare la tipul su; - condi ie este o expresie rela ional care se testeaz naintea fiecrei itera ii: dac condi ia este adevrat (diferit de 0), ciclul se continu; dac condi ia este fals (egal cu 0), instruc iunea for se ncheie; - incrementare se evalueaz dup fiecare itera ie specificnd astfel reini ializarea ciclului. Exemplu: Urmtorul program afieaz pe ecran numerele de la 1 la 100.
# include <stdio.h> void main (void) { int x; for (x = 1; x <= 100; x++) printf("%d ", x); }

unde:

82

Nu ntotdeauna ciclul for trebuie s se desfoare n sensul creterii variabilei de control. Putem crea cicluri for n care variabila de control se decrementeaz. Exemplu: Programul urmtor afieaz numerele de la 100 la 1.
# include <stdio.h> void main (void) { int x; for (x =100; x > 0; x--) printf("%d", x);

Nu exist restric ii n incrementarea sau decrementarea variabilei de control a ciclului. Exemplu: Urmtorul program afieaz pe ecran numerele de la 0 la 100 din 5 n 5:
# include <stdio.h> void main (void) { int x; for (x = 0; x <= 100; x = x + 5) printf ("%d", x); }

Instructiunea instruc iune din declara ia ciclului for poate fi o instruc iune simpl sau un bloc (un grup de instruc iuni delimitate de acolade) care va fi executat repetitiv. Exemplu: Programul urmtor afieaz pe ecran numerele de la 0 la 99, precum i ptratul acestora:
# include <stdio.h> void main (void) { int i; for (i = 0; i < 100; i++) { printf (" Acesta este i : %3d", i); printf (" si i patrat : %5d \n", i*i); } }

Exemplu: Calculul factorialului unui numr: n! = 123...n


# include <stdio.h> void main (void) { int n, i; long int factorial; printf ("Introduceti n : "); scanf ("%d", &n); factorial = 1; for (i = 1; i <= n; i++) factorial *= i; printf (" %d ! = %ld \n", n, factorial);

Instruc iunile for pot fi incluse una n alta. Exemplu: Programul urmtor parcurge un ir de caractere de la stnga la dreapta, afind subirurile ce au ca baz primul caracter.
# include <stdio.h> # include <string.h> void main (void)

83

int l, n, i; char sir[81]; puts ("Tastati un sir terminat cu <CR> : "); gets (sir); l = strlen (sir); for (n = 0; n <= l; ++n) { for (i = 0; i < n; ++i) putchar (sir[i]); putchar ('\n'); } }

Variante ale ciclului for: Limbajul C permite mai multe variante ale ciclului for care determin creterea flexibilit ii acestuia n diferite situa ii. Una din cele mai utilizate variante const n folosirea a mai multe variabile de control a ciclului. n exemplul urmtor, att x ct i y sunt variabile de control a ciclului:
# include <stdio.h> void main (void) { int x, y; for (x = 0, y = 0; x + y < l00; x++, y++) printf ("%d ", x + y); }

Acest program tiprete numerele de la 0 la 98 din 2 n 2. Se observ c ini ializrile i incrementrile celor dou variabile sunt separate prin virgul. Terminarea ciclului presupune testarea nu numai a variabilei de control cu anumite valori prestabilite, ci condi ia de terminare a ciclului poate fi orice expresie C corect. Exemplu: Considerm un program pentru antrenarea unui copil n exerci iile de adunare. Dac copilul vrea s se opreasc se apesa tasta T, atunci cnd calculatorul l ntreab dac s continue.
# include <stdio.h> # include <conio.h> void main (void) { int i, j, raspuns; char terminare = ' '; for (i=1; i<100; i++) { for (j=1; j<100 && terminare !='t';j++) { printf ("\nCit este %d + %d ? ",i,j); scanf("%d",&raspuns); if (raspuns != i+j) printf("\nGresit !"); else printf("\nCorect !"); printf(" Continuam ? "); terminare = getchar(); } /* Pentru terminare se apasa tasta t */ } }

84

O alt caracteristic interesant a ciclului for este aceea c nu se impune definirea tuturor celor trei parametri ai ciclului for, oricare dintre ei putnd fi op ionali. De exemplu, ciclul urmtor se va executa pn cnd de la tastatura se introduce numrul 123:
for (x = 0; x != 123;) scanf("%d", &x);

Deoarece instruc iunea de incrementare a lui x lipsete, de fiecare dat cnd ciclul se repet, programul testeaz ca x s fie egal cu 123, dar nu modific pe x n nici un fel. Dac de la tastatur se introduce 123, condi ia buclei devine fals i ciclul se termin. Exemplu: O variant de calcul a lui n! ar fi urmtoarea:
# include <stdio.h> void main (void) { int n, i; long int factorial; printf ("Introduceti n : "); scanf ("%d", &n); factorial = 1; for (i = 1; i <= n;) { factorial *= i ++; printf (" %d ! = %ld \n", n, factorial);

} }

Bucle infinite: Una din cele mai interesante utilizri ale ciclului for const n crearea de bucle infinite. Dac nici una din cele trei expresii care formeaz ciclul for nu sunt precizate, se ob ine o bucl fr sfrit, ca n exemplul urmtor n care se consider c elementul condi ie are valoarea adevrat:
for (;;) printf ("Aceasta bucla va rula la nesfirsit. \n ");

Ieirea dintr-o bucla for: Pentru terminarea unei bucle for, chiar i a buclei for(; ;) se folosete instruc iunea break care se plaseaz oriunde n corpul ciclului i determin ncheierea imediat a ciclului (la ntlnirea acesteia), programul continundu-se cu instruc iunea ce urmeaz dup instruc iunea for.

Exemplu: Acest program va rula pn cnd de la tastatura se apas tasta A:


# include <stdio.h> # include <ctype.h> void main (void) { for (; ;) { ch = getche (); if (ch == 'a') break; } printf (" Ai apasat tasta A "); }

85

Utilizarea ciclurilor for fr corp (instruc iune) : Pentru crearea unor ntrzieri de timp se pot folosi cicluri for cu corp vid de forma:
for (t = 0; t < O_ANUMITA_VALOARE; t++);

Observa ie: De obicei, instruc iunea for este legat de parcurgerea unor structuri de date de tip tablou.

5.5.2. Instruc iunea while


Forma general a instruc iunii repetitive while este:
while (conditie) instructiune;

unde instructiune poate fi o instruc iune vid, o instruc iune simpl sau un bloc de instruc iuni ce vor fi executate repetitiv. n timpul execu iei se evalueaz mai nti condi ia buclei a crei valoare trebuie s fie ntreag. Dac valoarea calculat este diferit de 0 (condi ie adevrat), atunci instructiune se execut. Dac, dup o evaluare (inclusiv prima) rezult o valoare 0 (condi ie fals), atunci controlul este transferat la instruc iunea ce urmeaz dup while. Astfel, instruc iunea asociat cu while se execut repetat, ct timp valoarea asociat condi iei este diferit de 0 sau condi ia este adevrat. Exemplu: Programul urmtor calculeaz c.m.m.d.c. pentru o pereche x, y de numere ntregi pozitive.
# include <stdio.h> void main (void) { int xi, yi, x, y; printf (" Introduceti doua numere pozitive: \n"); scanf ("%d %d", &xi, &yi); x = xi; y = yi; while (x != y) if (x > y) x -= y; else y -= x; printf (" C.m.m.d.c. (%d, %d) = %d", xi, yi, x); }

Metoda de calcul se bazeaz pe faptul c: daca x > y, atunci cmmdc (x, y) = cmmdc (x-y, x); daca x < y, atunci cmmdc (x, y) = cmmdc (x, y-x); daca x = y, atunci cmmdc (x, y) = x =y . De exemplu, cmmdc (14, 21) = 7. Deoarece instruc iunea while realizeaz testarea condi iei la nceputul instruc iunii, aceasta instruc iune este bun de utilizat n situa iile n care nu se dorete execu ia buclei, evident dac condi ia nu este adevrat.

86

Exemplu: Programul urmtor realizeaz centrarea unui text pe ecran:


# include <stdio.h> # include <ctype.h> void main (void) { char sir[255]; printf(" Introduceti un sir de caractere: \n"); gets (sir); centreaza (strlen (sir)); printf (sir); } /* Se calculeaz numrul de spa ii pentru unui ir de caractere cu lungimea lung */ centreaza (lung) int lung; { lung = (80 - lung)/2; while (lung > 0) { printf (" "); lung--; } }

centrarea

Dac dorim s programm un ciclu infinit, atunci se poate gsi o expresie care ramne tot timpul adevrat. Un exemplu uzual este urmtorul:
while (1) { Corpul ciclului }

Ieirea din ciclu, n acest caz, se asigur prin mecanisme de tip break, goto sau return. Corpul ciclului while poate con ine i numai instruc iunea vid. De exemplu,
while ((ch = getche ()) != 'A');

este o bucl simpl care se execut pn cnd de la tastatur se va introduce caracterul "A". Observa ie: Instruc iunea while reprezint mecanismul sintactic de baz pentru a programa cicluri n C. Reamintim c instruc iunea for se folosete dup urmtorul format general: for (initializare; conditie; incrementare) instructiune; care este echivalent semantic cu secven a: initializare; while (conditie) { instructiune; incrementare; }

5.5.3. Instruc iunea do-while


Spre deosebire de ciclurile programate cu while sau for, unde condi ia de ciclare este verificat la nceput, n cazul folosisii mecanismului do-while, condi ia se evalueaz dup execu ia secven ei

87

de instruc iuni ce reprezint corpul ciclului. Forma general a buclei do-while este:
do { instructiune; } while (conditie);

Semantic, do-while este echivalent cu secven a:


instructiune; while (conditie) instructiune;

Dei acoladele nu sunt necesare cnd instructiune este o instruc iune simpl, de obicei se utilizeaz pentru a evita confuzia cu while. Se remarc faptul c instructiune ce reprezint corpul ciclului (adic, o instruc iune simpl, o instruc iune compus sau o instruc iune vid) este executat cel pu in odat. Celelalte execu ii sunt condi ionate de valoarea ntreag rezultat din evaluarea condi iei. Dac aceast valoare este 0 (condi ie fals), atunci controlul se transfer la urmtoarea instruc iune din program; n caz contrar se execut corpul ciclului i se reevalueaz condi ia. Exemplu: Urmtoarea secven asigur preluarea corect a unei valori ntregi ntre 1 i 10:
# include <stdio.h> void main (void) { int num; do { printf("\n\nIntrod. un intreg ntre 1 si 10: "); scanf ("%d", &num); printf (" Numarul introdus este : %d ", num); } while (num < 1 || num > 10); }

Un caz tipic de utilizare a instruc iunii do-while este oferit de programele interactive n care selec ia unei op iuni se face pe baza unui meniu afiat pe ecranul terminalului. Exemplu: Urmtorul program implementeaz o versiune a unui meniu de verificare a corectitudinii ortografice ntr-un text:
# include <stdio.h> # include <ctype.h> void main (void) { char ch; printf ("1. Verificarea ortografiei \n "); printf ("2. Corectarea erorilor de ortografie \n"); printf ("3. Afisarea erorilor de ortografie \n "); do { printf ("\n Introduceti optiunea dumneavoastra: "); ch=getche(); // Se citeste optiunea de la tastatura switch (ch) { case '1':

88

verifica_ortografia(); break; case '2': corecteaza_erorile(); break; case '3': afiseaza_erorile(); break; } } while (ch != '1' && ch != '2' && ch != '3'); }

Dup afiarea op iunilor, programul va bucla pn cnd se va selecta o op iune valid. Exemplu: Adunarea elementelor a doi vectori:
int a[10], b[10], c[10]; . . . . . . . . . . . . . . i = 0; do { c[i] = a[i] + b[i]; i = i + 1; } while (i < 10);

sau
i = 0; do { c[i] = a[i] + b[i]; i++; } while (i < 10);

5.5.4. Bucle ncuibate


Cnd o bucl este introdus n alt bucl, bucla interioar se spune a fi inclus (nested, ncuibat) n bucla exterioar. Exemplu: Programul urmtor afieaz primele 4 puteri ale numerelor cuprinse ntre 1 i 9:
# include <stdio.h> void main (void) { int i, j, k, p; printf (" i i^2 i^3 i^4 \n "); for (i = 1; i < 10; i++) { for (j = 1; i < 5; j++) { p = 1; for (k = 1; i < j; k++) p = p * i; printf (" %9d ", p); } printf (" \n "); } }

Cnd se execut acest program se ob in urmtoarele rezultate:


i i^2 i^3 i^4 1 1 1 1 2 4 8 16 3 9 27 81 . . . . . . . . . . . 9 81 729 6561

89

Alinierea rezultatelor se datorete utilizrii n printf() a unui format de afiare corespunztor (%9d) care precizeaz dimensiunea minim a cmpului specificat. Un alt exemplu, pu in mai complex, este un program de nmul ire a dou matrice. Evident, n acest caz vom avea 3 bucle for incluse una n cealalt. // Program de inmultire a doua matrici
# include <stdio.h> float a[100][100],b[100][100],c[100][100]; float elem, s; int la, ca, lb, cb, lc, cc, i, j, k; void main(void) { la=101; ca=101; lb=ca+1; cb=ca; printf("Program de inmultire a doua matrici\nSe declara dimensiunile fiecarei matrici\n\n"); /* Introducem pe rand dimensiunile fiecarei matrici. Verificam sa nu se depaseasca dimensiunile maxime si verificam posibilitatea inmultirii matricilor */ while (ca!=lb){ printf("Se verifica daca dimensiunile declarate sunt compatibile pentru inmultire!\n\n"); while ((la>=100)||(ca>=100)) { printf("Intoduceti dimensiunile primei matrice"); printf("\nNr. linii matrice A = \n"); scanf("%d",&la); printf("Nr. coloane matrice A = \n"); scanf("%d",&ca); } while ((lb>=101)||(cb>=101)) { printf("Intoduceti dimens. celei de-a doua matrice"); printf("\nNr. linii matrice B = \n"); scanf("%d",&lb); printf("Nr. coloane matrice B = \n"); scanf("%d",&cb); } if(ca!=lb) { la=101;ca=101; lb=ca+1;cb=ca;} } /* Se introduc matricile */ for(i=0; i<=la-1; i++) for(j=0; j<=ca-1; j++) { printf("a(%d,%d) = ", i, j); scanf("%f",&elem); a[i][j] = elem; } for(i=0;i<=lb-1;i++) for(j=0;j<=cb-1;j++) { printf("b(%d,%d) = ",i,j); scanf("%f",&elem);

90

b[i][j]=elem; } // Se calculeaza fiecare element al matricei produs for(i=0;i<=la-1;i++) for(j=0;j<=cb-1;j++) { s=0; for(k=0;k<=ca-1;k++) s = s+a[i][k]*b[k][j]; c[i][j] = s; } // Se afisaza matricile printf("\n\nA = \n"); for(i=0;i<=la-1;i++) { printf("\n"); for(j=0;j<=ca-1;j++) printf("%6.3f ",a[i][j]); } printf("\n\nB = \n"); for(i=0;i<=lb-1;i++) { printf("\n"); for(j=0;j<=cb-1;j++) printf("%6.3f ",b[i][j]); } printf("\n\nC = A*B\n"); for(i=0;i<=la-1;i++) { printf("\n"); for(j=0;j<=cb-1;j++) printf("%6.3f ",c[i][j]); }}

5.5.5. Instruc iunea break


Instruc iunea break are dou utilizri. Prima utilizare const n terminarea unui case n cadrul instruc iunii switch. A doua utilizare const n terminarea imediat a unui ciclu scurtcircuitnd testul condi ional normal al buclei. Dac ntr-o bucl se ntlnete o instruc iune break, calculatorul termin (prsete) imediat bucla i controlul programului se transfer la instruc iunea ce urmeaz instruc iunii de buclare. De exemplu, programul:
# include <stdio.h> void main (void) { int t; for (t = 0; t < 100; t++) { printf (" %3d ", t); if (t == 10) break; }}

tiprete numerele pn la 10 i atunci se oprete deoarece break determin ieirea imediat din ciclu. n cazul buclelor incluse, este important de notat ca break determin ieirea imediat numai din bucla interioar (din bucla n care este introdus). De exemplu, programul:
# include <stdio.h> void main (void)

91

int t; for (t = 0; t < 100; ++t) { count = 1; for (;;) { printf (" %d ", count); count++; if (count == 10) break; } }

va afia pe ecran numerele de la 1 la 10 de 100 de ori. Instruc iunea break se poate utiliza i n cadrul ciclurilor programate cu while sau do-while, schema general de utilizare fiind urmtoarea: while (expresie) { ................. if (conditie) break; ................. } Dac la una din itera ii, condi ia din if este ndeplinit, atunci ciclul se termin automat, altfel el poate continua pn cnd expresia din while are valoarea fals. Dac instruc iunea break se execut n cadrul unei instruc iuni switch, care la rndul ei este inclus ntr-un ciclu programat cu while, for sau do-while, atunci ea determin terminarea numai a instruc iunii switch, nu i ieirea din ciclu.

5.5.6. Instruc iunea continue


Instruc iunea continue, executat ntr-un ciclu, determin oprirea itera iei curente i asigur trecerea imediat la itera ia urmtoare. De exemplu, programul urmtor va afia pe ecran numai numerele pare.
# include <stdio.h> void main (void) { int x; for (x = 0; t < 100; x++) { if (x % 2) continue; printf (" %d ", x); } }

Se observ c atunci cnd se genereaz un numr impar se execut instruc iunea continue ce va determina trecerea la itera ia urmtoare by-pasnd instruc iunea printf(). n cazul instruc iunilor while i do-while, o instruc iune continue determin trecerea direct la testul condi ional i prin urmare, continuarea procesului de buclare. n cazul unui for, se realizeaz mai nti opera ia de incrementare a variabilei de control a ciclului, apoi testarea condi iei de continuare a buclei.

92

Capitolul VI TIPURI DE DATE STRUCTURATE


n C exist dou categorii de tipuri de date structurate: tablourile i structurile. Un tablou este o colec ie omogen de valori de acelai tip identificate printr-un indice, iar o structur este o colec ie neomogen de valori identificate prin nume simbolice, denumite selectori.

6.1. Tablouri unidimensionale


Un tablou este o colec ie de variabile de acelai tip care sunt referite printr-un nume comun. n C, un tablou const din loca ii de memorie contigue. Adresa cea mai mic corespunde primului element, iar adresa cea mai mare corespunde ultimului element. Un tablou poate avea de la una la mai multe dimensiuni. Accesul la un element specific al tabloului se face utiliznd un index. Cel mai utilizat tablou este tabloul de caractere. irurile de caractere pot fi definite prin conceptele: vector de caractere i pointer-caracter. Declararea unui tablou cu o singur dimensiune are urmtoarea form general: tip var_nume[size]; Aici, tip, declar tipul de baz al tabloului. Tipul de baz determin tipul de dat al fiecrui element al tabloului. var_nume este numele tabloului, iar size este numrul elementelor pe care le va con ine tabloul. Exemple: int a[10]; // vectorul a contine 10 intregi float v[3]; // vectorul v contine 3 reali n C toate tablourile folosesc pe zero ca index al primului lor element. Elementele tabloului a[10] sunt a[0],...,a[9]. Exemplu: Programul urmtor ncarc un tablou de ntregi cu numerele de la 0 la 9:
void int int for (t main (void) { x[10]; // se rezerva 10 elemente intregi t; = 0; t < 10; t++) x[t] = t; }

93

Pentru un tablou unidimensional, dimensiunea total, n bytes, a acestuia va fi: Total bytes = sizeof (tip) * lungimea_tabloului

Observa ie: Limbajul C nu realizeaz verificarea dimensiunilor unui tablou: astfel, nu exist nimic care s ne opreasc s nu trecem peste sfritul tabloului. Dac se trece peste sfritul unui tablou ntr-o opera ie de atribuire, atunci se vor atribui valori unor alte variabile sau chiar se vor distruge pr i din program. Exemplu: Dei urmtorul program este incorect, compilatorul C nu semnaleaz nici o eroare:
void main (void) { int crash[10], i; for (i = 0; i < 100; i++) crash[i] = i; }

Se observ c bucla se itereaz de 100 de ori, dei vectorul crash con ine numai 10 elemente. Aceste verificri rmn n sarcina exclusiv a programatorului. Tablourile unidimensionale sunt, de fapt, liste de informa ii de acelai tip. De exemplu, prin rularea programului:
char ch[7]; void main (void) { int i; for (i = 0; i < 7; i++) ch[i] = 'A' + i; }

vectorul ch arat astfel: ch(0) ch(1) ch(2) ch(3) ch(4) ch(5) ch(6) A B C D E F G

6.1.1. Constante ir
n C o constant ir este o secven de caractere nchis ntre ghilimele. Exemplu: "acesta este un sir". Fiecare constant ir con ine cu un caracter mai mult dect numrul de caractere din ir, deoarece aceasta se termin totdeauna cu caracterul NULL '\0' care are valoarea 0. De exemplu, sizeof ("asaf") = 5. Tipul unui ir este "vector de un numr de caractere"; astfel "asaf" are tipul char[5]. irul vid este descris prin " " i are tipul char[1]. De notat c, pentru fiecare ir s, func ia strlen(s) din fiierul antet "string.h" ntoarce numrul caracterelor din ir fr terminatorul 0, adic: strlen(s) = sizeof(s) - 1. n interiorul unui ir se poate folosi conven ia de nota ie cu \. Aceasta face posibil reprezentarea caracterelor " i \ n interiorul unui

94

ir. Cel mai frecvent caracter folosit este caracterul '\n' = new line (NL). De exemplu, instruc iunea: printf("beep at end of message \007 \n "); determin scrierea unui mesaj, a caracterului BEL i a caracterului NL. Nu este permis continuarea irurilor de caractere de pe o linie pe alta.
Exemplu: "this is not a string but a syntax error".

O secven de forma \n ntr-un ir nu determin introducerea unui caracter NL n ir, ci este o simpl nota ie. Este posibil s folosim caracterul null ntr-un ir, dar majoritatea programelor nu testeaz dac mai sunt caractere dup el.

6.1.2. Ini ializarea vectorilor de caractere Citirea unui ir de la tastatur utiliznd func iile scanf() i gets(). o Utilizarea func iei scanf(). Exemplu:
# include <stdio.h> void main (void) { char nume[21], adresa[41]; printf ("\n Nume: "); scanf ("%s", nume); printf ("\n Adresa: "); scanf ("%s", adresa); printf ("%s\n%s\n", nume, adresa); }

S-au definit variabilele nume i adresa ca tip ir de caractere de maximum 20 i 40 de caractere. irul "%s" care apare n apelul func iei scanf() precizeaz c se vor citi n variabilele nume, respectiv adresa, valori de tip ir de caractere. n printf() descriptorul "%s" are rolul de a preciza cum trebuie convertite valorile datelor de afiat (n cazul de fa , valorile variabilelor nume i adresa). Func ia scanf() citete un ir de caractere din bufferul de intrare pn cnd ntlnete un spa iu, un caracter TAB, sau ajunge la sfritul acestuia. Astfel, dac se tasteaz, "ENE ALEXANDRU", atunci n variabila nume se va memora doar valoarea "ENE". Pentru a ob ine irul n ntregime este recomandat s se transmit numele sub forma: "ENE_ALEXANDRU".

o Cea mai bun cale de a introduce un ir de la tastatur const n utilizarea func iei gets() din fiierul antet "stdio.h". Forma general a func iei gets() este: gets (nume_vector)
95

Pentru a citi un ir se apeleaz gets() avnd ca argument numele vectorului, fr nici un index. Func ia gets() returneaz vectorul ce va pstra irul de caractere introdus de la tastatur. gets() va continua s citeasc caractere pn la introducerea caracterului CR. Exemplu: Programul urmtor afieaz irul de caractere introdus de la tastatur. # include <stdio.h>
void main (void) { char sir[80]; gets (sir); /* citeste un sir de la tastatura */ printf ("%s", sir); }

Se observ c func ia printf() poate primi ca argument un ir de caractere. Dac se introduce un ir mai lung dect dimensiunea tabloului, vectorul va fi suprascris.

Ini ializarea vectorilor de caractere utiliznd constantele ir


Folosind constantele ir, vectorii de caractere se ini ializeaz sub forma: char nume_vector[size] = "sir_de_caractere" unde size = numrul caracterelor din ir plus 1. Exemplu:
# include <stdio.h> void main (void) { char nume[14] = "ENE ALEXANDRU"; char adresa[24] = "Str. A. I. Cuza, nr.13"; puts (nume); puts (adresa); }

Vectorul nume va ocupa ncepnd de la adresa nume, 13 + 1 = 14 octe i, iar cel de-al doilea vector va ocupa ncepnd de la adresa adresa, 23 + 1 = 24 loca ii (bytes). Func ia puts() scrie pe stdout irul memorat n vectorul al crui nume apare ca parametru al func iei puts(), precum i caracterul "\n". De multe ori, n C se realizeaz ini ializarea unor vectori de caractere a cror dimensiune nu este precizat. Dac dimensiunea vectorului nu este precizat, compilatorul C va crea un vector suficient de lung nct s permit ini ializarea dorit. Exemplu: n loc s scriem :
char e1[12] = "read error\n"; char e2[13] = "write error\n"; char e3[18] = "cannot open file\n";

putem scrie:
char e1[ ] = "read error\n"; char e2[ ] = "write error\n";

96

char e3[ ] = "cannot open file\n";

Cu aceast ultim ini ializare, instruc iunea


printf ("%s are lungimea %d\n", e2, sizeof (e2));

va tipari:
write error are lungimea 13

o Ini ializarea unui vector (tablou unidimensional) se poate face i cu o list de ini ializatori scrii ntre acolade. Dac vectorul are o lungime necunoscut, numrul de ini ializatori determin mrimea tabloului, iar tipul devine complet. Dac tabloul are lungime fix, numrul de ini ializatori nu poate depi numrul de membri din tablou. n cazul n care sunt mai pu ini ini ializatori, membrii n plus sunt ini ializa i cu zero. Exemple: - Instruc iunea urmtoare ini ializeaz un vector de 10 ntregi cu numerele de la 1 la 10:
int i[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

Rezult c: i[0] = 1, ... , i[9] = 10. - Instruc iunea urmtoare declar i ini ializeaz vectorul x ca un tablou unidimensional cu 3 membri:
int x[] = {1, 2, 3};

- Instruc iunea urmtoare:


char sir[6] = { 'h', 'e', 'l', 'l', 'o', '\0' };

este echivalent cu:


char sir[6] = "hello";

6.1.3. Func ii pentru prelucrarea irurilor (fiierul antet string.h)


Func ia strcpy() Apelul func iei strcpy() are urmtoarea form general: strcpy (nume_sir, constanta_sir); Func ia strcpy() copiaz con inutul constantei_sir (inclusiv caracterul terminator '\n') n nume_sir. Exemplu:
# include <string.h> void main(void) { char sir[80]; strcpy (sir, "hello"); printf("%s", sir); }

Acest program va copia "hello" n irul sir. Func ia strcat() Apelul func iei strcat() are forma:

97

strcat (s1, s2); Func ia strcat() concateneaz irul s2 la sfritul irului s1 i ntoarce irul s1. irul s2 nu se modific. Ambele iruri trebuie s aib caracterul terminator NULL, iar rezultatul va avea de asemenea caracterul terminator NULL. Exemplu:
# include <stdio.h> # include <string.h> void main(void) { char first[20], second[10]; strcpy (first, "hello"); strcpy (second, "there"); strcat (first, second); printf ("%s", first); }

Acest program va afia "hellothere" pe ecran. Func ia strcmp() Se apeleaz sub forma: strcmp (s1, s2); Aceast func ie compar irurile s1 i s2 i returneaz valori negative, dac s1 < s2, 0, dac s1 = s2 i un numr pozitiv, dac s1 > s2. Exemplu: Aceast func ie poate fi folosit ca o subrutin de verificare a parolei:
# include <stdio.h> # include <string.h> void main (void) { char s[80]; printf ("Introduceti parola: "); gets (s); if (strcmp (s, "pasword")) { printf (" Invalid pasword \n "); return 0;} return 1; }

Func ia strlen() Func ia strlen() se apeleaz sub forma: strlen (s) unde s este un ir. Func ia strlen() returneaz lungimea irului s. Exemplu: Programul urmtor returneaz lungimea unui ir introdus de la tastatur.
# incude <stdio.h> # incude <string.h> void main (void) { char sir[80];

98

printf ("Introduceti un sir: "); gets (sir); printf ("Sirul %s contine %d strlen(sir)); }

caractere

",

sir,

Observa ie: Func ia strlen() nu numr i caracterul NULL. Exemplu: Programul urmtor afieaz inversul unui ir de caractere introduse de la tastatur.
# include <stdio.h> # include <string.h> void main (void) { char sir[80]; int i; gets(sir); for(i=strlen(sir)-1;i>=0;i--) printf("%c",sir[i]);

Exemplu: Programul urmtor realizeaz introducerea unor iruri, compararea lor, concatenarea lor i afiarea rezultatului.
# include <stdio.h> # include <string.h> void main (void) { char s1[80], s2[80]; gets(s1); gets(s2); printf("Lungimi: %d %d \n",strlen(s1),strlen(s2)); if (!strcmp (s1, s2)) printf ("Sirurile sunt egale\n"); strcat (s1, s2); printf ("%s\n", s1); }

Dac se ruleaz acest program i se introduc irurile s1 = "AUTOMATICA" i s2 = "AUTOMATICA", se va afia:


Lungimi 10 10 Sirurile sunt egale AUTOMATICAAUTOMATICA

Dac irurile sunt egale, func ia strcmp() returneaz fals (0) i din aceast cauz n instruc iunea if s-a folosit !strcmp().

Observa ie: Caracterul NULL de terminare a vectorului de caractere poate fi utilizat n buclele for ca n exemplul urmtor, unde se convertete un ir de caractere scris cu litere mici la litere mari.
# include <stdio.h> # include <string.h> void main (void) { char sir[80]; int i; strcpy (sir, "acesta este un test"); for(i = 0; sir[i]; i++) sir[i] = toupper (sir[i]); printf("%s", sir); }

99

Conversia caracterelor se face cu func ia toupper() care returneaz litera mare corespunztoare argumentului (literei mici). Ciclul func ioneaz pn cnd sir[i] devine caracterul null.

6.2. Tablouri cu dou dimensiuni (matrice)


Tablourile bidimensionale (matricele) sunt reprezentate ca vectori de vectori. De exemplu, declara ia: int v[2][5]; declar un vector cu 2 elemente, fiecare element fiind un vector de tip int[5]. Se observ c fiecare dimensiune a tabloului este separat (nchis) ntre paranteze, iar dimensiunile nu sunt separate prin virgul. Astfel, declara ia: int v[2, 5]; conduce la eroare.

6.2.1. Ini ializarea matricelor


Declara ia :
char v[2][5] = { 'a', 'b', 'c', 'd', 'e', '0', '1', '2', '3', '4' };

conduce la ini ializarea primului vector cu primele 5 litere, iar a celui de-al doilea cu primele 5 cifre. Exemplu: Programul:
# include <stdio.h> void main (void) { char v[2][5] = { 'a', 'b', 'c', 'd', 'e', '0', '1', '2', '3', '4' }; int i, j; for (i = 0; i < 2; i++){ for(j = 0; j < 5; j++) printf ("v[%d][%d] = %c", i, j, v[i][j]); printf ("\n"); } }

va produce :
v[0][0]=a v[0][1]=b v[0][2]=c v[0][3]=d v[0][4]=e v[1][0]=0 v[1][1]=1 v[1][2]=2 v[1][3]=3 v[1][4]=4.

Exemplu: Secven a de instruc iuni:


# include <stdio.h> void main (void) { int l, c, num[3][4]; for (l = 0; l < 3; ++l) for (c = 0; c < 4; ++c) num[l][c] = (l * 4) + c + 1;

conduce la ncrcarea tabloului num[3][4]cu numerele de la 1 la 12. Astfel, num[0][0] = 1, ..., num[2][3] = 12.

100

Se observ c limbajul C memorez tablourile bidimensionale ntr-o matrice linii-coloane, unde primul indice se refer la linie i al doilea indice se refer la coloan. Cantitatea de memorie alocat permanent pentru un tablou, exprimat n bytes, este:

nr_linii * nr_coloane * sizeof(tipul_datei)


Declara ia: float y[4][3] = { {1,3,5},
{2,4,6}, {3,5,7},};

este o ini ializare cu paranteze complete i are urmtorul efect: - numerele 1, 3, 5 ini ializeaz prima linie a tabloului: y[0][0], y[0][1], y[0][2] sau y[0]; - numerele 2, 4, 6 ini ializeaz pe y[1]; - numerele 3, 5, 7 ini ializeaz pe y[2]. ntruct ini ializatorul se termin cu virgul, elementele lui y[3] vor fi ini ializate cu 0. Acelai efect ar fi putut fi realizat de:
float y[4][3]={1, 3, 5, 2, 4, 6, 3, 5, 7, };

Secven a:
float y[4][3] = { {1}, {2}, {3}, {0}, };

ini ializeaz prima coloan a lui y, privit ca un tablou bidimensional, cu 1, 2, 3 i 0, restul tabloului fiind ini ializat cu 0.

6.2.2. Tablouri bidimensionale de iruri


Pentru crearea unui tablou de iruri se folosete un tablou de caractere, bidimensional, n care mrimea indicelui din stnga determin numrul de iruri, iar indicele din drepta specific lungimea maxim a fiecrui ir. De exemplu, declara ia :
char sir_tablou[30][80];

definete un tablou de 30 de iruri, fiecare ir avnd maximum 80 de caractere. Accesul la un singur ir este foarte uor: se specific numai primul indice. De exemplu:
gets (sir_tablou[2])

ntoarce al treilea ir din tabloul sir_tablou. Func ional, instruc iunea anterioar este echivalent cu:
gets (&sir_tablou[2][0]);

6.3. Tablouri multidimensionale


Forma general a declara iei unui tablou multidimensional este:
tip int nume[size1][size2]...[sizeN];

De exemplu, declara ia:


trei[4][10][3];

101

creeaz un tablou de 4*10*3 ntregi. Forma general de ini ializare a tablourilor este urmtoarea:

specificator_tip nume_tablou[size1][size2]...[sizeN]={lista_valori};
unde lista_valori este o list de constante separate prin virgul, compatibile cu tipul de baz al tabloului.

Observa ie: Limbajul C permite i ini ializarea tablourilor multidimensionale fr dimensiune. Trebuie men ionat c pentru aceasta este necesar precizarea indicelui celui mai din dreapta. Astfel, declara ia:
int sqrs[5][2] = {1, 1, 2, 4, 3, 9, 4, 16, 5, 25};

este echivalent cu declara ia:


int sqrs[ ][2] = {1, 1, 2, 4, 3, 9, 4, 16, 5, 25};

6.4. Structuri
O structur este o colec ie de variabile (de tipuri diferite) referite sub un singur nume. Defini ia unei structuri formeaz un aa numit ablon (template, tag) ce poate fi folosit la crearea variabilelor tip structur. Variabilele care formeaz structuri se numesc elementele structurii. De exemplu, fragmentul urmtor definete un ablon pentru o structur numit addr care definete la rndul su numele i adresa unei persoane necesare n cazul transmiterii unei scrisori:
struct addr { char name[30]; char street[40]; char city[20]; char state[3]; unsigned int zip; }; struct { char *name; char *street; char *city; char *state; unsigned int zip; } addr;

Pentru definirea unui ablon al unei structuri se folosete cuvntul cheie struct. Terminarea defini iei se face cu ; (este unul din pu inele cazuri de utilizare a caracterului punct i virgul ; dup acolad). Precizm c numele addr identific structura particular definit anterior (ablonul) i este specificatorul su de tip. Programul anterior definete numai forma (tipul) datelor structurii, dar nu definete variabilele structur, deci trebuie fcut distinc ie dintre structuraablon i variabila-structur. O variabil de tip structur se declar cu ajutorul ablonului structurii.

102

Pentru a declara o variabil actual cu aceast structur vom scrie


struct addr addr_info;

Aceast linie declar variabila addr_info ca variabil structur de tip addr. Limbajul C aloc suficient memorie pentru a pstra toate variabilele ce alctuiesc o structur. De exemplu, memoria alocat pentru structura addr_info va fi : Name 30 bytes Street 40 bytes City 20 bytes State 3 bytes Zip 4 bytes Cnd se definete o structur ablon, se pot declara una sau mai multe variabile-structuri, astfel : struct addr { char name[30]; char street[40]; char city[20]; char state[3]; unsigned int zip; } addr_info, binfo, cinfo; Secven a anterioar definete o structur ablon numit addr i declar variabilele addr_info, binfo, cinfo de acelai tip. Pentru declararea unei singure structuri numite addr_info, nu mai este necesar includerea numelui addr al structurii, astfel:
struct { char name[30]; char street[40]; char city[20]; char state[3]; unsigned int zip; } addr_info;

n cazul de mai sus se definete variabila-structur addr_info cu ablonul definit, dar fr nume Forma general de definire a unei structuri este :
struc nume_tip_structura { tip nume_variabile; tip nume_variabile; tip nume_variabile; . . . . . . . . . . . . . . } variabile_structura;

103

unde pot fi omise fie numele tipului structurii nume_tip_struct, fie variabile_structura, dar nu ambele. Dup cum se observ, nume_tip_structura dup cuvntul cheie struct se refer la ablonul structurii (tipul su) iar variabile_structura se refer la lista de variabile de acest tip (cu aceast structur) Referirea individual a elementelor unei structuri se face cu operatorul punct ".". De exemplu, instruc iunea urmtoare va atribui elementului zip al variabilei structur addr_info valoarea 12345: addr_info.zip = 12345; Pentru accesarea elementelor unei structuri se folosete forma general : nume_structura.nume_element De exemplu, pentru afiarea variabilei zip se va scrie: printf ("%d", addr_info.zip); n aceeai form, se pot referi celelalte elemente ale structurii addr_info. De exemplu apelul: gets (addr_info.name); are ca efect trecerea la un pointer-caracter la primul caracter al elementului nume. Pentru a avea acces la fiecare caracter al elementului addr_info.name, se poate indexa name. De exemplu, pentru afiarea con inutului lui addr_info.name, caracter cu caracter, se folosete programul:
register int t; for (t = 0; addr_info.name[t]; ++t) putchar (addr_info.name[t]);

6.4.1. Tablouri de structuri


Cel mai uzual mod de folosire a structurilor este n tablouri de structuri. Pentru declararea unui tablou de structuri, mai nti se definete o structur i apoi se declar un tablou de variabile de acel tip. De exemplu, pentru declararea unui tablou cu 100 de structuri addr definite anterior, se va scrie: struct addr addr_info[100]; Pentru a avea acces la o structur oarecare din cele 100 se va indexa numele structurii (n cazul acesta addr_info). De exemplu:
printf ("%d", addr_info[2].zip);

are ca efect afiarea codului zip din a treia structur. Se observ c, la fel orice variabil tablou, tablourile de structuri ncep cu indexul 0.

104

Exemplu de program pentru actualizarea unei liste de coresponden a - maillist Pentru a ilustra modul de utilizare a structurilor i tablourilor de structuri prezentm un exemplu de program pentru actualizarea unei liste de coresponden . Informa iile ce vor fi memorate se refer la name, street, city, state, zip. Pentru definirea structurii de baz addr care va con ine aceste informa ii vom scrie:
struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; } addr_info[SIZE];

Tabloul addr_info contine SIZE structuri de tip addr, unde SIZE se definete dup necesit i. Prima func ie necesar n program este main(), a crei structur este urmtoarea:
void main() { char choice; init_list(); for (;;) { choice = menu(); switch (choice) { case 'e' : enter(); break; case 'd' : display(); break; case 's' : save(); break; case 'l' : load(); break; case 'q' : exit(); }}}

Func ia init_list() pregtete tabloul structur pentru utilizare prin punerea unui caracter null n primul byte al cmpului "nume". Programul impune ca o variabil structur s nu fie utilizat dac cmpul nume este vid. Aceast ini ializare are loc n memoria intern a calculatorului (nu n fiierul maillist de pe disc). Structura func iei de initializare init_list() ar putea fi urmtoarea:
/* Functia init_list() */ void init_list() { register int t; for (t = 0; t < SIZE; t++) *addr_info[t].name = '\0';

105

Func ia de selectare a meniului menu() va afia mesajele op iunilor i va returna varianta aleas. Prin tastarea literei din paranteze, se va lansa n execu ie o anumit procedur.
/* Functia menu() */ char menu() { char s[5],ch; do { printf ("(E)nter\n"); printf ("(D)isplay\n"); printf ("(L)oad\n"); printf ("(S)ave\n"); printf ("(Q)uit\n"); printf (" Alegeti optiunea: "); gets(s); ch=s[0]; } while (!strrchr("edlsq",ch)); return tolower(ch); }

Observa ie: Func ia strrchr(cs,c) din <string.h> ntoarce un pointer la ultima apari ie a lui c n cs sau NULL dac nu apare. Func ia enter() are ca efect introducerea unor noi informa ii n urmtoarea structur liber a listei addr_info[SIZE]. Aceast introducere se efectueaz prin determinarea primei structuri libere din memorie (cu addr_info.name setat la 0) i prin completarea sa cu informa ii culese de la tastatur.
/* Functia enter() */ void enter() { register int i; for (i=0; i < SIZE; i++) if (!*addr_info[i].name) break; if (i == SIZE) { printf ("addr_info full \n"); /* Lista plina */ return;} printf ("Name: "); gets (addr_info[i].name); printf ("Street: "); gets (addr_info[i].street); printf ("City: "); gets (addr_info[i].city); printf ("State: "); gets (addr_info[i].state); printf ("Zip: "); scanf ("%d",&addr_info[i].zip);}

Rutinele save() i load() se utilizeaz pentru actualizarea bazei de date. Aceste rutine lucreaz cu fiierul disc maillist. Cnd se apeleaz load(), atunci se copiaz n tabloul structur din memorie

106

datele stocate n fiierul maillist. Func ia load() realizeaz opera iunea invers, de supranscriere a fiierului disc cu datele din memorie. Spre exemplu, dac dorim s adugm date la fiierul maillist care con ine deja date introduse anterior, ordinea de lansare a comenzilor va fi: init_list(), load(), enter(), save(), exit(). Structura acestor rutine este urmtoarea:
/* Functia save() */ void save() { register int i; if ((fp = fopen("maillist", "wb")) == NULL) { printf (" Cannot open file\n "); return;} for (i = 0; i <= SIZE; i++) if(*addr_info[i].name) if(fwrite(&addr_info[i],sizeof(struct addr),1,fp) !=1) printf (" File write error \n "); fclose (fp);} /* Functia load() */ void load() { register int i; if ((fp = fopen("maillist","rb")) == NULL) { printf("Cannot open file\n "); return;} for (i = 0; i < SIZE; i++) if(fread(&addr_info[i],sizeof(struct addr), 1, fp) == 1); else if (feof(fp)) { fclose (fp); return;} else printf ("File read error\n"); }

Att save() ct i load() confirm un succes a unei opera ii cu fiiere prin verificarea valorilor ntoarse de func iile fread() sau fwrite(). n plus, load() trebuie s caute i indicatorul de sfrit de fiier utiliznd func ia feof() deoarece fread() ntoarce aceeai valoare dac se ntlnete indicatorul de sfrit de fiier sau dac apare o eroare. Func ia display() afieaz pe ecran ntregul tablou structur din memorie care con ine date valide:
/* Functia display() */ void display() { register int t; for (t=0;t<SIZE;t++) { if (*addr_info[t].name!='\0') { printf("%s\n",addr_info[t].name); printf("%s\n",addr_info[t].street); printf("%s\n",addr_info[t].city);

107

printf("%s\n",addr_info[t].state); printf("%d\n\n",addr_info[t].zip); getchar();}}}

Listingul complet al programului va fi:


# include <stdio.h> # include <ctype.h> # include <string.h> # define SIZE 100 struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; } addr_info[SIZE]; FILE *fp; void init_list(),enter(),save(),load(); void display(),exit(); char menu(); void main() { char choice; init_list(); for (;;) { choice = menu(); switch (choice) { case 'e' : enter(); break; case 'd' : display(); break; case 's' : save(); break; case 'l' : load(); break; case 'q' : exit(); }}} /* Functia init_list() */ void init_list() { register int t; for (t = 0; t < SIZE; t++) *addr_info[t].name = '\0';

/* Functia menu() */ char menu() { char s[5],ch; do { printf ("(E)nter\n"); printf ("(D)isplay\n"); printf ("(L)oad\n"); printf ("(S)ave\n"); printf ("(Q)uit\n"); printf (" Alegeti optiunea: "); gets(s);

108

ch=s[0]; } while (!strrchr("edlsq",ch)); return tolower(ch); } /* Functia enter() */ void enter() { register int i; for (i=0; i < SIZE; i++) if (!*addr_info[i].name) break; if (i == SIZE) { printf ("addr_info full \n"); /* Lista plina */ return;} printf ("Name: "); gets (addr_info[i].name); printf ("Street: "); gets (addr_info[i].street); printf ("City: "); gets (addr_info[i].city); printf ("State: "); gets (addr_info[i].state); printf ("Zip: "); scanf ("%d",&addr_info[i].zip);} /* Functia save() */ void save() { register int i; if ((fp = fopen("maillist", "wb")) == NULL) { printf (" Cannot open file\n "); return;} for (i = 0; i <= SIZE; i++) if(*addr_info[i].name) if(fwrite(&addr_info[i], sizeof(struct addr), 1,fp) !=1) printf (" File write error \n "); fclose (fp);} /* Functia load() */ void load() { register int i; if ((fp = fopen("maillist","rb")) == NULL) { printf("Cannot open file\n "); return;} for (i = 0; i < SIZE; i++) if(fread(&addr_info[i],sizeof(struct ddr),1,fp)==1); else if (feof(fp)) { fclose (fp); return;} else printf ("File read error\n"); } /* Functia display() */ void display() { register int t;

109

printf("\n%20s","Name"); printf("%30s","Street"); printf("%15s","City"); printf("%10s","State"); printf("%5s\n","Zip"); for (t=0;t<SIZE;t++) { if (*addr_info[t].name!='\0') { printf("%20s",addr_info[t].name); printf("%30s",addr_info[t].street); printf("%15s",addr_info[t].city); printf("%10s",addr_info[t].state); printf("%5d",addr_info[t].zip); getchar();}}}

6.4.2. Introducerea structurilor n func ii


a) Introducerea elementelor unei structuri n func ii Cnd un element al unei variabile tip structur este transmis (pasat) unei func ii, de fapt este transmis valoarea acelui element. Exemplu:
struct struct1{ char x; int y; float z; char s[10]; } struct2;

Modalitatea de a introduce fiecare element ntr-o func ie este urmtoarea:


func (struct2.x);

/* se paseaza valoarea caracterului x */


func2 (struct2.y);

/* se paseaza valoarea intregului y */


func3 (struct2.z);

/* se paseaza valoarea reala a lui z */


func4 (struct2.s);

/* se utilizeaza adresa sirului s */


func (struct2.s[2]);

//se utilizeaza valoarea caracterului lui s[2] unde func(), func2(), func3(), func4() sunt numele unor func ii. Pentru a transmite func iei func() adresele elementelor din structura struct2, se scrie astfel: func (&struct2.x); /* se paseaza adresa caracterului x */ func (&struct2.s[2]); /* se paseaza adresa caracterului s[2] */

110

b) Transmiterea unei ntregi structuri unei func ii Cnd o structur este utilizat ca argument al unei func ii, limbajul C transmite func iei ntreaga structur utiliznd metoda standard a apelului prin valoare. Aceasta nseamn c orice modificare asupra con inutului structurii n interiorul func iei n care este apelat structura, nu afecteaz structura folosit ca argument. Ceea ce trebuie avut neaprat n vedere atunci cnd, ca parametru, se utilizeaz o structur este ca tipul argumentului s fie identic cu tipul parametrului. Exemplu:
# include <stdio.h> void f1(); void main() { struct { int a,b; char ch; } arg; arg.a = 1000; f1(arg); /* se apeleaza functia f1() */ } void f1(param) /* se declara functia f1 */ struct { int x, y; char ch; } param; {printf ("%d\n", param.x); }

Acest program declar att argumentul arg al lui f1, ct i parametrul param ca avnd acelai tip de structur. Programul va afia pe ecran valoarea 1000. Pentru a face programele mai compacte se procedeaz la definirea structurii ca variabil global i apoi la utilizarea numelui acesteia pentru a declara diversele variabile structur. Exemplu:
# include <stdio.h> void f1(); /* Se defineste un tip structura */ struct struct_tip { int a, b; char ch;}; void main() { struct struct_tip arg; /* se declara structura arg */ arg.a = 1000; f1(arg);}

111

void f1(struct struct_tip functia f1() */ {printf ("%d\n",param.a); }

param)

/*

se

declara

Pentru exemplificare, propunem urmtorul program: - se preia de la tastatura un prim ir de numere ntregi - se preia de la tastatura un al doilea ir de numere ntregi - se concateneaz cele dou iruri - irul rezultat se sorteaz n ordine cresctoare, avnd n vedere ca primele pozi ii s fie ocupate de numerele pare din ir sortate cresctor dup care s urmeze numerele impare din ir sortate tot cresctor. Pentru a realiza acest program, vom utiliza nu variabile de tip ir ci o structur care s con in ca prim element irul respectiv iar cel de-al doilea element s fie lungimea acestuia. Se vor construi func ii care s realizeze citirea irurilor de la tastatur, scrierea lor pe display, respectiv s le ordoneze i s le sorteze dup paritate. Toate aceste func ii comunic prin intermediul unei variabile globale de tip structur i a mai multor variabile locale de tip structur. Programul n C este prezentat n continuare:
# include <stdio.h> // definim prototipurile functiilor utilizate struct sir_lung cit_sir(); struct sir_lung concat_sir(); struct sir_lung ord_sir(); struct sir_lung par_sir(); struct sir_lung impar_sir(); void tip_sir(); /* se defineste structura sir+lungime si variabila globala sir */ struct sir_lung { int sir[80]; int lung;} sir; // programul principal void main(){ struct sir_lung sir_init,sir_ord,sir_par,sir_impar; sir_init=cit_sir(); getchar(); sir_ord=cit_sir(); sir_init=concat_sir(sir_init,sir_ord); sir_par=par_sir(sir_init); sir_par=ord_sir(sir_par); sir_impar=impar_sir(sir_init); sir_impar=ord_sir(sir_impar); sir_ord=concat_sir(sir_par,sir_impar);

112

tip_sir(sir_init); tip_sir(sir_ord);} // se definesc functiile struct sir_lung cit_sir() {int result=1, i=0; sir.lung=0; while (result) { result=scanf("%d",&sir.sir[i]); i++;} sir.lung=--i; return sir;} void tip_sir(struct sir_lung sirf) { int i; for (i=0;i<sirf.lung;i++) printf("%d ",sirf.sir[i]);printf("\n");} struct sir_lung concat_sir(struct sir_lung sir1, struct sir_lung sir2) { int i; struct sir_lung sir_concat; sir_concat=sir1; for (i=0;i<sir2.lung;i++) sir_concat.sir[sir1.lung+i]=sir2.sir[i]; sir_concat.lung=sir1.lung+sir2.lung; return sir=sir_concat;} struct sir_lung ord_sir(struct sir_lung sir1) {int i,j,temp; for (i=0;i<sir1.lung;i++) for (j=i+1;j<sir1.lung;j++) if (sir1.sir[i]>sir1.sir[j]) {temp=sir1.sir[i];sir1.sir[i]=sir1.sir[j]; sir1.sir[j]=temp;} return sir=sir1;} struct sir_lung par_sir(struct sir_lung sir1) { int i,j=0; struct sir_lung sir_par; for (i=0;i<sir1.lung;i++) if (!(sir1.sir[i]%2)) {sir_par.sir[j]=sir1.sir[i];j++;} sir_par.lung=j; return sir=sir_par;} struct sir_lung impar_sir(struct sir_lung sir1) { int i,j=0; struct sir_lung sir_impar; for (i=0;i<sir1.lung;i++) if (sir1.sir[i]%2) {sir_impar.sir[j]=sir1.sir[i];j++;} sir_impar.lung=j; return sir=sir_impar;}

113

Din func ia cit_sir() se poate iei prin tastarea oricarui caracter nenumeric. Se observ cum aceasta func ie lucreaz direct cu variabila global ir iar celelalte cu variabile locale proprii care apoi sunt asignate variabilei globale ir la returnare. Rulnd programul vom ob ine rezultatele:
1 2 17 -3 6 -4; -9 -2 31 2 -7; 1 2 17 -3 6 -4 -9 -2 31 2 -7 -4 -2 2 2 6 -9 -7 -3 1 17 31

6.4.3. Tablouri i structuri n structuri


Elementele unei structuri pot fi simple (int, char etc.) sau complexe (tablouri de elemente de diferite tipuri, structuri). Exemplu: Considerm structura :
struct x { int a[10][10]; /* tablou de 10x10 intregi */ float b;} y;

De exemplu, referirea ntregului a[3][7] se face prin: y.a[3][7] Cnd o structur este un element al altei structuri, structurile se numesc ncuibate (nested, incluse). Exemplu:
struct sal { struct addr adresa; float salariu; } muncitor;

Se observ c elementele variabilei-structur "adresa" sunt incluse n structura "sal". Aici "addr" este structura definit anterior. Exemplul anterior definete structura "sal" cu 2 elemente: primul este o structur de tip "addr" care con ine adresa salariatului; al doilea element este salariul sptmnal al acestuia. Instruc iunea urmtoare va atribui codul 90178 variabilei "zip" din adresa muncitorului muncitor.adresa.zip = 90178; Se observ c elementele fiecrei structuri sunt referite de la exterior ctre interior, respectiv de la stnga la dreapta.

6.5. Uniuni
n C o uniune este o zon de memorie utilizat de mai multe variabile ce pot fi diferite ca tip. Definitia uniunilor este similar celei a structurilor.

114

Exemplu:
union tip_u { int i; char ch;};

O variabil de acest tip poate fi declarat fie prin plasarea numelui su la sfritul acestei defini ii, fie utiliznd o instruc iune de declarare separat. De exemplu, instruc iunea : union tip_u exuniune; declar variabila-uniune "exuniune" n care att ntregul i, ct i caracterul ch ocup aceeai zon de memorie (cu toate ca int ocupa 4 octe i, iar char numai un octet). Cnd se declar o variabil union compilatorul C rezerv o zon de memorie suficient de lung capabil s preia variabila cu lungimea cea mai mare. Pentru a accesa elementele unei uniuni se utilizeaz aceeai sintax folosit la structuri (operatorii punct i " -> "). Cnd un element al unei uniuni se adreseaz direct, se utilizeaz operatorul ".", iar cnd elementul se adreseaz printr-un pointer, se folosete operatorul "-> ". De exemplu, pentru a atribui elementul ntreg i al uniunii anterioare "exuniune" valoarea 10, se va scrie: exuniune.i = 10; n exemplul urmtor, programul transfer un pointer la "exuniune" unei func ii :
func1(union tip_u *un) { un -> i = 10; }

/* se atribuie valoarea 10 intregului i al uniunii exuniune */ In C, uniunile sunt des utilizate cnd sunt necesare conversii de tip. De exemplu, func ia standard putw() ce realizeaz scrierea binar a unui ntreg ntr-un fiier de pe disc, poate fi scris folosind o uniune. Pentru aceasta, mai nti se creaz o uniune ce cuprinde un ntreg i un vector de dou caractere, astfel:
union pw { int i; char ch[2];};

Atunci, structura lui putw(), utiliznd aceasta uniune este :


putw(word, fp) union pw word; /* se declara uniunea word */ FILE *fp { putc(word ->ch[0]); // se scrie primul caracter putc(word->ch[1]); // se scrie al doilea caracter }

115

6.6. Enumerri
O enumerare este o mul ime de constante ntregi ce pot lua toate valorile unei variabile de un anumit tip. Enumerrile se definesc n acelai mod ca i structurile, utiliznd cuvntul cheie enum ce semnaleaz nceputul unui tip enumerare. Forma general de definire a unei enumerri este: enum nume_tip_enum { lista_enumeratori} lista_variabile; unde att nume_tip_enum, ct i lista_variabile sunt op ionale. Exemplu: Urmtorul fragment definete o enumerare numit "bancnota" cu care apoi se declar o enumerare numit "bani" avnd acest tip:
enum bancnota {suta,douasute,cincisute,mie,cincimii,zecemii}; enum bancnota bani;

Dndu-se aceast defini ie i declara ie, sunt valabile urmatoarele instructiuni:


bani = mie; if (5*bani == cincimii) printf("Sunt 5000 lei.\n");

Trebuie precizat c fiecare enumerator este caracterizat printr-o valoare ntreaga. Fr nici o alt ini ializare, valoarea primului enumerator este 0, a celui de-al doilea este 1, s.a.m.d. De aceea, instruc iunea: printf ("%d %d, suta, mie); va afisa pe ecran: 0 3 Se pot specifica valorile unuia sau mai multor simboluri folosind ini ializatori. De exemplu: enum bancnota {suta, douasute, cincisute, mie=1000, cincimii, zecemii}; face ca simbolurile din enumerarea bancnota s aib valorile:
suta = 0 douasute = 1 cincisute = 2 mie = 1000 cincimii = 1001 zecemii = 1002

Urmatorul fragment de program nu func ioneaz, deoarece "bani" este un ntreg i nu un ir :


bani = cincimii; printf ("%s", bani);

Nici acest program nu functioneaz:


gets (s); strcpy (bani, s);

116

Pentru a afia tipurile bancnotelor con inute n enumerarea "bani", se va scrie:


switch (bani) { case suta: printf (" suta "); break; case douasute: printf (" douasute "); break; . . . . . . . . . . . . . . . . . . . . . . . . . . case zecemii: printf (" zecemii "); }

Uneori pentru a translata valoarea unui enumerator n irul corespunztor, se poate declara un tablou de iruri i utiliza valoarea enumeratorului ca index. De exemplu, urmtorul fragment va afia irul corespunztor:
char name[ ][20] = { "suta", "douasute", "cincisute", "mie", "cincimii" "zecemii" }; . . . . . . printf ("%s", name[bani]);

Fragmentul anterior va func iona numai dac nu se realizeaz ini ializarea simbolurilor, deoarece indexarea irurilor ncepe cu zero. Urmtorul program afieaz numele bancnotelor:
# include <stdio.h> enum bancnota { suta, douasute, cincisute,mie, cincimii,zecemii,cincizecimii,sutamii,cincisutemii}; char name[][20]= {"suta","douasute","cincisute","mie","douamii","cincimii" ,"zecemii","cincizecimii" "sutamii","cincisutemii"}; void main() { enum bancnota bani; for (bani = suta; bani <= cincisutemii; bani ++) printf ("%s\n", name[bani]);}

Dac variabilei uniune y din exemplul urmtor i se aplic operatorul sizeof() vom gsi sizeof(y) = 8.
# include <stdio.h> union { char ch; int i; double f; } y; void main() {printf("%d\n",sizeof(y));}

Deci compilatorul va re ine valoarea celei mai largi tipuri de date din uniune.

117

Capitolul VII POINTERI


Un pointer este o variabil care pstreaz adresa unui obiect de tip corespunztor. Forma general pentru declararea unei variabile pointer este: tip * nume_variabila; unde tip poate fi oricare din tipurile de baz admise n C, iar nume_variabila este numele variabilei pointer. Tipul de baz al pointerului definete tipul variabilelor spre care indic pointerul. Variabila pointer este o variabil de un tip special, aparte de tipurile char, int, float. Cuvntul cheie tip din declara ia unui pointer se refer la tipul de dat spre care indic pointerul, nu la formatul n care se stocheaz efectiv o variabil pointer n memorie. Formatul n care se stocheaz o variabil pointer n memorie depinde de tipul de compilator care se folosete, deci depinde n mare msur de tipul procesorului pentru care a fost proiectat compilatorul. O indica ie despre formatul n care se stocheaz o variabil pointer n memorie poate fi ob inut prin tiprirea con inutului unei variabile pointer (o adres) utiliznd printf() cu formatul %p. Exemplu:
char *p; /* pointer la caracter */ int *temps, *start; /* pointeri la intregi */ char *const q; /* pointer constant la caracter */

7.1. Operatori pointer


Exist doi operatori pointer speciali * i &: Operatorul & este un operator unar care ofer (returneaz) adresa unei variabile (adresa operandului su). Operatorul * este complementarul lui &. Este un operator unar care returneaz valoarea variabilei plasat la adresa care urmeaz dup acest operator. Exemplu:
# include <stdio.h> void main (void) { int *count_addr, count, val; count = 100; /* int count are valoarea 100 */

118

count_addr = &count; /*count_addr indica spre count. Aceasta instructiune plaseaza in count_addr adresa din memorie a variabilei count, adresa care nu are nici o legatura cu valoarea lui count */ val = count_addr; /* val preia valoarea de la adresa count_addr. Aceasta instructiune plaseaza valoarea lui count aflata la adresa count_addr n variabila val */ printf ("%d", val); /*Se va tipari numarul 100 */ Spre exemplu, s considerm por iunea de program: short i, j; // i si j sunt ambele intregi scurti short *p // p este pointer la tip intreg scurt i = 123; p = &i; j = *p; }

S presupunem c zona de stocare a celor trei variabile arat astfel:


Adresa i j p
i = 123; p = &i;

Memoria

1200 1202 1204

Dup primele dou atribuiri zona de stocare va arta astfel:


A dresa i j p 1200 1202 1204 1200 M emoria 123

Con inutul variabilei p (de tip pointer) va fi valoarea 1200, adic adresa variabilei i. Instruc iunea j = *p; copiaz un ntreg scurt de la loca ia 1200 n j, loca iile de memorie fiind acum ca cele din figur:
Adresa i j p 1200 1202 1204 Memoria 123 123 1200

119

7.1.1. Importan a tipului de baz


Considerm declara ia: val = *count_addr; Se pune ntrebarea: care va fi numrul de bytes ce va fi transferat variabilei val de la adresa indicat prin *count_addr. Sau, mai general, de unde tie compilatorul c i bytes s transfere n cazul oricrei asignri care utilizeaz pointeri. Rspunsul la aceste ntrebri este acela c, tipul de baz al pointerului determin tipul datei spre care indic pointerul. Exemplu:
/* Acest program nu lucreaza corect */ # include <stdio.h> void main (void) { float x = 10.12, y; short int *p; /* pointer la intreg */ p = &x; /* p preia adresa lui x */ y = *p; /* y preia valoarea de la adresa p */ printf ("x = %f y = %f",x,y); }

Acest program nu va atribui valoarea lui x lui y, deoarece n program se declar p ca fiind pointer la ntreg scurt i compilatorul va transfera n y numai 2 bytes (corespunztori reprezentrii unui ntreg scurt) i nu 4 bytes, corespunztori unui numr real n virgul mobil.

7.1.2. Expresii n care intervin pointeri


n general, expresiile n care intervin pointeri respect aceleai reguli ca orice alte expresii din limbajul C. Atribuirea pointerilor Ca orice variabil, un pointer poate fi folosit n membrul drept al unei instruc iuni de asignare (atribuire), pentru atribuirea valorii unui pointer unui alt pointer. Exemplu:
# include <stdio.h> void main (void) { int x; int *p1,*p2; /* pointeri la intregi */ p1 = &x; /* p1 indica spre x */ p2 = p1 /* p2 indica tot spre x */ printf ("p1 = %p p2 = %p", p1, p2); } /* Se afiseaza valoarea hexa a adresei lui x, nu valoarea lui x */

Se observ c n func ia printf() tiprirea se face cu formatul %p care specific faptul c variabilele din list vor fi afiate ca adrese pointer.

120

Din cele de mai sus se observ c opera ia fundamental care se execut asupra unui pointer este indirectarea, ceea ce presupune referirea la un obiect indicat de acel pointer. Exemplu:
char c1 = 'a'; char *p = &c1; /* p contine adresa lui c1 */ char c2 = *p; /*c2 preia valoarea de la adresa p*/ printf ("c1 = %c c2 = %c", c1, c2);

Deci variabila indicat de p este c1, iar valoarea memorat n c1 este 'a', aa nct valoarea lui *p atribuit lui c2 este 'a'. Opera ii aritmetice efectuate asupra pointerilor o Utilizarea operatorilor de incrementare i decrementare Fie secven a:
int *p1; p1++; /* pointer la intreg */

De fiecare dat cnd se incrementeaz p1, acesta va indica spre urmtorul ntreg. Astfel, dac p1 = 2000, dup efectuarea instruc iunii p1++, acesta va fi p1 = 2004 (va indica spre urmtorul ntreg). Dup fiecare incrementare a unui pointer, acesta va indica spre urmtorul element al tipului su de baz. Dup fiecare decrementare a unui pointer, acesta va indica spre elementul anterior. Valoarea pointerilor va fi crescut sau micorat n concordan cu lungimea tipului datelor spre care acetia indic, aa cum se poate vedea n exemplul urmtor:
char *ch = 3000; short int *i = 3000;

ch ch + 1 ch + 2 ch + 3 ch + 4 ch + 5 ch + 6

Cum valoarea indirectat de un pointer este o l-valoare, ea poate fi asignat i incrementat ca orice alt variabil. O l-valoare (left value) este un operand care poate fi plasat n stnga unei opera ii de atribuire. Verifica i utilizarea pointerilor din programul urmtor:

3000 3001 3002 3003 3004 3005 3006 Memoria

i i+1 i+2 i+3

121

# include <stdio.h> void main(void) { short *pi, *pj, t; long *pl; double *pd; short i, j; i=1; j=2; t=3; printf("i= %d, j= %d\n", i, j); pi=&i; pj=&j; printf("pi= %p, pj= %p\n", pi, pj); *pj /= *pi+1; printf("*pi= %d *pj= %d\n", *pi, *pj); *pj /= *pi+2; printf("*pi= %d *pj= %d\n", *pi, *pj); printf("++pj= %p, ++*pj= %d\n",++pj,++*pj);

n urma rulrii sale, pe calculatoarele mai moderne ob inem rezultatul


i= 1,j= 2 pi= 0065FDE0,pj= 0065FDDC *pi= 1 *pj= 1 *pi= 1 *pj= 0 ++pj= 0065FDDE,++*pj= 1

La sau dintr-un pointer, se pot aduna sau scdea valori de tip ntreg. Rezultatul este un pointer de acelai tip cu cel ini ial, indicnd spre un alt element din tablou. De exemplu,
p1 = p1 + 9;

o Utilizarea operatorilor de adunare i de scdere

face ca p1 s indice spre al 9-lea element avnd tipul lui p1, considernd c elementul curent este indicat de p1. Evident c valoarea pointerului se va modifica corespunztor lungimii tipului datei indicat prin pointer.
Exemplu:
int *p1; /* Pointer la intreg */ p1 = p1 + 9;

Dac valoarea p1 = 3000, atunci p1 + 9 va avea valoarea:


(valoarea lui p1)+9*sizeof(int)=3000+9*4=3036

Aceleai considerente sunt valabile n cazul n care un ntreg este sczut dintr-un pointer. Dac doi pointeri de acelai tip sunt sczu i, rezultatul este un numr ntreg cu semn care reprezint deplasamentul dintre cei doi pointeri (pointerii la obiecte vecine difer cu 1). n cazul tablourilor, dac pointerul rezultat indic n afara tabloului, rezultatul este nedefinit.

122

Dac p indic spre ultimul membru dintr-un tablou, atunci (p+1) are valoare nedeterminat. Observa ii : Nu se pot aduna sau scdea valori de tip float sau double la/sau dintr-un pointer. Nu se pot efectua opera ii de nmul ire i mpr ire cu pointeri. Exemplu: Scderea a doi pointeri este exemplificat n programul:
# include <stdio.h> void main(){ int i=4, j; float x[] = {1,2,3,4,5,6,7,8,9}, *px; j = &x[i]-&x[i-2]; px = &x[4]+i; printf("%d %f %p %p\n",j,*px,&x[4],px); }

Doi pointeri de acelai tip se pot compara printr-o expresie rela ional, astfel: dac p i q sunt doi pointeri, atunci instruc iunile:
if (p < q) printf ( p indica spre o adresa mai mica decit q \n );

o Compararea pointerilor

sunt corecte. Compararea pointerilor se utilizeaz cnd doi sau mai mul i pointeri indic spre acelai obiect comun. Exemplu: Un exemplu interesant de utilizare a pointerilor const n examinarea con inutului loca iilor de memorie ale calculatorului.
/* Programul afiseaza continutul locatiilor de memorie de la o adresa specificata */ # include <stdio.h> # include <stdlib.h> dump (start); void main (void) { /* start = adresa de inceput */ unsigned long int start; printf (Introduceti adresa de start: ); scanf ( %lu , &start); dump (start); /* Se apeleaza functia dump () */ } dump (start) /* Se defineste functia dump() */ unsigned long int start; {char far *p; int t; p = (char far *) start; /*Conversie la un pointer*/ for (t = 0; ; t++, p++) { if (!(t%16)) printf ("/n"); printf ("%2X ", *p);

123

/*Afiseaza in hexazecimal continutul locatiei de memorie adresata cu *p*/ if (kbhit()) return;} // Stop cand se apasa orice tasta }

Programul introduce cuvntul cheie far care permite pointerului p s indice loca ii de memorie care nu sunt n acelai segment de memorie n care este plasat programul. Formatul %lu din scanf() nseamn: "citete un unsigned long int". Formatul %X din printf() ordon calculatorului s afieze argumentul n hexazecimal cu litere mari (Formatul %x afieaz n hexazecimal cu litere mici). Programul folosete explicit un ablon de tip pentru transferul valorii ntregului fr semn, start, pe care l introducem de la tastatur, ntr-un pointer. Func ia kbhit() returneaz ADEVARAT dac se apas o tast, altfel returneaz FALS. n exemplele de pn acum, s-au folosit func ii C care atunci cnd erau apelate, parametrii acestor func ii erau (ntotdeauna) actualiza i prin pasarea valorii fiecrui argument. Acest fapt ne ndrept ete s numim C-ul ca un limbaj de apel prin valoare. Exist totui o excep ie de la aceast regul atunci cnd argumentul este un tablou. Aceast excep ie este explicat, pe scurt, prin faptul c valoarea unui nume al unui tablou (vector, matrice etc.) neindexate este adresa primului su element. Deci, n cazul unui argument de tip tablou, ceea ce se paseaz unei func ii este o anumit adres. Folosind variabile pointer se pot pasa adrese pentru orice tip de date. Spre exemplu, func ia scanf() accept un parametru de tip pointer (adres): scanf(%1f,&x); Ceea ce este important de eviden iat este cum anume se poate scrie o func ie care s accepte ca parametri formali sau ca argumente pointeri ?. Func ia care recep ioneaz o adres ca argument va trebui s declare acest parametru ca o variabil pointer. De exemplu, func ia swap() care va interschimba valorile a doi ntregi poate fi declarat astfel:
# include <stdio.h> void swap(); /*Prototipul functiei swap()*/ void main(void) { int i,j; i=1; j=2; printf("i= %d j= %d\n", i, j); swap(&i,&j); /* Apelul functiei */

o Utilizarea pointerilor ca parametri formali ai func iilor

124

printf("i= %d j= %d\n", i, j); } void swap(int *pi, int *pj) { short t; t = *pi; *pi = *pj; *pj = t; }

7.2. Pointeri i tablouri


ntre pointeri i tablouri exist o strns legtur n limbajul C. Exist ns o mare deosebire ntre tablouri i pointeri pe care trebuie s o avem mereu n vedere. Un tablou const ntotdeauna dintr-o mare cantitate de memorie, ndeajuns de mare pentru a re ine to i octe ii corepunztori tuturor elementelor tabloului. Astfel, tabloul q declarat ca short q[100]; rezerv 2x100 octe i de memorie iar int q[100] rezerv 4x100 octe i de memorie. Pe de alt parte, pentru un pointer se aloc o zon de memorie redus la numai c iva octe i necesari pentru a re ine o adres de memorie. De fapt, zona de memorie alocat unui pointer depinde de tipul pointerului (tipul datelor spre care puncteaz acesata) i va fi de numai c iva octe i ( n exemplele anterioare - 4 octe i). Considerm secven a:
char sir[80]; char *p1; p1 = sir;

Acest fragment face ca p1 s adreseze primul element al tabloului sir. n C numele unui tablou fr indici este adresa de start a tabloului. De fapt, numele tabloului este un pointer la tablou. Ca o concluzie, un pointer declarat sau numele unui tablou fr indici reprezint adrese, pe cnd numele unui tablou cu indici se refer la valorile stocate n acea pozi ie n tablou. Deoarece indicele inferior al oricrui vector este zero, pentru a referi al 5-lea element al irului sir, putem scrie sir[4] sau *(p1+4) sau p1[4]. Deci pointerul p1 indic spre primul element al lui sir. Pentru a avea acces la elementul unui tablou, n limbajul C, se folosesc 2 metode : 1. utilizarea indicilor tabloului; 2. utilizarea pointerilor . Deoarece a doua metod este mai rapid, n programarea n C, de regul, se utilizeaz aceast metod.

125

Pentru a vedea modul de utilizare a celor dou metode, considerm un program care tiprete cu litere mici un ir de caractere introdus de la tastatur cu litere mari: Exemplu: Versiunea cu indici
void main (void) { char sir[80]; int i; printf ("Introduceti un sir de caractere scrise litere mari: \n"); gets (sir); printf ("Acesta este sirul in litere mici: \n"); for(i=0;sir[i];i++) printf("%c", tolower(str[i])); }

cu

Exemplu: Versiunea cu pointeri


void main (void) { char sir[80] , *p; printf ("Introduceti un sir de caractere scrise litere mari: \n"); gets (sir); printf (" Acesta este sirul in litere mici: \ n"); p = sir; /* p preia adresa de inceput a sirului */ while (*p) printf (" %c ", tolower(*p++)); } cu

Pointerii sunt rapizi i uor de utilizat cnd se dorete referirea elementelor unui tablou n ordine strict cresctoare sau strict descresctoare. Dac ns se dorete referirea aleatoare a elementelor unui tablou, atunci indexarea tabloului este cel mai simplu i sigur de utilizat.

7.2.1. Indexarea pointerilor


n C, dac p este un pointer, iar i este ntreg, p[i] este identic cu *(p+i). Dac avem declara iile:
short q[100]; short *pq

atunci sunt permise i posibile urmtoarele declara ii:


Varianta cu tablou pq=&q[0] pq=q q[n] Varianta cu pointeri pq=&q[0] pq=q pq[n] *(pq+n) Descriere
Pointerul pq indic adresa primului element al tabloului q pq[n] nseamn acelai lucru cu *(pq+n)

n C, dac se pune un index unui pointer, ca n pq[n], atunci se consider aceast utilizare echivalent cu *(pq+n). Cu alte cuvinte,

126

orice referire la pq[n] nseamn acelai lucru cu valoarea de la adresa (pq+n). Exemplu: Programul urmtor realizeaz tiprirea pe ecran a numerelor cuprinse ntre 1 i 10.
void main (void) { int v[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int *p, i; p = v; /* p indica spre v */ for (i=0;i<10;i++) printf ("%d", p[i]); }

Utilizarea constantelor ir n locul poinetrilor la caractere este posibil dar nu este uzual. Exemplu:
# include <stdio.h> void main(){ char *sir = "To be or not to be", *altsir; printf("%s\n", "That don't impress me much"+5); printf("%c\n",*("12345"+3)); printf("%c\n","12345"[1]); puts("string\n"); altsir = "American pie"; printf("sir = %s\naltsir = %s\n",sir,altsir); }

Exemplu de utilizare a stivei. Stiva este o list cu acces tip LIFO (last in - first out). Pentru a avea acces la elementele stivei, se utilizeaz doua rutine: push() i pop(). Calculatorul pstreaz stiva ntr-un tablou, iar rutinele push() i pop() realizeaz accesul la elementele stivei utiliznd pointeri. Pentru memorarea vrfului stivei, utilizm variabila tos (top of stack). Considerm c folosim numai numere de tip ntreg. n programul main(), rutinele push() i pop() sunt utilizate astfel: push() citete numerele introduse de la tastatur i le memoreaz n stiv dac acestea sunt nenule. Cnd se introduce un zero, pop() extrage valoarea din stiv.
# include <stdlib.h> # include <stdio.h> void push(); /* Prototipul functiei push() */ int pop(); /* Prototipul functiei pop() */ // Se rezerva pentru stiva 50x4 = 200 locatii int stack[50]; int *p1, *tos; void main(void) { int value; p1 = stack; /* p1 preia adresa bazei stivei */ tos = p1; /* tos va contine varful stivei */ do { scanf ("%d", &value);

127

if (value != 0) push (value); else printf ("Nr. extras din stiva este pop()); } while (value != -1); } void push(int i) /* Functia push() */ { p1++; if (p1==(tos+50)) { printf("\n Stiva este plina."); exit (0);} *p1 = i; printf("introdus in stiva\n"); } int pop() { if (p1 == tos) { printf ("\n Stiva este complet goala."); exit (0); } p1 --; return *(p1+1); }

%d

\n",

7.2.2. Pointeri i iruri


Deoarece numele unui tablou fr indici este un pointer la primul element al tabloului, pentru implementarea unor func ii care manipuleaz iruri, se pot utiliza pointeri. tim c func ia strcmp(s1, s2) realizeaz compararea irurilor s1 i s2 i ntoarce 0 dac s1 = s2, o valoare negativ, dac s1 < s2 i o valoare pozitiv, dac s1 > s2. Exemplu: Prezentm o variant de scriere a func iei strcmp(s1,s2)
char *s1, *s2; { while (*s1) if (*s1 - *s2) /* Daca valoarea punctata de s1 este diferita de valoarea punctata de s2, */ return *s1-*s2; /* Returneaza diferenta */ else {s1++; s2++;} return '\0'; //Se returneaza 0 in caz de egalitate }

Reamintim c un ir n C se termin cu caracterul NULL. De aceea, instructiunea while(*s1) rmne adevrat pn cnd se ntlnete caracterul NULL, care este o valoare fals. Dac ntr-o expresie se utilizeaz un ir constant, calculatorul trateaz constanta ca pointer la primul caracter al irului. Exemplu: Programul urmtor afieaz pe ecran mesajul " Acest program func ioneaz ":
# include <stdio.h> void main (void) { char *s; s = " Acest program functioneaza "; printf (s); }

128

7.2.3. Preluarea adresei unui element al unui tablou


Pn acum s-a vzut c un pointer poate s adreseze primul element al unui tablou. Este posibil s se adreseze orice element al unui tablou aplicnd operatorul & unui tablou indexat. De exemplu,
p = &x[2];

plaseaz adresa celui de-al 3-lea element al vectorului x n pointerul p. Un domeniu n care aceast practic este esen iala const n gsirea unui subir ntr-un ir dat. Exemplu: Programul urmtor afieaz ultima parte a unui ir introdus de la tastatur, din punctul n care se ntlnete primul spa iu:
# include <stdio.h> void main (void) { char s[80]; char *p; int i; printf (" Introduceti un sir : \n "); gets (s); /* Gaseste primul spatiu sau sfarsitul sirului */ for (i = 0; s[i] && s[i] != ' '; i++) p = & s[i+1]; printf (p); }

Dac p indic spre un spa iu, programul va afia spa iul i apoi subirul rmas. Dac n irul introdus nu este nici un spa iu, p indic spre sfritul irului i atunci nu se va afia nimic. De exemplu, dac se introduce my friend, atunci printf() afieaz mai nti un spa iu i apoi friend.

7.2.4. Tablouri de pointeri


Putem construi tablouri de pointeri n aceeai manier n care se definesc alte tipuri de date. Exemplu:
int *x[10]; // Vector de 10 pointeri la intregi char *p[20]; // Vector de 20 pointeri la caracter

Pentru atribuirea unei variabile ntregi, var, celui de al treilea element al tabloului de pointeri *x[10], se va scrie:
x[2] = &var;

Pentru gsirea valorii lui var, se va scrie:


y = *x[2]; //Valoarea lui var este atribuita lui y

Exemplu: Tablourile de pointeri pot fi utilizate n construirea mesajelor de eroare, astfel:

129

char *err[ ] = { " Cannot open file \n ", " Read error \n ", " Write error \n " }; selmes (int num) /* Selecteaza un mesaj */ { scanf("%d", &num); /* Se introduce un numar de la tastatura */ printf("%s", err[num]); }

Func ia printf() este apelat din func ia selmes(). Aceasta va afia mesajul de eroare indexat prin numrul de eroare num, care este pasat ca argument func iei selmes(). De exemplu, dac se introduce 2, atunci se va afia mesajul: Write error Atentie !. Trebuie facut distinc ia ntre:
int *v[10]; // Tablou de 10 pointeri la intregi int (*v)[10]; // Pointer la un tablou de 10 intregi

Pentru aceasta trebuie inut cont de faptul c * este un operator prefixat, iar [] i () sunt operatori postfixa i. Deoarece prioritatea operatorilor postfixa i este mai mare dect cea a operatorilor prefixa i, atunci cnd se dorete schimbarea priorit ii, trebuie folosite paranteze.

7.2.5. Pointeri la pointeri


Un tablou de pointeri este ceea ce numim pointeri la pointeri. Conceptul de tablou de pointeri este simplu, deoarece indexarea tabloului conduce la clarificarea semnifica iei lui. Un pointer la un pointer este o form de indirectare multipl sau un lan de pointeri. n cazul unei indirectari simple, valoarea pointerului este adresa variabilei care con ine valoarea dorit: Pointer Variabil Adres ---------> Valoare n cazul unui pointer la pointer, primul pointer con ine adresa celui de-al doilea pointer, care indic spre variabila ce con ine valoarea dorit: Pointer Pointer Variabil Adres ---------> Adres ---------> Valoare Declararea indirectrilor multiple se face sub forma:
/* cpp este un pointer la pointer la caracter */ char **cpp; /* newbalance este un pointer la pointer la float */

130

float **newbalance;

Pentru a avea acces la o valoare indirectat printr-un pointer la pointer este necesar, de asemenea, utilizarea operatorului * de dou ori, aa cum se vede n exemplul urmtor:
# include <stdio.h> void main (void) { int x, *p, **q; x = 10; p = &x; /* p preia adresa lui x */ q = &p; /* q preia adresa lui p */ printf(" %d ", **q);/*Se afiseaza valoarea lui x*/

7.2.6. Ini ializarea pointerilor


Secven a:
char *p; char s[] = "Hello world \n "; p = s; /* p indica spre s */

este echivalent cu:


char *p = "Hello world \n";

ntr-un program, p din ultima declara ie poate fi utilizat ca orice alt ir. Astfel, programul urmtor este corect:
# include <stdio.h> char *p = " Hello world \n "; void main (void) { register int t; /*Se tipareste sirul in ordine directa si inversa*/ printf (p); for(t = strlen(p)-1; t > -1; t--) printf("%c",p[t]); }

Observa ie: Neini ializarea pointerilor sau ini ializarea greit a acestora, poate duce la erori care, n cazul programelor de dimensiuni mari, sunt foarte greu de depistat i pot avea urmri catastrofale. Exemplu: Considerm programul:
# include <stdio.h> void main(void) { int x, *p; x = 10; *p = x; printf ("%d\n", *p);

Acest program atribuie valoarea 10 anumitor loca ii de memorie necunoscute. Programul nu va oferi niciodat o valoare pointerului p dar va tipri valoarea lui x.

131

Exemplu: Considerm acum urmtorul program:


# include <stdio.h> void main (void) { int x, *p; x = 10; p = x; printf("%d",*p);

Func ia printf() nu va afia niciodat valoarea lui x (care este 10), dar va tipri o valoare necunoscut. Aceasta datorit atribuirii greite:
p = x;

Instruc iunea atribuie valoarea 10 pointerului p, care se presupune c reprezint o adres i nu o valoare. Pentru a corecta programul, trebuie scris: p = &x;

7.2.7. Alocarea dinamic a memoriei


Exist dou metode principale prin care un program C poate memora informa ii n memoria principal a calculatorului. variabilelor globale, memoria ce li se aloc este fix pe tot timpul execu iei programului. Pentru variabilele locale, programul aloc memorie n spa iul stivei, n timpul execu iei programului. Dei variabilele locale sunt eficient de implementat, n C, de multe ori, utilizarea acestora, necesit cunoaterea n avans a cantit ii de memorie necesare n fiecare situa ie.
H i gh Stiva M em or ie liber pentr u alocar e (heap) V ar iabile globale (statice) L ow P r ogr am M em ori a

Prima metod folosete variabilele globale i locale. n cazul

A doua metod de alocare a memoriei, const n utilizarea func iilor de alocare dinamic malloc() i free(). Prin aceast metod, un program aloc memorie pentru diversele informa ii n spa iul
132

memoriei libere numit heap, plasat ntre programul util i memoria sa permanent i stiv. Se observ c stiva crete n jos, iar dimensiunea acesteia depinde de program. Un program cu multe func ii recursive va folosi mult mai intens stiva n compara ie cu un program ce nu utilizeaza func ii recursive, aceasta deoarece adresele de retur i variabilele locale corespunztoare acestor func ii sunt salvate n stiv. Func iile malloc() i free() Aceste func ii formeaz sistemul de alocare dinamic a memoriei n C i fac parte din fisierul antet <stdlib.h>. Acestea lucreaz mpreun i utilizeaz zona de memorie liber plasat ntre codul program i memoria sa permanent (fix) i vrful stivei, n scopul stabilirii i men inerii unei liste a variabilelor memorate. De fiecare dat cnd se face o cerere de memorie, func ia malloc() aloc o parte din memoria rmas liber. De fiecare dat cnd se face un apel de eliberare a memoriei, func ia free() elibereaz memorie sistemului. Declararea func iei malloc() se face sub forma:
void *malloc (int numar_de_bytes);

Aceasta ntoarce un pointer de tip void, ceea ce nseamn c trebuie utilizat un ablon explicit de tip atunci cnd pointerul returnat de malloc() se atribuie unui pointer de un alt tip. Dac apelul lui malloc() se execut cu succes, malloc() va returna un pointer la primul byte al zonei de memorie din heap ce a fost alocat. Dac nu este suficient memorie pentru a satisfce cererea malloc(), apare un eec i malloc() returneaz NULL. Pentru determinarea exact a numrului de bytes necesari fiecrui tip de date, se poate folosi operatorul sizeof(). Prin aceasta, programele pot deveni portabile pe o varietate de sisteme. Func ia free() returneaz sistemului memoria alocat anterior cu malloc(). Dup eliberarea memoriei cu free(), aceasta se poate reutiliza folosind un apel malloc(). Declararea func iei free() se realizeaz sub forma:
free(void *p);

Func ia free() elibereaz spa iul indicat de p i nu face nimic dac p este NULL. Parametrul actual p trebuie s fie un pointer la un spa iu alocat anterior cu malloc(), calloc() sau realloc().

Exemplu: Urmtorul program va aloca memorie pentru 40 de ntregi, va afia valoarea acestora, dup care elibereaz zona, utiliznd free():

133

# include <stdio.h> # include <stdlib.h> void main(void) { int t, *p; p = (int *) malloc(40*sizeof(int)); if (!p) printf("Out of memory \n"); //Verificati neaparat daca p este un pointer corect else { for (t=0; t<40; ++t) *(p + t) = t; for (t=0; t < 40; ++t) printf("%d", *(p + t)); free(p); } }

Func iile calloc() i realloc() Func ia calloc() aloc un bloc de n zone de memorie, fiecare de dim octe i i seteaz la 0 zonele de memorie; func ia returneaz un pointer la acel bloc (adresa primului octet din bloc). Declararea func iei se face cu:
void *calloc(unsigned int n, unsigned int dim);

Func ia realloc() primete un pointer la un bloc de memorie alocat n prealabil (declarat pointer de tip void) i redimensioneaz zona alocat la dim octe i (dac este nevoie, se copiaz vechiul con inut ntr-o alt zon de memorie). Declararea func iei se face cu:
void *realloc(void *ptr, unsigned int dim);

7.2.8. Pointeri la structuri


Limbajul C recunoate pointerii la structuri n acelai mod n care se recunoate pointerii la orice alt tip de variabil. Declararea unui pointer la structur se face plasnd operatorul * n fa a numelui unei variabile structur. De exemplu, pentru structura addr definit mai nainte, urmtoarea instruc iune declar pe addr_pointer ca pointer la o dat de acest tip :
struct addr *addr_pointer;

O utilizare important a pointerilor la structur const n realizarea apelului prin adres ntr-o func ie. Cnd unei func ii i se transmite ca argument un pointer la o structur, calculatorul va salva i va reface din stiv numai adresa structurii, conducnd astfel la cresterea vitezei de executare a programului.

134

Pentru a gsi adresa unei variabile structur, se plaseaz operatorul & naintea numelui variabilei structur. De exemplu, dndu-se urmtorul fragment :
struct balanta { float balance; char name[80]; } person; struct balanta *p; /* se declara un pointer la structura */

atunci:
p = &person;

plaseaz adresa lui person n pointerul p. Pentru a referi elementul balance, se va scrie:
(*p).balance

Deoarece operatorul punct are prioritate mai mare dect operatorul *, pentru o referire corect a elementelor unei structuri utiliznd pointerii sunt necesare paranteze. Actualmente, pentru referirea unui element al unei variabile structur dndu-se un pointer la acea variabil, exist dou metode: Prima metod utilizeaz referirea explicit a pointerului numestructur i este considerat o metoda nvechit (obsolete), iar a doua metod, modern, utilizeaz operatorul sgeat -> (minus urmat de mai mare). Exemplu: Pentru a vedea cum se utilizeaz un pointer-struct, examinm urmtorul program care afieaz ora, minutul i secunda utiliznd un timer software.
# include <stdio.h> void actualizeaza(); void afiseaza(), delay(); struct tm { /* se defineste structura tm */ int ore; int minute; int secunde;}; void main() {struct tm time; // Structura time de tip tm time.ore = 0; time.minute = 0; time.secunde = 0; for (;;) { actualizeaza (&time); afiseaza (&time); }} void actualizeaza(t) /*Versiunea 1- referirea explicita prin pointeri */ struct tm *t; {

135

(*t).secunde ++; if ((*t).secunde == 60) { (*t).secunde = 0; (*t).minute ++; } if ((*t).minute == 60) { (*t).minute = 0; (*t).ore ++;} if ((*t).ore == 24) (*t).ore = 0; delay();} void afiseaza(t) // Se defineste functia afiseaza() struct tm *t; { printf ("%d : ", (*t).ore); printf ("%d : ", (*t).minute); printf ("%d ", (*t).secunde); printf ("\n");} void delay() /* Se defineste functia delay() */ { long int t; for (t = 1;t<140000000;++t);}

Pentru ajustarea duratei ntrzierii se poate modifica valoarea contorului t al buclei. Se vede ca programul definete o structur global numit tm, dar nu declar variabilele. In interiorul func iei main() se declar structura "time" i se ini ializeaz cu 00:00:00. Programul transmite adresa lui time func iilor actualizeaza() i afiseaza(). n ambele func ii, argumentul acestora este declarat a fi un pointer-structur de tip "tm". Referirea fiecrui element se face prin intermediul acestui pointer. Elementele unei structuri pot fi referite utiliznd n locul operatorului ".", operatorul "->". De exemplu : (*t).ore este echivalent cu t -> ore Atunci programul de mai sus se poate rescrie sub forma:
# include <stdio.h> void actualizeaza(); void afiseaza(), delay(); struct tm { /* se defineste structura tm */ int ore; int minute; int secunde;}; void main() {struct tm time; // Declara structura time de tip tm time.ore = 0; time.minute = 0; time.secunde = 0;

136

for (;;) { actualizeaza (&time); afiseaza (&time); }} void actualizeaza(t) /*Versiunea 1- referirea explicita prin pointeri */ struct tm *t; { t->secunde ++; if (t->secunde == 60) { t->secunde = 0; t->minute ++; } if (t->minute == 60) { t->minute = 0; t->ore ++;} if (t->ore == 24) t->ore = 0; delay();} void afiseaza(t) // Se defineste functia afiseaza() struct tm *t; { printf ("%d : ", t->ore); printf ("%d : ", t->minute); printf ("%d ", t->secunde); printf ("\n");} void delay() /* Se definete func ia delay() */ { long int t; for (t = 1;t<140000000;++t);}

7.2.9. Structuri dinamice liniare de tip list


Structura a fost introdus ca fiind o grup (colec ie) ordonat de date care pot fi de tipuri diferite i care au anumite legturi logice ntre ele. Adesea, aceast grup de date se repet de mai multe ori. Se ajunge astfel la no iunea de tablou, ale crui elemente sunt fiecare cte o structur. Tabloul de date definit n acest fel este i el de acest tip nou, pe care l mai numim i tip structurat. Dup cum s-a remarcat, n exemplele de pn acum am folosit structuri de tip static. Static se refer la faptul c tablourile de structuri au dimensiuni predefinite. Termenul structuri de date statice exprim ideea c putem modifica cu uurin valorile componentelor dar este foarte dificil s mrim numrul lor peste limita maxim declarat nainte de compilare. Mai mult, prin tergerea unor elemente structur dintr-un tablou de structuri ob inem goluri n tablou, pe care le putem umple numai printr-o gestiune foarte precis a pozi iilor din tablou. Folosind pointeri la tabloul de structuri, este foarte posibil s indicm spre un element care a fost ters. Dac dorim o reprezentare contigu

137

n memorie, va trebui s compactm (sau s defragmentm) tabloul la fiecare tergere a unui element de tip structur. Mai mult, dac dorim s schimbm ordinea n care s-au stocat elementele din tablou sau s inserm ntr-o pozi ie intermediar un element nou, aceaste opera ii devin foarte anevoioase. ntr-un exemplu anterior am folosit secven a
# define SIZE 100 struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; } addr_info[SIZE];

Rezult c am definit un tablou static cu 100 de elemente, cu numele addr_info, la care fiecare element este o structur cu ablonul addr. Dac n aceast aplica ie, chiar n timpul execu iei programului, constatm c avem nevoie de mai mult de 100 de rezervri de memorie, nu exist nici o posibilitate de a mri tabloul fr a modific i apoi recompila sursa programului. Tabloul trebuie redeclarat cu o dimansiune mai mare (n cazul nostru prin #define SIZE 200, de exemplu), apoi se recompileaz programul i abia apoi se execut cu succes. Acest lucru prezent dou inconveniente (vezi [Mocanu, 2001]): 1- Execu ia i ob inerea rezultatelor sunt amnate i produc ntrzieri pentru modificarea programului surs, recompilare i reintroducerea datelor care fuseser deja introduse pn n momentul n care s-a constatat necesitatea mririi tabloului. 2- Este posibil ca programul surs s nu fie disponibil. Eliminarea neajunsurilor prezentate mai sus se face prin implementarea listelor cu ajutorul unor structuri de date dinamice. Cnd apare necesitatea introducerii unui element n list, se va aloca dinamic spa iu pentru respectivul element, se va crea elementul prin nscrierea informa iilor corespunztoare i se va lega n list. Cnd un element este scos din list spa iul de memorie ocupat de acesta va fi eliberat i se vor reface legturile. Structurile dinamice se construiesc prin legarea componentelor structurii, numite variabile dinamice. O variabil dinamic ce intr n componen a unor structuri de date dinamice (nod) prezint n structura sa dou pr i:

138

1. Partea de informa ie (info) ce poate apar ine unui tip simplu (int, char, float, double, etc.) conform cu necesit ile problemei. 2. Partea de legtur (link) ce con ine cel pu in un pointer de legtur (next) la o alt variabil dinamic, de obicei de acelai tip, ce realizeaz nln uirea variabilelor dinamice n structuri de date dinamice. Dintre structurile de date dinamice, cele mai simple i mai utilizate sunt listele. Lista este o structur liniar, de tipul unui tablou unidimensional (vector), care are un prim element i un ultim element. Celelalte elemente au un predecesor i un succesor. Elementele unei liste se numesc noduri. La rndul lor, listele pot fi grupate n mai multe categorii, cele mai importante fiind listele simplu nln uite, listele circulare i listele dublu legate. Principalele opera ii care se pot efectua asupra unei liste sunt: crearea listei, adugare/tergere/modificare au unui element (nod), accesul la un element i tergerea n totalitate a listei. Lista simplu nln uit este cel mai simplu tip de list din punctul de vedere al legrii elementelor: legtura ntre elemente este ntr-un singur sens, de la primul ctre ultimul. Exist un nod pentru care pointerul spre nodul urmtor este NULL. Acesta este ultimul nod al listei simplu nln uite (sau simplu legate). De asemenea, exist un nod spre care nu pointeaz nici un alt nod, acesta fiin primul nod al listei. O list simplu nln uit poate fi identificat n mod unic prin primul element al listei. Determinarea ultimului element se poate face prin parcurgerea secven ial a listei pn la ntlnirea nodului cu pointerul spre nodul urmtor cu valoarea NULL.

info next

info next List simpl nln uit

info NULL

Listele circulare sunt un alt tip de liste pentru care rela ia de preceden nu mai este liniar ci ultimul element pointeaz ctre primul. Procedurile necesare pentru crearea i utilizarea unei liste circulare sunt extrem de asemntoare cu cele de la liste liniare, cu

139

singura deosebire c ultimul element nu are adresa de pointer vid (NULL) ci adresa primului element.
info next info next List circular info next

Listele dublu legate sunt utile n rezolvarea unor probleme care necesit parcurgeri frecvente ale structurilor dinamice n ambele sensuri. Ele pot fi privite ca structuri dinamice ce combin dou liste liniare simplu nln uite ce partajeaz acelai cmp comun info, una fiind imaginea n oglind a celeilalte.
info next NULL info next previous List dublu legat info NULL previous

Pointerul next indic spre urmtorul nod, iar cmpul previous indic spre cmpul anterior. Vom prezenta n continuare modul n care putem proiecta func iile principale care ac ioneaz asupra unei structuri dinamice. Pentru aceasta vom utiliza dou variabile globale de tip pointer, una care pointeaz spre primul nod al listei iar cealalt spre ultimul nod al listei. Vom denumi aceste variabile first respectiv last. Particularizrile se vor face pe exemplul bazei de date construite anterior. Tipul unui nod se declar n felul urmtor:
General struct tip_nod { declaratii struct tip_nod *next; }; Particular struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; struct addr *next; };

140

Att la crearea unei liste ct i la inserarea unui nod se va apela func ia malloc() pentru a rezerva spa iu de memorie pentru un nod. Zona alocat prin intermediul func iei malloc() se poate elibera folosind func ia free(). Propunem conceperea unui program asemntor cu programul de exploatare a unei baze de date conceput anterior dar care s foloseasc n locul unui tablou static o list simplu nln uit. Programul poate fi extins ulterior pentru structuri dinamice mai complexe, care s foloseasc liste dublu nln uite sau circulare. Programul va avea urmtoarele facilit i: 1. Crearea unei liste simplu nln uite n memorie (pentru prima oar). 2. Exploatarea unei liste simplu nln uite n memorie: 2.1. Inserarea unor nregistrri (noduri): a) Inserarea unui nod naintea primului nod al listei b) Inserarea unui nod nainte/dup un nod intern al listei c) Inserarea unui nod dup ultimul nod al listei (adugare la coada listei) 2.2. tergerea unor nregistrri a) tergerea primului nod al listei b) tergerea unui nod intern al listei c) tergerea ultimului nod al listei 3. Salvarea din memorie pe disc a unei liste simplu nln uite 4. Citirea de pe disc n memorie a bazei de date salvate anterior 5. Afiarea pe ecran, nregistrare cu nregistrare, a bazei de date con inute n lista dinamic. Programul este prevzut cu o intefa simpl prin care utilizatorul poate alege dintre op iunile pe care le are la dispozi ie. Interfa a este sub forma unui meniu din care, prin tastarea ini ialelor comenzilor, acestea se lanseaz n execu ie. Vom descrie pe rnd func iile care ndeplinesc sarcinile enumerate mai sus. Pentru o mai bun proiectare a programului, vom folosi att modularizarea intern prin construirea mai multor func ii ct i o modularizare extern prin izolarea programului principal de restul func iilor care se folosesc.

141

Baz de date cu list simplu nln uit Afiare pe ecran a nregistrrilor din baza de date

Interfa a cu utilizatorul

Comenzi pentru citire/scriere pe disc

Comenzi pentru procesarea listei dinamice

Citire de pe disc n lista

Salvarea pe disc a listei

Inserare

dinamic

dinamice din memorie

tergere

- prima nregistrare - ultima nregistrare - nregistrare intermediar

Exemplu: Programul principal bd_main.c


# include "local.h" void main() { char choice; for (; ;) { choice = menu(); switch (choice) { case 'c' : create_list(); break; case 'l' : loadf_list(); break; case 's' : savef_list(); break; case 'd' : display_list(); break; case 'i' : insert(); break; case 'e' : erase(); break; case 'q' : exit(0); break;}}}

Fiierul header local.h este:


# include <stdio.h> # include <ctype.h> # include <string.h> # include <stdlib.h> typedef struct addr { char name[20]; char street[30]; char city[15]; char state[10];

142

unsigned int zip; struct addr *next;}TNOD; TNOD *first, *last; FILE *fp; extern int create_list(); extern char menu(); extern void display_list(), insert(), erase(); extern int loadf_list(), savef_list(); extern TNOD *add_nod();

Cu ajutorul acestui fiier header se realizeaz definirea ablonului structurii addr i apoi, prin comanda typedef, se definete un nou tip de date numit TNOD. First i last sunt pointeri la structuri de acest tip iar fp este pointer la fiier (file pointer). Cu extern se definesc prototipurile unor func ii care nu se gsesc n fiierul bd_main.c ci n fiierul bd_bib.c unde sunt colectate toate func iile pe care le folosim. Acest fiier are la rndul su un fiier header numit local1.h care con ine:
# include <stdio.h> # include <ctype.h> # include <string.h> # include <stdlib.h> extern FILE *fp; typedef struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; struct addr *next;}TNOD; extern TNOD *first, *last;

Prin cele dou fiiere header cele dou fiiere surs bd_main.c i bd_bib.c se pot compila mpreun, rezultnd un singur fiier executabil. Interfa a cu utilizatorul Aceasta const ntr-un meniu principal, care permite accesarea func iilor principale, i din dou submeniuri pentru opera iile de tergere i inserare de nregistrri.
/* Functia menu() principala */ char menu() { char s[5],ch; do { printf ("\n(C)reate new list\n"); printf ("(L)oad list from file\n"); printf ("(S)ave to file\n");

143

printf ("(D)isplay list\n"); printf ("(I)nsert record\n"); printf ("(E)rase\n"); printf ("(Q)uit\n"); printf (" Alegeti optiunea: "); gets(s); ch=s[0]; } while (!strrchr("clsdieq",ch)); return tolower(ch); } //meniu functia de stergere char menu_del() { char s[5],ch; do { printf ("(E)ntire list delete\n"); printf ("(F)irst record delete\n"); printf ("(L)ast record delete\n"); printf ("(I)ntermediate delete\n"); printf ("(Q)uit\n"); printf (" Option ? "); gets(s);ch=s[0]; } while (!strrchr("efliq",ch)); return tolower(ch); } //meniu functia de inserare char menu_insert() { char s[5],ch; do { printf ("(F)irst record insert\n"); printf ("(L)ast record insert\n"); printf ("(I)ntermediate insert\n"); printf ("(Q)uit\n"); printf (" Option ? "); gets(s);ch=s[0]; } while (!strrchr("fliq",ch)); return tolower(ch); }

ncrcarea bazei de date de pe disc Baza de date este nregistrat pe disc n fiierul maillist.dat care se creaz la prima salvare a listei dinamice din memorie. Func ia loadf_nod() citete o nregistrare (record) din fiier, returnnd 1 n caz de reuit, -1 dac se ajunge la sfritul fiierului i 0 n cazul n care exist o eroare de citire de pe disc.
/* Functia loadf_nod() from file*/ int loadf_nod(TNOD *p) {if (fread(p,sizeof(TNOD),1,fp)==1) return 1; else if (feof(fp)) {fclose (fp); return -1;} else {printf ("File read error\n"); return 0;}}

144

/* Functia loadf_list() from file */ int loadf_list() { int n=sizeof(TNOD),i; TNOD *p; first=last=NULL; if ((fp = fopen("maillist","rb")) == NULL) { printf("Cannot open file\n ");return 0;} while (((p=(TNOD *)malloc(n))!=NULL)&&((i=loadf_nod(p))==1)) if (first==NULL){ /* prima creare */ first=last=p; first->next=NULL;} else { last->next=p;last=p; last->next=NULL;} if (p==NULL){ printf("Memorie insuficienta pentru lista\n"); return 0;} free(p);return i;}

Func ia loadf_list() aloc fiecare nregistrare citit de pe disc unui nod al listei dinamice construite n memorie. n pointerii first i last se stocheaz n permanen adresele primei i ultimei nregistrri (nod sau structur). Salvarea bazei de date pe disc Reprezint opera iunea invers celei de citire. Lista simplu nln uit din memorie se salveaz n ntregime pe disc n fiierul maillist.dat. Spre deosebire de func ia loadf_list() care apela la func ia loadf_nod(), func ia savef_list() nu face nici un apel la o alt func ie definit de utilizator:
/* Functia savef_list() */ int savef_list() { TNOD *p; if ((fp = fopen("maillist", "wb")) == NULL) { printf (" Cannot open file\n ");return 0;} p=first; do { if(fwrite(p, sizeof(TNOD), 1,fp) !=1) {printf (" File write error \n "); fclose (fp);return 0;} p=p->next;} while (p!=NULL); fclose (fp);return 1;}

Crearea unei liste simple nln uite La nceput variabilele first i last au valoarea NULL, lista fiind vid. Crearea unei liste se face prin func ia create_list. Ea returneaz 0

145

pentru eroare i 1 dac opera ia reuete. Prin aceast func ie se ini ializeaz o list dinamic n memorie, fr a face o citire a unei baze de date create anterior. Aceasta este prima func ie care se apeleaz cnd construim o baz de date nou.
/* Functia create_list() new */ void create_list() { int n=sizeof(TNOD); char ch='c'; TNOD *p; first=last=NULL; while ((p=(TNOD *)malloc(n))!=NULL) {p=add_nod(p); if (first==NULL){ /* prima creare */ first=last=p; first->next=NULL;} else { last->next=p; last=p; last->next=NULL;} printf ("Exit? (x): "); if ((ch=getchar())=='x') break;} if (p==NULL){ printf("Memorie insuficienta pentru lista\n"); free(p);}}

Afiarea listei dinamice din memorie Prin aceast opera ie, realizat de func ia display_list(), se afieaz secven ial toate nregistrrile con inute n nodurile listei pornind de la prima i terminnd cu ultima.
// functie de afisare antet void disp_antet() { printf("\n%20s","Name"); printf("%30s","Street"); printf("%15s","City"); printf("%10s","State"); printf("%5s\n","Zip");} // afisare o singura inregistrare (nod) void disp_nod(TNOD *p) { printf("%20s",p->name); printf("%30s",p->street); printf("%15s",p->city); printf("%10s",p->state); printf("%5d",p->zip);} /* Functia display_list() */ void display_list() { TNOD *p; disp_antet();

146

p=first; if (p!=NULL) do { disp_nod(p); p=p->next; getchar();} while (p!=NULL); else printf("Lista vida !\n");}

Func ia display_list() apeleaz la func ia disp_nod() care afiez o singur nregistrare. Dac este nevoie de afiarea unui cap de tabel (antet) se apeleaz la func ia disp_antet(). Inserarea unor noduri n lista dinamic Aceast opera ie presupune introducerea unor noduri (nregistrri) fie la nceputul listei naintea primului nod, fie la sfritul su (adugare) dup ultimul nod, fie ntre dou noduri vecine nesituate la extremit i. Func iile listate mai jos realizeaz aceste sarcini.
// functia de inserare void insert() { char choice; for (; ;) { choice = menu_insert(); switch (choice) { case 'f' : ins_first(); break; case 'l' : ins_last(); break; case 'i' : ins_int(); break; case 'q' : break;} break;}} /* Functia insert_last() */ void ins_last() { int n=sizeof(TNOD); char ch='c',s[2]; TNOD *p; /* ne pozitionam pe ultimul nod */ p=first; while (p->next!=NULL) p=p->next; // se creaza lista in memorie while (ch!='x') { p=(TNOD *)malloc(n); last->next=p; p=add_nod(p); p->next=NULL; last=p; printf("Exit? (x): "); gets(s);ch=s[0];}} /* Functia insert_first() */ void ins_first() { int n=sizeof(TNOD); char ch='c',s[2];

147

TNOD *p; while (ch!='x') { p=(TNOD *)malloc(n); p->next=first; p=add_nod(p); first=p; printf("Exit? (x): "); gets(s);ch=s[0];}} // inserare dupa inregistrarea afisata void ins_int() { TNOD *p, *pi; char ch='n', s[2]; disp_antet(); p=first; while ((p!=NULL)&&(ch!='y')) { disp_nod(p); printf("Here ? [y]: "); gets(s);ch=s[0]; if (ch!='y') p=p->next;} pi=(TNOD *)malloc(sizeof(TNOD)); pi=add_nod(pi); pi->next=p->next; p->next=pi;}

La inserarea unui nod, este nevoie ca acesta s fie legat de cele ntre care se introduce cu ajutorul pointerilor next. Dac se dorete ca nodul inserat s fie primul sau ultimul din list, este nevoie s modificm pointerii globali first i last.

tergerea unor noduri din lista dinamic Nodurile pe care dorim s le tergem pot fi interioare sau se pot afla la extremit i. n acest caz, este nevoie s modificm pointerii first i last . n toate cazurile este nevoie s refacem legturile distruse dup dispari ia prin tergere a unor noduri. tergerea nu este efectiv, ci numai se refac legturile i se elibereaz cu func ia free() zona de memorie alocat n prealabil (prin inserare sau creare) cu func ia malloc(). Mai mult, avem op iunea de a terge ntreaga list din memorie n scopul nlocuirii n totalitate a datelor con inute n nregistrri.
// func ia de tergere void erase() { char choice; for (; ;) { choice = menu_del(); switch (choice) { case 'e' : del_list(); break;

148

case 'f' : del_first(); break; case 'l' : del_last(); break; case 'i' : del_int(); break; case 'q' : break;} break;}} // se sterge intreaga lista si se elibereaza memoria void del_list() { TNOD *p,*pu; p=first;pu=p->next; while (pu!=NULL) {free(p); p=pu;pu=pu->next;} first=NULL;last=NULL;} // sterge prima inregistrare void del_first() { int n=sizeof(TNOD); char ch='c',s[2]; TNOD *p,*pu; while (ch!='x') { p=first;pu=p->next; free(p); first=pu; printf("Exit? (x): "); gets(s);ch=s[0];}} // stergere ultima inregistrare void del_last() { int n=sizeof(TNOD); char ch='c',s[2]; TNOD *p; /* ne pozitionam pe penultimul nod */ while (ch!='x') { p=first; while (p->next!=last) p=p->next; free(p->next); p->next=NULL; last=p; printf("Deleted. Exit? (x): "); gets(s);ch=s[0];}} // stergere inregistrare intermediara void del_int() { TNOD *p, *pa, *pu; char ch='n', s[2]; disp_antet(); pa=first;p=pa->next;pu=p->next; while ((p!=last)&&(ch!='y')) { disp_nod(p); printf("Delete ? [y]: "); gets(s);ch=s[0]; if (ch='y') {pa->next=pu; free(p);} else {pa=p;p=pu;pu=pu->next;}}}

149

Capitolul VIII FUNC II


8.1. Forma general a unei func ii
Principalul mijloc prin care se pot modulariza programele C este oferit de conceptul de func ie (unele func ii standard au fost deja folosite pentru diverse opera ii). n C orice func ie "ntoarce" (returneaz), dup apel, o valoare al crui tip trebuie cunoscut. n practic, ns, de multe ori valorile returnate de func ii sunt ignorate. Standardul limbajului C permite chiar declararea explicit a func iilor care nu returneaz valori ca fiind de tip void. n C o func ie poate fi definit, declarat i apelat. Definirea unei func ii C se realizeaz dup urmtorul format general:
nume_func ie (lista_parametri) declara ii_parametri { declara ii _locale instruc iuni } tip

sau, o form mai nou adoptat de ANSI-C n 1989:


tip nume_func ie (declara ii _parametri) { declara ii _locale instruc iuni }

Tipul unei func ii corespunde tipului valorii pe care func ia o va returna utiliznd instruc iunea return. Acesta poate fi un tip de baz (char, int, float, double etc.) sau un tip derivat (pointer, structur etc.). Dac pentru o func ie nu se specific nici un tip, atunci, implicit se consider c func ia ntoarce o valoare ntreag. Lista parametrilor, lista_parametri, este o list de nume de variabile separate prin virgul care vor primi valorile argumentelor n momentul apelrii acesteia. Tipul acestor parametri este descris fie n paragraful declara ii_parametri, fie direct n lista parametrilor. Lista parametrilor este nchis ntre paranteze. Chiar dac o func ie nu are parametri, parantezele nu trebuie s lipseasc.

150

De exemplu, func ia max(a, b), care returneaz cel mai mare dintre numerele ntregi a i b, se poate defini sub forma:
int max(a, b) int a, b; { if (a > b) return (a); else return (b); }

sau

int

max(int a, int b)

{ if (a > b) return (a); else return (b); }

n cazul n care tipul parametrilor formali ai unei func ii nu se declar, atunci ei sunt considera i implicit de tip int. n cazul compilatoarelor moderne, programul urmtor va genera dou avertismente. Exemplu:
# include <stdio.h> float max(); // Prototipul functiei max() float x; void main() { x = max(3, 4); printf("max= %d",x); } float max(a, b) float a, b; { if (a > b) return (a); else return (b); }

n urma compilrii va rezulta:


Compiling... test.c C:\cpp_examples\test.c(11): warning C4244: 'return': conversion from 'int' to 'float', possible loss of data C:\cpp_examples\test.c(13): warning C4244: 'return': conversion from 'int' to 'float', possible loss of data test.obj - 0 error(s), 2 warning(s)

Aceste avertismente sunt generate deoarece, la primul pas al compilrii, la parcurgerea liniei de declarare a func iei:
float max(a,b)

parametrii formali a i b sunt considera i de tip ntreg. Programul, modificat ca mai jos, va duce la o compilare fr probleme: Exemplu:
# include <stdio.h>

151

float max(); float x; void main() { x = max(3.2, 4.1); printf("max= %d\n",x); } float max(float a, float b) { if (a > b) return (a); else return (b); }

Tot corect va rula i programul:


# include <stdio.h> int max(); int x; void main() { x = max(-3,4); printf("max= %d\n",x); } int max(a,b) { if (a > b) return (a); else return (b); }

Se observ c nu mai este nevoie de declararea explicit a parametrilor formali de tip ntreg a i b.

8.2. Rentoarcerea dintr-o func ie


Mai inti precizm c instruc iunea return are dou utilizri importante: return determin ieirea imediat din func ia n care se afl instruc iunea i rentoarcerea n programul apelant; return poate fi folosit pentru a ntoarce o valoare. Rentoarcerea dintr-o func ie n programul apelant (func ia apelant) se poate face n dou moduri: a) Dup parcurgerea codului corespunztor func iei se revine n programul apelant la instruc iunea imediat urmtoare. Exemplu: Aceasta func ie tiprete un ir n ordine invers:
# include <string.h> void afis_invers(char s[]); void main() { char s[10];

152

printf("Introduceti un sir de tastatura (max 10)\n"); scanf("%s",s); afis_invers(s); } void afis_invers(char s[]) { register int t; for (t = strlen(s)-1; t >= 0; t--) printf("%c", s[t]); printf("\n"); }

caracrere

de

la

b) Al doilea mod de ntoarcere dintr-o func ie se realizeaz utiliznd func ia return. Func ia return poate fi folosit fr nici o valoare asociat. Exemplu: Func ia urmtoare afieaz rezultatele ridicrii unui numr ntreg la o putere ntreag pozitiv:
power (baza, exp){ int baza, exp, i; scanf("%d %d", &baza, &exp); if (exp < 0) return; /* Functia se termina daca exp e negativ */ i = 1; for (; exp; exp--) i = baza * i; printf (" Rezultatul este %d \n", i); }

Dac exponentul exp este negativ, instruc iunea return determin terminarea func iei nainte ca sistemul s ntlneasc }, dar nu returneaz nici o valoare. O func ie poate con ine mai multe instruc iuni return, care pot simplifica anumite algoritme.

8.3. Valori returnate


Toate func iile, cu excep ia celor daclarate a fi de tip void, returneaz o valoare. Aceast valoare este fie explicit specificat prin return, fie este zero dac nu se utilizeaz instruc iunea return. Dac o func ie este declar ta ca fiind de tip void, aceasta poate fi folosit n orice expresie C. O func ie nu poate fi membrul stng ntr-o expresie de atribuire. De exemplu, instruc iunea: swap(x,y) = 100; este greit. Func iile care nu sunt de tip void se pot mpr i n trei categorii: 1) Func ii "pure" sunt func iile care efectueaz opera ii asupra argumentelor i returneaz o valoare de baz pe acea opera ie. Exemplu: sqrt() i sin() returneaz respectiv radcina ptrat i sinusul argumentului. 2) A doua categorie de func ii sunt cele care manipuleaz informa ii i ntorc o valoare care arat reuita sau eecul acestei

153

manipulri. Un exemplu este fwrite() folosit pentru a scrie informa ii pe disk. Dac scrierea se face cu succes, fwrite() ntoarce numrul de octe i nscrii (ceru i s se nscrie); orice alt valoare indic apari ia unei erori. 3) A treia categorie de func ii sunt cele care nu trebuie s ntoarc o valoare explicit. De exemplu, func ia printf() ntoarce numrul de caractere tiprite, numr care, de obicei, nu are o utilizare ulterioar. Dac pentru o func ie care returneaz o valoare nu se specific o opera ie de atribuire, calculatorul va ignora valoarea returnat.

Exemplu: Considerm urmtorul program care utilizeaz func ia mul():


# include <stdio.h> mul(); void main (void){ int x, y, z; x = 10; y = 20; z = mul(x, y); //- primul apel al lui mul() printf("%d\n", mul(x,y)); /- al doilea apel al lui mul() mul(x,y); //- al treilea apel al lui mul() } mul(a,b) // Se defineste functia mul() { return a*b; }

Linia a atribuie valoarea returnat de mul() lui z. n linia b, valoarea returnat nu este atribuit, dar aceasta este utilizat de printf(). In linia c valoarea returnat este pierdut, deoarece nu se atribuie nici unei variabile ce va fi utilizat n alt parte a programului.

8.4. Domeniul unei func ii


n C, fiecare func ie este un bloc de instruc iuni. Codul unei func ii este propriu acelei func ii i nu poate fi accesat (utilizat) prin nici o instruc iune din orice alt func ie, cu excep ia instruc iunii de apel al acelei func ii. (De exemplu, nu putem utiliza goto pentru a realiza saltul dintr-o func ie n mijlocul unei alte func ii). Blocul de instruc iuni care descrie corpul unei func ii este separat de restul programului i dac acesta nu utilizeaz variabile globale sau date, el nici nu poate afecta, nici nu va fi afectat de alte pr i ale programului. Codul i datele care sunt definite n interiorul unei func ii nu pot interac iona cu codul i datele definite n alt func ie, deoarece cele dou func ii au scopuri diferite.

154

n cadrul unei func ii se deosebesc trei tipuri de variabile, astfel: variabile locale, parametri formali i variabile globale. Domeniul unei func ii determin modul n care alte pr i ale programului pot avea acces la aceste trei tipuri de variabile stabilind i durata de via a acestora.

8.4.1. Variabile locale


Variabilele declarate n interiorul unei func ii se numesc variabile locale. Variabilele locale pot fi referite numai prin instruc iuni interioare blocului n care au fost daclarate aceste variabile. Variabilele locale nu sunt cunoscute n afara blocului n care au fost daclarate, domeniul lor limitndu-se numai la acest bloc. Mai exact, variabilele locale exist numai pe durata execu iei blocului de cod n care acestea au fost daclarate; deci o variabil local este creat la intrarea n blocul su i distrus la ieire. De obicei, blocurile de program n care se declar variabilele locale sunt func iile. Implicit, o variabil local este auto, deci se stocheaz n memoria stiv. Ea poate fi declarat i register, caz n care se stocheaz n regitrii interni ai microprocesorului sau poate fi declarat static, caz n care se stocheaz n memoria de date sau static, valoarea sa pstrndu-se i la ieirea din func ie. Exemplu:
func1() { int x; x = 10; } func2() { int x; x = -199; }

Aici variabila ntreag x este declarat de dou ori, o dat n func1() i o dat n func2(). x din func1() nu are nici o legatur cu x din func2(), deoarece fiecare x este cunoscut numai n blocul n interiorul cruia a fost declarat. Limbajul C con ine cuvntul cheie auto, care poate fi folosit pentru declararea de variabile locale. Cu toate acestea, ntruct C presupune c toate variabilele neglobale sunt prin defini ie (implicit) variabile locale, deci au atributul auto, acest cuvnt cheie nu se utilizeaz. De obicei, variabilele locale utilizate n interiorul unei func ii se declar la nceputul blocului de cod al acestei func ii. Acest lucru nu este neaprat necesar, deoarece o variabil local poate fi declarat

155

oriunde n interiorul blocului n care se utilizeaz, dar nainte de a fi folosit. Exemplu: Considerm urmtoarea func ie:
func (){ char ch; printf (" Continuam (y / n) ? : "); ch = getche(); //Se preia optiunea de la tastatura /* Daca raspunsul este yes */ if (ch == 'y') { char s[80]; /* s se creeaza numai dupa intrarea in acest bloc */ printf (" Introduceti numerele: \n "); gets (s); prelucreaza_nr (s); /* Se prelucreaza numerele */ } }

Aici, func() creeaz variabila local s la intrarea n blocul de cod a lui if i o distruge la ieirea din acesta. Mai mult, s este cunoscut numai n interiorul blocului if i nu poate fi referit din alt parte, chiar din alt parte a func iei func() care o con ine. Deoarece calculatorul creeaz i distruge variabilele locale la fiecare intrare i ieire din blocul n care acestea sunt daclarate, con inutul lor este pierdut o dat ce calculatorul prseste blocul. Astfel, variabilele locale nu pot re ine valorile lor dup ncheierea apelului func iei.

8.4.2. Parametri formali


Dac o func ie va folosi argumente, atunci aceasta trebuie s declare variabilele care vor accepta (primi) valorile argumentelor. Aceste variabile se numesc parametri formali ai func iei. Parametrii formali ai func iei se comport ca orice alt variabil local din interiorul func iei. Declararea parametrilor formali se face dup numele func iei i naintea corpului propriu-zis al func iei. Exemplu:
/* Func ia urmtoare ntoarce 1 dac apar ine irului s altfel ntoarce 0 */ # include <stdio.h> int func (char s[10],char c) { while (*s) if (*s == c) return 1; else s++; return 0; } void main() { char s[10], c; scanf("%c %s", &c, &s); caracterul c

156

if (func(s, c)) printf("Caracterul se afla in sir\n"); else printf("Caracterul NU se afla in sir\n");

Func ia func() are doi parametri: s i c. Aceasta func ie ntoarce 1 dac caracterul c apar ine irului i 0 dac c nu apar ine irului. Precizm c argumentele cu care se va apela func ia trebuie s aib acelai tip cu parametrii formali declara i n func ie. Aceti parametri formali pot fi utiliza i ca orice alt variabil local. Un al doilea mod (recomandat de ANSI-C n 1989) de a declara parametrii unei func ii const n declararea complet a fiecrui parametru n interiorul parantezelor asociate func iei. De exemplu, declararea parametrilor func iei func() de mai sus se poate face i sub forma:
func (char *s, char c) { . . . . . . . . . . . }

8.4.3. Variabile globale


Spre deosebire de variabilele locale, variabilele globale sunt cunoscute ntregului modul program i pot fi utilizate de orice parte a programului. De asemenea, variabilele globale vor pstra valorile lor pe durata execu iei complete a programului, deci se stocheaz n memoria static. Variabilele globale se creeaz prin declararea lor n afara oricrei func ii (inclusiv main()). Variabilele globale pot fi plasate n orice parte a programului, evident n afara oricrei func ii, i nainte de prima lor utilizare. De obicei, variabilele globale se plaseaz la nceputul unui program, mai exact naintea func iei main(). Exemplu:
int count; /* count este global */ void main (void) { count = 100; func1(); } func1() /* Se defineste functia func1() */ { int temp; /* temp preia variabila globala count */ temp = count; func2(); printf ("count is %d",count); // Se va afisa 100 } func2() /* se defineste functia func2() */ { int count; /* count este local */

157

for (count = 1; count < 10; count ++) printf ("%2d\n" , count); }

Se observ c, dei nici func ia main() i nici func ia func1() nu au declarat variabila count, ambele o folosesc. Func ia func2() a declarat o variabil local count. Cnd se refer la count, func2() se va referi numai la variabila local count i nu la variabila global count declarat la nceputul programului. Reamintim c, dac o variabil global i o variabil local au acelai nume, toate referirile la numele variabilei n interiorul func iei n care este declarat variabila local se vor efectua numai asupra variabilei locale i nu vor avea nici un efect asupra variabilei globale. Deci, o variabil local ascunde o variabil global. Variabilele globale sunt memorate ntr-o zon fix de memorie destinat special acestui scop (memoria static), rmnnd n aceast zon pe parcursul ntregii execu ii a programului. Variabilele declarate explicit extern sunt tot variabile globale, dar accesibile nu numai modulului program n care au fost declarate, ci i tuturor modulelor n care au fost declarate de tip extern. Un alt exemplu util i pentru n elegerea lucrului cu pointeri este urmtorul: se declar o variabil global x, i apoi dou variabile locale cu acelai nume, x, care se vor ascunde una pe cealalt. Pentru a vedea i modul n care se stocheaz n memorie aceste variabile, vom afia i loca iile de memorie pentru fiecare tip de variabil x, precum i pentru pointerul p corespunztor. Re inem c, n general, dac:
p = &x => p = &x = &(*p) = (&*)p = p *p = *(&x) = (*&)x = x

Faptul c * respectiv & sunt opera iuni complementare (inverse una celeilalte) se observ din rela iile de mai sus, din care deducem c:
&* = *& = identitate &(*z)= z; // z este pointer *(&z) = z; // z este o variabila de un anume tip

(z este de alt tip n fiecare din egalit ile de mai sus, pointer sau variabil).

158

Adresa

Memoria .. .. .. .. .. .. .. .. . x .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. p=&x .. .. .. .. .. .. .. .. q=&p

&x=adresa x

&p=adresa p

&q=adresa q

# include <stdio.h> int x = 34; /* x este global */ void main(void) { int *p = &x, *r; /* p este o variabila pointer catre un intreg */ void **q; printf("x=%d &x=%p *p=%d p=%p &p=%p\n",x,&x, *p, p, &p); { int x; x = 1; /* Acest prim x este o variabila locala ce o ascunde pe cea globala */ p = &x; printf("x=%d &x=%p *p=%d p=%p &p=%p\n",x,&x, *p, p, &p);} { int x; /* Acest al doilea x ascunde prima variabila locala x */ x = 2; // Se atribuie valoarea 2 acestui x p = &x; /* Pointerul p retine adresa variabilei x */ printf("x=%d &x=%p *p=%d p=%p &p=%p\n",x, &x, *p, p, &p); q = &p; // q retine adresa pointerului p r = *q; // r retine valoarea de la adresa q /*Cum q = &p => r = *(&p) = p => *r = *p = x */ printf("q=%p *q=%p **q=%d &q=%p\n", q, *q, *r, &q); } }

n urma execu iei programului, ob inem urmtorul rezultat:


x=34 x=1 x=2 q=0065FDF4 &x=00426A54 &x=0065FDE8 &x=0065FDE4 *q=0065FDE4 *p=34 *p=1 *p=2 **q=2 p=00426A54 &p=0065FDF4 p=0065FDE8 &p=0065FDF4 p=0065FDE4 &p=0065FDF4 &q=0065FDEC

Prin declara ia int *p = &x; variabila p este declarat variabil pointer ctre o dat de tip ntreg i este ini ializat cu adresa variabilei spre puncteaz, anume x. Pointerul q este declarat ca un pointer la un alt pointer.

159

Denum. Tipul Variab. variabilei

int x int x int x p q

global local local pointer local pointer local

Caracteristici ale Caracteristici ale variabilei variabilei pointer ataate Adresa &x Valoare Adresa &p Valoarea p *p x 00426A54 34 0065FDF4 00426A54 34 0065FDF0 0065FDEC p = &x q = &p 1 2 *p = x *q = p &q= 0065FDEC q= 0065FDF4 **q =2 0065FDF4 0065FDF4 0065FDF0 0065FDEC 1 2

Acelai lucru se face pentru celelalte dou variabile locale. Din interpretarea rezultatelor de mai sus putem trage urmtoarele concluzii. Spre exemplu,
printf(%d,x); este echivalent cu printf(%d,*p); scanf(%d,&x); este echivalent cu scanf(%d,p);

dac n prealabil s-a fcut atribuirea p = &x; Se mai observ cum pointerul p, care ini ial indica spre variabila global x, este ncrcat cu adresa de memorie 4336176 (00426A54 H), pe cnd n cazurile cnd indica variabilele locale x se alocau adresele de memorie 6684140 (0065FDF0 H) i 6684144 (0065FDEC H), adrese adiacente, la un interval de 4 octe i, att ct sunt aloca i pentru variabila de tip ntreg. Se observ c variabila global se afl ntr-o alt zon de memorie dect variabilele locale. Modul de lucru cu pointerii este scos n eviden prin instruc iunile:
q=&p; r=*q; // q retine adresa pointerului p // r retine valoarea de la adresa q // q=&p => r = *(&p) = p => *r = *p = x printf("q=%p *q=%p **q=%d &q=%p\n",q,*q,*r,&q);

prin care se ini ializeaz pointerul q cu adresa pointerului p, apoi pointerul r va primi valoarea *q, adic valoarea p. Una din principalele caracteristici a limbajelor structurate o constituie compartimentarea codului i a datelor. n C, compartimentarea se realizeaz folosind variabile i func ii. De exemplu, pentru scrierea unei func ii mul() care determin produsul a doi ntregi se pot utiliza dou metode, una general i una specific, astfel:

160

General :
mul (x, y) int x, y { return (x * y);}

Specific :
int x, y; mul () { return (x * y);}

Cnd se dorete realizarea produsului a oricror doi ntregi x i y se utilizeaz varianta general a func iei, iar cnd se dorete produsul numai al variabilelor globale x i y se utilizeaz varianta specific. Exemplu:
# include <stdio.h> # include <string.h> int count; // count este global intregului program play(); // Prototipul pentru functia play() void main(void) { char sir[80]; // sir este variabila locala a functiei main() printf("Introduceti un sir : \n"); gets(sir); play(sir); } play(char *p) // Se declara functia play() { // p este local functiei play() if (!strcmp(p, "add")) { int a,b; /* a si b sunt locale blocului if interiorul functiei play()*/ scanf ("%d %d", &a, &b); printf ("%d \n", a+b); } // int a, b nu sunt cunoscute sau evidente aici else if(!strcmp(p,"beep")) printf("%c",7); }

din

8.5. Apelul func iilor


Apelul unei func ii nseamn referirea func iei, mpreun cu valorile actuale ale parametrilor formali, precum i preluarea valorii returnate, dac este necesar. La apelul func iei, tipul argumentelor trebuie s fie acelai cu cel al tipului parametrilor formali ai func iei. Dac apar nepotriviri de tip (de exemplu, parametrul formal al func iei este de tip int, iar apelul func iei folosete un argument de tip float) de obicei, compilatorul C nu semnalizeaz eroare, dar rezultatul poate fi incorect. n C transmiterea argumentelor de la func ia apelant spre func ia apelat se face prin valori sau prin adrese.

161

a) n cazul transmiterii argumentului prin valoare, se realizeaz copierea (atribuirea) valorilor fiecrui argument n (la) cte un parametru formal al func iei apelate. Exemplu: Se apeleaz o func ie ce calculeaza ptratul unui numr ntreg.
# include <stdio.h> square(); // Prototipul functiei sqrt() void main(void) { int t = 10; printf("%d %d\n", t, square(t)); } square(x) // Se declara functia sqrt() int x; { x = x*x; return(x); }

Se observ c prin aceast metod, schimbrile survenite asupra parametrului formal x nu afecteaz variabila utilizat pentru apelul func iei (schimbrile lui x nu modific n nici un fel pe t). b) Dac transmiterea argumentului se realizeaz prin adrese, atunci la apelul func iei n loc de valori se folosesc adrese, iar n defini ie, parametrii formali se declar ca pointeri. Exemplu: O func ie swap() care schimb valorile a dou variabile reale se poate defini astfel:
void swap(float *x, float *y){ float temp; temp = x; /* temp preia valoarea de la adresa x */ *x = *y; /* valoarea de la adresa y este copiata la adresa x */ y = temp; /* la adresa y se copiaza valoarea lui temp */ }

Se observ c parametrii formali ai func iei swap() sunt pointeri la float. Programul urmtor arat modul de apel al acestei func ii.
# include <stdio.h> void swap(float *x,float *y); void main(void) { float x, y; // x si y sunt de tip float scanf("%f,%f",&x,&y);/*Se introduc de la tastatura doua numere reale separate prin virgula*/ printf ("x = %f, y = %f \n ",x,y); swap(&x,&y); /*Se apeleaza functia swap() avand ca argumente adresele lui x si y */ printf("x = %f, y = %f \n ",x,y); }

Prin &x i &y, programul transfer adresele lui x i y func iei swap() i nu valorile lui x i y.

162

Un apel combinat, valoare-referin este prezentat n exemplul urmtor:


# include <stdio.h> void f(); void main (void) { int x = 1, y = 1; printf("x = %d, y = %d \n", x, y); f(x,&y);} void f(int val, int *ref) { val++; (*ref)++; printf("x = %d, y = %d \n",val,*ref); }

8.6. Apelul func iilor avnd ca argumente tablouri


Cnd se apeleaz o func ie avnd ca argument un tablou, acesteia i se va transmite un pointer la primul element al tabloului. Reamintim c n C numele unui tablou fr nici un indice este un pointer la primul element al tabloului. Deci, un argument de tipul T[ ] (vector de tipul T) va fi convertit la T * (pointer de tipul T). Rezult c vectorii, ca i tablourile multidimensionale, nu pot fi transmise prin valoare. Aceasta nseamn c declararea parametrului formal trebuie s fie compatibil tipului pointer. Exist trei moduri de a declara un parametru care va primi un pointer la un tablou (vector). a) Parametrul formal poate fi declarat ca un tablou, astfel:
# include <stdio.h> display(); // Prototipul functiei display() void main(void) { int v[10], i; for (i = 0; i < 10; ++i) v[i] = i; display(v);} display(num) // Se defineste functia display() int num[10]; { int i; for (i = 0; i < 10; i++) printf ("%d", num[i]);

Chiar dac acest program declar parametrul num ca pe un vector de 10 ntregi, compilatorul C va converti automat pe num la un pointer la ntreg, deoarece parametrul nu poate primi ntregul tablou (vector). b) O a doua cale de a declara un parametru vector (tablou), const n a specifica parametrul ca pe un vector fr dimensiune:
display(int num[]) { int i; for(i = 0; i < 10; i++) printf ("%d",num[i]); }

163

Aceasta func ie declar pe num ca fiind un vector de ntregi cu dimensiune necunoscut. Deoarece limbajul C nu verific dimensiunea vectorilor, dimensiunea actual a vectorului este irelevant ca parametru al func iei. i de aceasta dat, num va fi convertit la un pointer la ntreg. c) Ultima metod prin care se poate declara un parametru tablou este ca pointer, astfel:
display(int *num) { int i; for (i = 0; i < 10; i++) printf ("%d", num[i]); }

Limbajul C permite acest tip de declara ie deoarece putem indexa orice pointer utiliznd []. Toate cele trei metode de declarare a unui tablou ca parametru produc acelai rezultat: un pointer. Cu toate acestea, un element al unui tablou folosit ca argument al unei func ii va fi tratat ca orice alt variabil. Astfel, programul de mai sus poate fi rescris sub forma:
# include <stdio.h> void main (void) { int v[10], i; for (i = 0; i < 10; i++) v[i] = i; for (i = 0; i < 10; i++) display (v[i]); } display(int num) { printf ("%d" , num); }

De data aceasta, parametrul din display() este de tip int, deoarece programul utilizeaz numai valoarea elementului tabloului. Exemplu: Vom prezenta un program pentru afiarea tuturor numerelor prime cuprinse ntre dou limite ntregi. Programul principal apeleaz dou func ii: nr_prim() returneaz 1 dac argumentul su ntreg este prim i 0 dac nu este prim; numerele prime sunt grupate ntr-un vector, care se afieaz ulterior cu func ia display().
# include <stdio.h> int nr_prim(); // Se declara prototipul void display(); void main (void) { int a,b,i,j,v[80]; printf("Introduceti limitele: "); scanf("%d %d", &a, &b); j = 0; for (i=a; i<=b; ++i) if (nr_prim(i)) {v[j]=i; ++j;} display(v,j);} int nr_prim(int i) { int j; // Decide daca i este prim

164

for (j=2; j<=i/2; j++) if (i%j==0) return 0; return 1; } void display(int *p, int j) /* Tipareste un vector de intregi */ { int i; for (i=0; i<j; ++i) printf("%d ", p[i]); }

Din cele de mai sus, trebuie re inut c atunci cnd un tablou se utilizeaz ca argument al unei func ii, calculatorul transmite func iei adresa de nceput a tabloului. Acest lucru constituie o excep ie a limbajului C n conven ia de transmitere a parametrilor prin valoare. Astfel, codul func iei poate ac iona asupra con inutului tabloului i l poate altera. Exemplu: Programul urmtor va modifica con inutul vectorului sir din func ia main() dup apelul func iei afis_litmari().
# include <stdio.h> # include <ctype.h> afis_litmari(); void main (void) { char sir[80]; gets(sir); afis_litmari(sir); printf("\n%s\n",sir);} // Se defineste functia afis_litmari() afis_litmari(char *s) { register int t; for (t = 0; s[t]; ++t) { // Se modifica continutul sirului sir s[t] = toupper(s[t]); printf("%c",s[t]);}}

Rezultatul rulrii programului va fi:


abcdefghijklmnoprstuvxyzw ABCDEFGHIJKLMNOPRSTUVXYZW ABCDEFGHIJKLMNOPRSTUVXYZW

Exemplu: Dac nu dorim s se ntmple acest lucru, programul de mai sus se poate rescrie sub forma:
# include <stdio.h> # include <ctype.h> afis_litmari(); void main (void) { char sir[80]; gets(sir); afis_litmari(sir); printf("\n%s\n",sir);} afis_litmari(char *s)

165

/* Se defineste functia afis_litmari() */ { register int t; for (t = 0; s[t]; ++t) printf("%c",toupper(s[t])); } //Nu se modifica continutul sirului sir

Rezultatul rulrii va fi de aceast dat:


abcbdefghijklmnoprstuvxyzw ABCBDEFGHIJKLMNOPRSTUVXYZW abcbdefghijklmnoprstuvxyzw

n aceasta variant con inutul tabloului ramne nemodificat, deoarece programul nu-i schimb valoarea. Un exemplu clasic de transmitere a tablourilor ntr-o func ie l constituie func ia gets() din biblioteca C standard. Prezentm o variant simplificat a acestei func ii numit xgets().
xgets(s) char *s; { char ch; int t; for (t = 0; t < 80; ++t) { ch = getchar(); switch (ch) { case '\n' : s[t] = '\0'; /* terminare sir return; case '\b': if (t > 0) t--; break; default: s[t] = ch; } } s[80] ='\0'; }

*/

Func ia xgets() trebuie apelat avnd ca argument un tablou de caractere, care, prin defini ie, este un pointer la caracter. Numrul caracterelor introduse de la tastatur, prin func ia for este de 80. Dac se introduc mai mult de 80 de caractere, func ia se ncheie cu return. Dac se introduce un spa iu, contorul t este redus cu 1. Cnd se apas CR, xgets() introduce terminatorul de ir.

8.7. Argumentele argc i argv ale func iei main()


Singurele argumente pe care le poate avea func ia main() sunt argv i argc. Parametrul argc con ine numrul argumentelor din linia de comand i este un ntreg. ntotdeauna acesta va fi cel pu in 1, deoarece numele programului este codificat ca primul argument.

166

Parametrul argv este un pointer la un tablou de pointeri la caractere. Fiecare element din acest tablou indic spre un argument linie_comanda. Toate argumentele linie_comanda sunt iruri. Exemplu: Urmtorul program arat modul de utilizare al argumentelor linie_comanda i va afia Hello urmat de numele dumneavoastr, dac v introduce i numele, imediat dup numele programului:
# include <stdio.h> void main (argc, argv) // Numele programului int argc; char *argv[]; {if (argc != 2) { printf (" Ati uitat sa va introduceti numele \n"); return; } printf ("Hello %s !", argv[1]); }

Dac acest program se numete ARG_LC.C i numele dumneavoastr este DAN, atunci, pentru a executa programul, n linia de comand, ve i tipri ARG_LC DAN. Ieirea programului va fi Hello DAN !. Argumentele linie_comanda trebuie separate prin spa iu sau TAB i nu prin virgul, sau;. Parametrul argv[] se declar, de obicei, sub forma char *argv[]; i reprezint un tablou de lungime nedeterminat, mai precis reprezint un tablou de pointeri. Accesul la elementele lui argv[] se realizeaz prin indexarea acestuia, astfel: argv[0] va indica spre primul ir, care este ntotdeauna numele programului; argv[1] va indica spre primul argument etc. Evita i folosirea sa fr paranteze, adic char
*argv.

Urmtorul program numit "nrinvers" numr invers de la o valoare specificat prin linia de comand i transmite un beep cnd ajunge la zero. Precizm c programul convertete primul argument, care con ine numrul la un ntreg folosind func ia standard atoi(). Dac irul "display" apare ca al doilea argument_comanda, programul va afia, de asemenea, numrul introdus pe ecran.
# include <stdio.h> # include <string.h> # include <stdlib.h> void main(int argc, char *argv[]) /* nrinvers */ { int disp, count; if (argc < 2) { printf ("Trebuie introdusa lungimea numarului in linia de comanda\n");

167

return; } if (argc==3 && !strcmp(argv[2], "display")) disp = 1; else disp = 0; for (count = atoi(argv[1]); count; --count) if (disp) printf("%d ",count); printf("%c",7); /* Se emite un beep */

Observa ie: Dac n linia de comand nu se specific nici un argument, programul va afia un mesaj de eroare.

8.8. Func ii care returneaz valori nentregi


Dac nu se declar explicit tipul func iei, compilatorul C o va declara implicit de tip int. Pentru ca func ia s ntoarc un tip diferit de int trebuie, pe de o parte, s se precizeze un specificator de tip al func iei i apoi s se identifice tipul func iei naintea apelului acesteia. O func ie C poate returna orice tip de dat din C. Declararea tipului este similar celei de la declararea tipului variabilei: specificatorul de tip ce precede func ia indic tipul datei ntoarse de func ie. Pentru a nu se genera incertitudini datorate dimensiunii de reprezentare, nainte de utilizarea unei func ii ce ntoarce tipuri nentregi, tipul acestei func ii trebuie fcut cunoscut programului. Acest lucru este necesar deoarece compilatorul nu cunoate tipul datei ntoarse de func ie i acesta va genera un cod greit pentru apelul func iei. Pentru a preveni aceast greeal, la nceputul programului se plaseaz o form special de declara ie care s precizeze compilatorului ce tip de valoare va returna acea func ie. Aceast declara ie se numete prototipul func iei. Exemplu:
# include <stdio.h> float sum();//Prototipul functiei (fara parametri) void main(void) { float first = 123.23, second = 99.09; printf("%f\n", sum(first, second)); } float sum(float a, float b) // Definitie sum() //Se returneaz o valoare de tip float { return a+b; }

Instructiunea de declarare a tipului func iei are forma general:


specificator_de_tip nume_func ie();

Chiar dac func ia are argumente, n declara ia de tip acestea nu se precizeaz (cu excep ia compilatoarelor mai vechi de 1989, care nu sunt adaptate la cerin ele ANSI-C).

168

Dac o func ie ce a fost declarat int ntoarce un caracter, calculatorul convertete valoarea caracter ntr-un ntreg. Deoarece conversiile caracter --> ntreg-caracter sunt fr probleme, o func ie ce ntoarce un caracter poate fi definit ca o func ie care ntoarce un ntreg.

8.9. Returnarea pointerilor


Dei func iile care ntorc pointeri se manipuleaz n acelai mod ca i celelalte tipuri de func ii, trebuie discutate cteva concepte importante. Pointerii la variabile nu sunt nici ntregi, nici ntregi fr semn. Pointerii sunt adrese de memorie a anumitor tipuri de date: int, char, float, double, struct etc. Motivul acestei distinc ii este legat de faptul c atunci cnd se prelucreaz un pointer aritmetic, aceast prelucrare este dependent de tipul datei indirectate: de exemplu, dac este increment un pointer la int, noua valoare (a adresei) va fi cu 4 mai mare fa de valoarea anterioar. n general, cnd un pointer este incrementat sau decrementat, acesta va indica ctre elementul urmtor, respectiv anterior, din tabloul pe care l indirecteaz. De exemplu, dac func ia int f() returneaz un ntreg, atunci func ia int *f(), returneaz un pointer la o dat de tip int. Deoarece fiecare tip de date poate avea lungimi diferite, compilatorul trebuie "s tie" ce tip de dat este indirectat de pointer, pentru a-l face s indice corect spre urmtorul element. Exemplu: Programul urmtor con ine o func ie care ntoarce un pointer ntr-un ir n locul n care calculatorul gsete o coinciden de caractere.
char *match (char c, char *s) {int count; count = 0; while (c!=s[count] && s[count] != '\0') return (&s[count]); }

count ++;

Func ia match() va ncerca s ntoarc un pointer la locul (elementul) din ir unde calculatorul gsete prima coinciden cu caracterul c. Dac nu se gseste nici o coinciden , func ia va ntoarce un pointer la terminatorul de ir (NULL). Un scurt program ce ar utiliza func ia match() este urmtorul :
# include <stdio.h>

169

# include <conio.h> char *match(); // Prototipul functiei void main (void) { char s[80], *p, ch; gets (s); /* Se introduce un sir */ ch = getche(); /* Se introduce un caracter */ p = match (ch, s); /* Apelul functiei */ /* p preia valoarea functiei match() */ if (p) { printf("\n Adresa caracterului ce coincide cu cel dat este: %p", p); printf("\n Subsirul de la adresa caracterului ce coincide cu cel dat este:\n %s\n",p);} else printf("Nu exista nici o coincidenta"); }

Acest program citete mai nti un ir i apoi un caracter. n cazul n care caracterul este n ir, atunci se tipreste irul din punctul unde se afl caracterul, altfel se tiprete "Nu exist nici o coinciden ". Un caz interesant este oferit de func iile care returneaz pointeri ctre iruri de caractere. O astfel de func ie se declar sub forma:
char *f()

Exemplu: Programul urmtor arat modul n care se definete, se declar i se apeleaz o astfel de func ie.
# include <stdio.h> void main(void) { int i; char *NumeLuna(); scanf("%d", &i); printf("%s \n ", NumeLuna(i)); } char *NumeLuna(nr) int nr; { char *luna[]= {"Eroare", "Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie","Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"}; return ((nr>=1) && (nr <= 12)?luna[nr]:luna[0]);

Un alt exemplu va fi reprezentat de o variant a func iei strcpy() din string.h , deci o func ie care copiaz caracterele din irul s2 n irul s1. Rezultatul se gsete n s1.
/* Vom incepe cu definirea functiei strcpy2() si apoi vom declara programul principal main(). In acest fel nu mai este necesara declararea prototipului functiei strcpy2() */

170

# include <stdio.h> char *strcpy2(register char s1[],register char s2[]) { char *s0 = s1; // Echivalent: char *s0;s0 = s1; while ((*s1++ = *s2++) != '\0'); return s0; } void main() { char *sir1,*sir2; puts(Introduceti un sir de la tatstatura \n); gets(sir2); puts(strcpy2(sir1,sir2));}

Se observ cum se ini ializeaz s0 cu s1. Bucla while atribuie valorile (caracterele) (*s2) n loca iile indicate de pointerul s1, incrementnd ambii pointeri simultan. Bucla se termin la ntlnirea caracterului null, care se copiaz i el. Valoarea returnat, s0, re ine adresa de nceput a irului s1. Un ultim exemplu l constituie un program de manipulare a unor matrici. Acest program realizeaz citirea unei matrici, transpunerea sa i respectiv afiarea rezultatului apelnd la func iile cit_mat(), trans_mat() i tip_mat().
# include <stdio.h> # define DIM_MAX 10 void cit_mat(); void tip_mat(); int *trans_mat(); void main() { int a[DIM_MAX][DIM_MAX], dim_lin, dim_col, *p; printf("Introduceti dimensiunea matricei dim_col]: "); scanf("%d %d", &dim_lin, &dim_col); cit_mat(a, dim_lin, dim_col); tip_mat(a, dim_lin, dim_col); p = trans_mat(a, dim_lin, dim_col); tip_mat(a, dim_col, dim_lin); } void cit_mat(int p[][DIM_MAX], int lin, int col) { int i, j; for (i=0; i<lin; i++) for (j=0; j<col; j++) { printf("x[%d][%d] = ", i, j); scanf("%d", &p[i][j]); } } void tip_mat(int p[][DIM_MAX], int lin, int col) { int i, j; for (i=0; i<lin; i++) { for (j=0; j<col; j++) printf("%d ",p[i][j]);

[dim_lin

171

printf("\n"); } printf("\n"); } int *trans_mat(int p[][DIM_MAX], int lin, int col) { int t, i, j; for (i=0; i<lin; i++) for (j=i; j<col; j++) {t = p[i][j], p[i][j] = p[j][i], p[j][i] = t;} return p; }

8.10. Func ii de tip void


Din punct de vedere sintactic, tipul void se comport ca un tip fundamental (de baz). Nu exist obiecte de tip void. Tipul void este utilizat pentru declararea implicit a acelor func ii care nu ntorc o valoare. void se utilizeaz i ca tip de baz pentru pointeri la un obiect de tip necunoscut. Exemplu:
void f(void) void *pv /* functia f nu intoarce o valoare */ /* pointer la un obiect necunoscut */

Utiliznd void se impiedic folosirea func iilor ce nu ntorc o valoare n orice expresie, prevenind astfel o ntrebuin are greit a acestora. De exemplu, func ia afis_vertical() afieaz pe ecran argumentul su ir, vertical, i ntruct nu ntoarce nici o valoare, este declarat de tip void.
void afis_vertical (sir) char *sir; { while (*sir) printf ("%c \n", *sir ++);

naintea utilizrii acestei func ii sau oricrei alte func ii de tip void, aceasta trebuie declarat. Dac nu se declar, compilatorul C consider c aceasta ntoarce o valoare ntreag. Astfel, modul de utilizare al func iei afis_vertical() este urmtorul:
# include <stdio.h> void afis_vertical(); // Se declara prototipul void main (void) { afis_vertical ("Hello "); } void afis_vertical (sir) char *sir; { while (*sir) printf ("%c \n", *sir ++); }

172

8.11. Func ii prototip


Dup cum se tie, naintea folosirii unei func ii care ntoarce o alt valoare dect int, aceasta trebuie definit. Func iile prototip au fost adugate de comitetul ANSI-C standard. Declararea unei func ii prototip se face conform urmtorului format:
tip nume_func ie (tip_arg1, tip_arg2,...)

unde:

tip = tipul valorii ntoarse de func ie; tip_arg1, tip_arg2,... = tipurile argumentelor func iei. Exemplu: Programul urmtor va determina compilatorul s emit un mesaj de eroare sau de avertisment deoarece acesta ncearc s apeleze func ia func() avnd al doilea argument de tip int, n loc de float, cum a fost declarat n func ia func():
#include <stdio.h> void func(int, float);//Prototipul functiei func() void main (void) { int x, y; x = 10; y = 10; func (x, y); } /* Se afiseaza o nepotrivire */ void func (x, y) /* Parametrii functiei sunt: */ int x; /* x - intreg */ float y; /* y - real */ { printf ("%f", y/(float) x); }

Func iile prototip se folosesc pentru a ajuta compilatorul n prima faz n care func iile utilizate sunt definite dup programul principal. Acesta trebuie ntiin at asupra tipul datei returnat de o func ie pentru a aloca corect memoria. Dac func iile sunt declarate naintea liniei de program main(), func iile prototip nu mai sunt necesare, deoarece compilatorul extrage informa ia despre func ii n momentul n care parcurge corpul defini iei lor. Spre exemplu, programul de mai sus se poate scrie i sub forma urmtoare, n care nu vom mai avea o declara ie de func ie prototip:
#include <stdio.h> void func (x, y) /* Parametrii functiei sunt: */ int x; /* x - intreg */ float y; /* y - real */ { printf ("%f", y/(float) x); } void main (void) { int x, y; x = 10; y = 10; func (x, y); } /* Nu se afiseaza nepotrivire */

173

Utiliznd recomandrile ANSI-C din 1989, programul de mai sus se poate scrie mai compact:
#include <stdio.h> void func (int x, float y) /* Parametrii formali includ tipul */ { printf ("%f", y/(float) x); } void main (void) { int x, y; x = 10; y = 10; func (x, y); }//afisare avertisment de conversie

sau, folosind func ia prototip:


#include <stdio.h> void func(); /* Declarare prototip fara parametri formali ! */ void main (void) { int x, y; x = 10; y = 10; func (x, y); } void func (int x, float y) /* Parametrii formali includ tipul */ { printf ("%f", y/(float) x); }

n ultimul program am eviden iat o recomandare care simplific efortul de programare n sensul c n linia de declarare a prototipurilor func iilor folosite este necesar s definim tipul func iei nu i tipul parametrilor formali. Compilatorul se informeaz despre tipul parametrilor formali la parcurgerea corpului defini iei func iei. Din cele de mai sus se observ ca folosirea func iilor prototip ne ajut la verificarea corectitudinii programelor, deoarece nu este permis apelarea unei func ii cu alte tipuri de argumente, dect tipul celor declarate.

8.12. Func ii recursive


Func iile C pot fi recursive, adic se pot autoapela direct sau indirect. O func ie este recursiv dac o instruc iune din corpul func iei este o instruc iune de apel al aceleiai func ii. Uneori o func ie recursiv se numete i func ie circular. Un exemplu de o astfel de func ie este func ia factorial() care determin factorialul unui numr. Aceast func ie se poate organiza recursiv, tiind c: n! = n(n-1)!. Avnd n vedere 0!=1, aceast func ie se poate organiza astfel:
long factorial (int n) { if (n == 0) return (1);

174

else return (n * factorial(n-1)); }

Programul de apel al acestei func ii se scrie sub forma:


# include <stdio.h> void main (void) { int n; printf("Introduceti un numar intreg : \n"); scanf ("%d, &n); printf ("(%d) ! = %ld",n,factorial(n)); } long factorial (int n) { if (n == 0) return (1); else return (n * factorial(n-1)); }

Observa ie: Atunci cnd o func ie se autoapeleaz recursiv, la fiecare apel al func iei se memoreaz pe stiv att valorile parametrilor actuali, ct i ntregul set de variabile dinamice definite n cadrul func iei. Din aceasta cauz stiva trebuie dimensionat corespunztor.
O variant echivalent a func iei factorial() definit mai sus ar fi urmtoarea:
long factorial(int n) { if (!n) return (1); else return (n * factorial (n-1)); }

Un alt exemplu interesant este dat de irul lui Fibonacci, n care termenul general an este dat de rela ia de recuren : an = an-1+ an-2 , unde a0 = 0 i a1=1. Codul func iei poate fi scris sub forma:
long fib(int n) { if (n == 0) return (0); else if (n == 1) return (1); else return (fib(n-1)+fib(n-2)); }

Utilizarea recursivit ii poate s nu conduc la o reducere a memoriei necesare, att timp ct stiva este folosit intens pentru fiecare apel recursiv. De asemenea i execu ia programului poate s nu fie mai rapid. Dar codul recursiv este mai compact i de multe ori mai uor de scris i n eles dect echivalentul su recursiv. Recursivitatea este convenabil n mod deosebit pentru opera ii pe structuri de date definite recursiv, cum sunt listele, arborii etc.

175

8.13. Clase de memorare (specificatori sau

atribute)
Din punct de vedere al execu iei programelor C, memoria computerului este organizat n trei zone, cunoscute n mod tradi ional ca segment de memorie text, segment de memorie static (sau de date) i segment de memorie dinamic (sau stiv).
Segment de memorie text Con ine instruc iunile programului, deci (memorie program) programul executabil Segment de memorie Con ine variabilele a caror loca ie rmne fix static Segment de memorie Con ine variabilele de tip automatic, parametrii func iilor i apelurile i retururile dinamic (de tip stiv) de/din func ii

n tabelul urmtor se prezint caracteristicile claselor de memorie.


Specificator de memorie Domeniul de vizibilitate al variabilei Durata de via a variabilei Plasament
n memoria dinamic (de tip stiv) n regitrii microproceso rului n memoria static In memoria static

Auto Local fiecrei func ii Temporar, numai (automatic) sau fiecrui bloc n care cnd se execut a fost declarat func ia n care este declarat Register Local fiecrei func ii Temporar, numai (registru) cnd se execut func ia n care este declarat Extern Global, de ctre toate Permanent, pe func iile dintr-un fiier parcursul rulrii surs sau din mai multe programului fiiere surs executabil Static Local sau global Permanent, ct timp este in memorie programul executabil

Vizibilitatea precizeaz domeniul sau locul n care o variabil este vizibil. Domeniul de vizibilitate este n general determinant i n stabilirea duratei de via a variabilei. Din punctul de vedere al duratei de via a variabilei, aceasta poate fi temporar (exist numai pe perioada n care func ia care o

176

declar este activat) sau permanent (exist pe toat durata de execu ie a programului). Dac tipul se declar explicit n declaratorul variabilei, clasa de memorie se determin prin specificatorul de clas de memorie i prin locul unde se face declara ia (n interiorul unei func ii sau naintea oricrei func ii). Variabilele cele mai folosite sunt cele care sunt declarate n blocurile apar innd unei func ii. Aceste variabile sunt de dou feluri: - auto, aa cum sunt marea majoritate a variabilelor declarate numai prin tip. Acesta este un specificator implicit, deci nu este nevoie s l invocm la declararea variabilelor. Variabilele auto sunt plasate n memoria stiv, iar domeniul de vizibilitate este local, numai pentru func ia n care variabila a fost declarat, iar din punctul de vedere al duratei de via sunt volatile, adic dispar din memoria stiv dup rentoarcerea din func ie. - static, declarate explicit. Variabilele static sunt plasate n memoria static, iar domeniul de vizibilitate este local, numai pentru func ia n care variabila a fost declarat, iar din punctul de vedere al duratei de via sunt permanente, adic nu dispar din memoria static dup rentoarcerea din func ie. - register, declarate explicit. Variabilele register sunt identice cu cele auto cu excep ia faptului c stocarea nu are loc n memoria stiv ci n regitrii interni ai microprocesorului n scopul sporirii vitezei de execu ie a programelor. - extern, declarate explicit. Din punct de vedere al modulrii unor programe, este preferabil s divizm un program complex n mai multe module program care se leag n faza de link-editare. O variabil declarat extern ntr-un modul program semnaleaz compilatorului faptul c aceast variabil a fost declarat ntr-un alt modul. Aceste variabile sunt globale, adic sunt vzute de orice modul de program i de orice func ie component a unui modul program. Stocarea are loc n memoria static iar durata de via este permanent, pe toat perioada execu iei programului. Ini ializarea unei variabile static difer de cea a unei variabile auto prin aceea c ini ializarea este fcut o singur dat, la ncrcarea programului n memorie i lansarea sa n execu ie. Dup prima ini ializare, o variabil static nu mai poate fi reini ializat (de exemplu, la un nou apel al func iei n care este ini ializat).

177

Iat ilustrat acest lucru prin dou exemple simple. Se tiprete, cu ajutorul func iei receip(), un numr care este mai nti ini ializat cu valoarea 1 i returnat incrementat cu o unitate. n cazul folosirii variabilelor implicite locale auto se ruleaz programul:
# include <stdio.h> short receip(); void main(){ printf("First = %d\n",receip()); printf("Second = %d\n",receip());} short receip() { short number = 1; return number++;}

i se ob ine rezultatul:
First = 1 Second = 1

Dac se modific n func ia receip() variabila number din auto n static, vom avea
# include <stdio.h> short receip(); void main(){ printf("First = %d\n",receip()); printf("Second = %d\n",receip());} short receip() { static short number = 1; return number++;}

i ob inem rezultatul
First = 1 Second = 2

Limbajul C suport patru specificatori ai claselor de memorare: auto, extern, static, register. Acetia precizeaz modul de memorare al variabilelor care i urmeaz. Specificatorii de memorare preced restul declara iei unei variabile care capt forma general: specificator_de_memorare specificator_de_tip lista_de_variabile; Specificatorul auto Se folosete pentru a declar varibilele locale (obiectele dintr-un bloc). Totui, utilizarea acestuia este foarte rar, deoarece, implicit, variabilele locale au clasa de memorare automat (auto). Specificatorul extern Se utilizeaz pentru a face cunoscute anumite variabile globale declarare ntr-un modul de program (fiier) altor module de programe (fiiere) cu care se va lega primul pentru a alctui programul complet. Exemplu:

178

Modulul 1 int x, y; char ch; main() { . . . . . . . . . . . . } func1() { x = 123;

Modulul 2 extern int x, y; extern char ch; func22() { x = y / 10; } func23() { y = 10; }

Dac o variabil global este utilizat ntr-una sau mai multe func ii din modulul n care acestea au fost declarate nu este necesar utilizarea op iunii extern. Dac compilatorul gsete o variabil ce n-a fost declarat, atunci acesta o va cuta automat printre variabilele globale. Exemplu:
int first, last; /* variabile globale */ main( ) { extern int first;}//folosire optionala declaratie extern

Variabile statice Obiectele statice pot fi locale unui bloc sau externe tuturor blocurilor, dar n ambele situa ii ele i pstreaz valoarea la ieirea i intrarea, din sau n func ii. Variabile locale statice Cnd cuvntul cheie static se aplic unei variabile locale, compilatorul C creaz pentru aceasta o memorie permanent n acelai mod ca i pentru o variabil global. Diferen a dintre o variabil local static i o variabil global este c variabila local static este cunoscut numai n interiorul blocului n care a fost declarat. Un exemplu de func ie care necesit o astfel de variabil este un generator de numere care produce un nou numr pe baza celui anterior.
serie() {static int numar_serie; numar_serie = numar_serie + 23; return (numar_serie); }

Se observ c variabila numar_serie continu s existe ntre dou apeluri ale func iei serie() fr ca aceasta s fi fost declarat ca variabil global. Se observ de asemenea c func ia nu atribuie nici o valoare ini ial variabilei numar_serie, ceea ce nseamn c valoarea in ial a acesteia este 0.

179

Variabile globale statice O variabil global cu atributul static este o variabil global cunoscut numai n modulul n care a fost declarat. Deci o variabil global static nu poate fi cunoscut i nici modificat din alte module de program (alte fiiere). Exemplu:
static int numar_serie; //var. globala este cunoscuta numai in acest fisier serie() { numar_serie = numar_serie + 23; return (numar_serie); } /* initializarea variabilei numar_serie */ serie_start(val_init) int val_init;{ numar_serie = val_init; }

Apelul func iei serie_start() cu o valoare intreag ini ializeaz seria generatoare de numere, dup care apelul func iei serie() va genera urmtorul numr din serie. Specificatorul register Acest modificator se aplic numai variabilei de tip int i char. Acest specificator precizeaz faptul ca variabilele declarate cu acest modificator sunt des utilizate i se pastreaz de obicei n registrele CPU. Specificatorul register nu se aplica variabilelor globale. Exemplu: Aceasta func ie calculeaza me pentru ntregi :
int_putere (m, e) int m; register int e; { register int temp; temp = 1; for (; e; e--) temp * = m; return temp; }

n acest exemplu au fost declarate ca variabile registru att e ct i temp. De obicei utilizarea unei variabile registru conduce la micorarea timpului de execu ie al unui program. Exemplu :
unsigned int i; unsigned int delay; main() { register unsigned int j; long t; t = time ('\0'); for (delay = 0; delay < 10; delay++) for (i = 0; i < 64000; i++);

180

printf("Timpul pentru bucla non-registru: %ld\n" ,time('\0')-t); t = time ('\0'); for (delay = 0; delay < 10; delay++) for (j = 0; j < 64000; j++); printf ("Timpul bucla registru: %ld",time ('\0')-t);}

Dac se execut acest program se va gsi c timpul de execu ie al buclei registru este aproximativ jumtate din timpul de execu ie al variabilei non-registru.

8.14. Pointeri la func ii


ntr-un fel, un pointer func ie este un nou tip de dat. Chiar dac o func ie nu este o variabil, aceasta are o loca ie fizic n memorie care poate fi atribuit unui pointer. Adresa atribuit pointerului este punctul de intrare al func iei. Acest pointer poate fi utilizat n locul numelui func iei. Pointerul permite de asemenea func iilor s fie pasate (trecute) ca argumente n alte func ii. Adresa unei func ii se ob ine utiliznd numele func iei fr nici o parantez sau argumente (ca n cazul tablourilor). Exemplu:
# include <stdio.h> # include <ctype.h> void check(); int strcmp(); /* prototip functie */ void main() { char s1[80], s2[80]; void *p; /* p preia adresa de intrare a functiei */ p = strcmp; gets(s1); gets(s2); check(s1,s2,p); } void check (char *a, char *b, int (*cmp) ()) /* cu int (*cmp) () se declara un pointer functie */ { printf (" Test de egalitate \n "); if (!(*cmp) (a,b)) printf ("Egal\n"); else printf ("Neegal\n"); }

Declararea lui strcmp() n main() s-a facut din dou motive: 1) programul trebuie s tie ce tip de valoare returneaz strcmp(); 2) numele trebuie cunoscut de compilator ca i func ie. Deoarece n C nu exist o modalitate de a declara direct un pointer func ie, acesta se declar indirect folosind un pointer void care poate primi orice fel de pointer.

181

Apelul func iei check() se face avnd ca parametri doi pointeri la caracter i un pointer func ie. Instruc iunea : (*cmp)(a, b) realizeaz apelul func iei, n acest caz strcmp() iar a i b sunt argumentele acestuia. Exemplu:
# include <stdio.h> # include <ctype.h> int strcmp(); /* prototip functie */ void main() { char s1[80], s2[80]; int (*p)(); /* p este pointer la functie */ p = strcmp; gets (s1); gets (s2); printf (" Test de egalitate \n "); if (!(*p) (s1,s2)) printf ("Egal\n"); else printf("Neegal\n"); }

Observa ie: Func ia check() poate utiliza direct func ia strcmp() sub forma: check (s1, s2, strcmp); Exemplu:
# include <stdio.h> # include <ctype.h> void check (); int strcmp(); /* prototip functie */ void main() { char s1[80], s2[80]; gets (s1); gets (s2); check (s1, s2, strcmp); } void check (char *a, char *b, int (*cmp) ()) // se defineste functia check() /* cu int (*cmp) () se declara un pointer functie */ { printf (" Test de egalitate \n "); if (!(*cmp) (a,b)) printf ("Egal\n"); else printf ("Neegal\n"); }

182

Capitolul IX PREPROCESAREA
Un preprocesor C realizeaz substituirea macrodefini iilor, o serie de calcule adi ionale i incluziunea fiierelor. Liniile programului surs care ncep cu "#", precedat eventual de spa iu comunic cu preprocesorul. Sintaxa acestor linii este independent de restul limbajului; pot apare oriunde n program i pot avea efect care se men ine (indiferent de domeniul n care apare) pn la sfritul unitatii de translatare. Preprocesorul C con ine urmtoarele directive: #if #include #ifdef #define #ifndef #undef #else #line #elif #error #pragma

9.1. Directive uzuale


Directiva #define se utilizeaz pentru a defini un identificator i un ir (o secven ) pe care compilatorul l va atribui identificatorului de fiecare dat cnd l ntlnete n textul surs. Forma general a directivei #define este : #define identificator ir Se observ c directiva #define nu con ine "; ". n secven a de atomi lexicali "ir" nu trebuie s apar spa iu. Linia se termina cu CR. Exemplu: # define TRUE 1 # define FALSE 0 Cnd n program se ntlnesc numele TRUE i FALSE, acestea se vor nlocui cu 1, respectiv 0. Instruc iunea:
printf ("%d %d %d", FALSE, TRUE, TRUE + 5);

va afia pe ecran 0 1 6.

183

Dup definirea unui macro_name, acesta poate fi folosit pentru definirea altui macro_name. Exemplu:
# define ONE 1 /* Se definete macro_name ONE */ # define TWO ONE + ONE /* Se utilizeaz macro_name ONE */ # define THREE ONE + TWO

Deci aceast macrodefini ie realizeaz simpla nlocuire a unui identificator cu irul asociat. Dac, de exemplu, se dorete definirea unui mesaj standard de eroare, se poate scrie:
# define E_MS "standard error on input \n" . . . . . . . . . . printf (E_MS);

Ultima linie este echivalent cu :


printf ("standard error on input\n");

atunci cnd n program se ntlnete identificatorul E_MS. Exemplu: Programul urmtor nu va afia "this is a test", deoarece argumentul lui printf() nu este nchis ntre ghilimele.
# define XYZ this is a test . . . . . . . . . . . . . . . . . printf ("XYZ");

Se va afia XYZ i nu "this is a test". Dac irul este prea lung i nu ncape pe o linie, acesta se scrie sub forma:
# define LONG_STRING " this is a very long \ string that is used as an example "

Observa ie: De obicei macro_names sunt definite cu litere mari. Directiva #define poate fi folosit i pentru precizarea dimensiunii unui tablou, astfel:
# define MAX_SIZE 100 float balance [ MAX_SIZE ];

Macro_nameul dintr-o argumente. Exemplu :

directiva #define

poate avea i

# define MIN (a ,b) a < b ? a : b void main() { int x, y; x = 10; y = 20; printf("Numarul mai mic este: %d ", MIN (x,y)); }

Dup substituirea lui MIN(a, b) n care a = x i b = y, instruc iunea printf() va arata astfel :
printf("Numarul mai mic este: %d",(x<y)?x:y);

184

Directiva #error Directiva #error for eaz compilatorul s stopeze opera ia de compilare cnd aceast este intilnita n program. Este utilizata n primul rind pentru depanarea programelor. Forma general a directivei este: #error mesaj_de_eroare Aceasta linie determin procesorul s scrie mesajul de eroare i s termine compilarea. Directiva # include Directiva # include comand compilatorului s includ n fiierul ce con ine directiva #include un alt fiier surs al crui nume este specificat n directiv. Formele directivei sunt :
# include <nume_fisier> # include "nume_fisier"

Prima form se refer la fiiere header (cu extensia .h) care se gsesc n subdirectorul include din fiecare mediu de programare C, iar cea de-a doua la fiiere header create n directorul de lucru al utilizatorului (directorul curent). Directivele # include pot fi folosite i una n interiorul celeilalte.

9.2. Directive pentru compilare condi ionat


Limbajul C con ine cteva directive care ne permit s compilm selectiv anumite por iuni de program. Directivele #if, #else, #elif i #endif Forma general a lui #if este:
#if expresie_constanta secventa de instructiuni #endif

Dac expresie_constanta este adevrat, compilatorul va compila fragmentul de cod cuprins ntre #if i #endif, iar dac expresie_constanta este fals, compilatorul va sri peste acest bloc. Exemplu:
#define MAX 100 void main() { #if MAX > 99 printf("Se compileaza pentru tablouri > 99\n"); #endif }

Observa ie: Expresie_constanta se evalueaz n timpul compilrii. De aceea, aceasta trebuie s con in numai variabile constante definite anterior utilizrii lor. Expresie_constanta nu trebuie s con in operatorul sizeof.

185

Directiva #else lucreaz similar cu instruc iunea determinnd o alternativ de compilare. Exemplu :
# define MAX 10 void main() { #if MAX > 99 printf("Se compileaza pentru tablouri > 99\n"); #else printf("Se compileaza pentru tablouri < 99\n"); #endif }

else

Deoarece MAX = 10, compilatorul va compila numai codul cuprins ntre #else i #endif, deci va tipri mesajul : Se compileaz pentru tablouri < 99 Directiva #elif inlocuiete "else if" i este utilizat pentru realizarea op iunilor multiple de tip if / else / if utilizate la compilare. Forma general a directivelor #if , #elif, #endif este:
#if expresie Secventa_de_instructiuni #elif expresie_1 Secventa_de_instructiuni_1 #elif expresie_2 Secventa_de_instructiuni_2 . . . . . . . . . . . . . . #elif expresie_N Secventa_de_instructiuni_N #endif

Dac "expresie" este adevrat se compileaz "Secventa_de_instructiuni" i nu se mai tasteaz nici o alt expresie #elif. Dac "expresie" este fals, compilatorul verific urmtoarele expresii n serie, compilndu-se "Secventa_de_instructiuni_i", corespunzatoare primei "expresie_i" adevrat, i = 1, 2, . . . , N. Directivele #if i #elif se pot include unele pe altele. Exemplu:
MAX > 100 #if VERSIUNE_SERIALA int port = 198; #elif int port = 200; #endif #else char out_buffer[100]; #endif #if

Directivele #ifdef i #ifndef O alt metod de compilare condi ionat utilizeaz directivele #ifdef i #ifndef, care nseamn "if defined" i "if not defined".

186

Forma general a lui #ifdef este :


#ifdef macro_name Secventa_de_instructiuni #endif

Dac anterior apari iei secven ei de mai sus s-a definit un macro_name printr-o directiv #define, compilatorul va compila "Secventa_de_instructiuni" dintre #ifdef i #endif. Forma general a lui #ifndef este:
#ifndef macro_name Secventa_de_instructiuni #endif

Dac macro_name nu este definit prntr-o directiv #define, atunci se va compila blocul dintre #ifndef i #endif. Att #ifdef, ct i #ifndef pot utiliza un #else, dar nu #elif. Exemplu:
# define TOM 10 void main() { #ifdef TOM printf("Hello TOM !\n"); #else printf("Hello anyone !\n"); #endif #ifndef JERY printf ("Jery not defined \n"); #endif } Programul va afia: Hello TOM ! i JERY not defined.

Dac nu s-a definit TOM, atunci programul va afia : Hello anyone !.

Directiva #undef Se utilizeaz pentru a anula defini ia unui macro_name definit printr-o directiv #define. Exemplu:
#define LENGTH 100 #define WIDTH 100 char array[LENGTH][WIDTH]; #undef LENGTH #undef WIDTH

Acest program definete att LENGTH, ct i WIDTH pn se ntlnete directiva #undef. Principala utilizare a lui #undef este de a permite localizarea unui macro_name numai n anumite sec iuni ale programului. Directiva #line O linie cu una din formele:

187

#line numar "nume_fiaier" #line numar

determin compilatorul s considere, din motive de diagnosticare a erorilor, c numrul de linie al urmatoarei linii din programul surs este dat de "numr", iar numele fiierului n care se afl programul surs este dat de "nume_fiier". Dac lipseste "nume_fiier", programul surs se afl n fiierul curent. Exemplu: Urmtoarea secven face ca numrul de linie s nceap cu 100.
# line 100 void main() /* linia 100 */ { /* linia 101 */ printf ("%d\n" , __LINE__); /* linia 102 */ }

Instructiunea printf() va afia valoarea 102 deoarece aceast reprezint a treia linie n program, dup instruc iunea #line 100. Directiva #pragma O linie de control de forma:
#pragma nume

determin compilatorul s realizeze o ac iune care depinde de modul de implementare al directivei #pragma. "nume" este numele ac iunii #pragma dorite. Limbajul C definete dou instruc iuni #pragma: warn i inline. Directiva warn determin compilatorul s emit un mesaj de avertisment. Forma general a lui warn este :
#pragma warn mesaj

unde "mesaj" este unul din mesajele de avertisment definite n C. Forma general a directivei inline este :
#pragma inline

i avertizeaz compilatorul c programul surs con ine i cod n limbajul de asamblare. Directiva vid O linie de forma: # nu are nici un efect. Macro_names (macrosimboluri) predefinite Limbajul C con ine c iva identificatori predefini i, care la compilare se expandeaz pentru a produce informa ii speciale. Acetia sunt:

188

__LINE__ o constanta zecimal care con ine numele liniei surs curente. __FILE__ un ir care con ine numele fiierului care se compileaz. __DATA__ un ir care con ine data compilrii sub forma luna/zi/an. __TIME__ un ir care con ine ora compilrii sub form: hh:mm:ss __STDC__ constanta 1. Acest identificator este 1 numai n implementarile standard; dac constanta este orice alt numr, atunci implementarea este diferit de cea standard. Aceste macrosimboluri, mpreun cu simbolurile definite cu #define nu pot fi redefinite.

9.3. Modularizarea programelor


De obicei (vezi [Mocanu, 2001] programele C constau din fiiere surs unice, cu excep ia fiierelor header. Un singur fiier surs este n general suficient n cazul programelor mici. Modularizarea intern este un principiu de baz al programrii n C i const n utilizarea pe scar larg a func iilor definite de utilizator. Scrierea programului principal (main) se concentreaz mai ales pe apelul acestor func ii. n cazul n care corpul de defini ie al func iilor utilizator se afl dup corpul de defini ie main, este necesar ca s declarm prototipul func iilor utilizate de main() pentru a informa corect compilatorul despre tipul variabilelor returnate de func ii. O alt modalitate este aceea de a defini func iile utilizator naintea func iei principale main(), caz n care nu mai sunt necesare prototipurile. Programul este modularizat cu ajutorul func iilor prin divizarea sa n nuclee func ionale. Acestea pot fi comparate cu nite mici piese de lego cu ajutorul crora se pot construi ulterior structuri (programe) foarte complexe. Pe scurt, modularizarea intern const n descompunerea sarcinii globale a unui program n func ii de prelucrare distincte. O func ie de uz general este o func ie care poate fi folosit ntro varietate de situa ii i, probabil, de ctre mai mul i utilizatori. Este de preferat ca aceste func ii de uz general s nu primeasc informa ii prin intermediul unor variabile globale ci prin intermediul parametrilor. Sporete astfel foarte mult flexibilitatea n folosirea acestor func ii.

189

Modularizarea extern const n divizarea unui program foarte complex n mai multe subprograme. Astfel, un fiier surs mai mare se poate diviza n dou sau mai multe fiiere surs mai mici. Evident, aceste fiiere sunt strns legate ntre ele pentru a forma n final un tot unitar echivalent cu programul complex ini ial (dinainte de divizare).

n figura de mai sus se prezint un ecran al Microsoft Visual C++ din MSDN 6.0 No iunea cea mai cuprinztoare este aceea de Workspace (spa iu de lucru) care cuprinde n esen o colec ie de proiecte corelate i prelucrabile mpreun. Un workspace cuprinde unul sau mai multe Projects (proiecte) dintre care numai unul este principal i restul sunt subordonate (subprojects). Fiecare proiect este compus la rndul su din mai multe fiiere, de acelai tip sau de tipuri diferite. Prezentarea exhaustiv a organizrii acestui mediu de dezvoltare a aplica iilor C/C++ este un demers n afara prezentei lucrri. Ceea ce merit s subliniem este faptul c, n cadrul cel mai ntlnit, anume un workspace care include un singur project, acest proiect con ine mai ales fiiere surs i fiiere de tip header. Aceste fiiere se numesc module. Modulul principal este fiierul care con ine func ia principal main(). Celelalte fiiere surs, dac exist, se numesc module

190

secundare. De obicei, cu fiecare modul secundar se asociaz un fiier header propriu separat. Acest fiier header trebuie s con in toate directivele i declara iile de variabile necesare pentru o corect compilare separat a modulului cu care se asociaz. Pentru a exemplifica cele de mai sus, vom modulariza un exemplu anterior, anume al unei baze de date simple. Workspace-ul va con ine un singur project, care va con ine urmtoarele 4 fiiere: bd_main.c local.h
bd_bib.c local1.h bd_main.c (bd - baz de date) este modulul principal, cel care con ine func ia main(). El are asociat fiierul header local.h. n mod asemntor, local1.h este fiierul header asociat cu modulul secundar bd_bib.c (bib - bibliotec) care con ine toate defini iile

func iilor utilizator. Con inutul lor este prezentat n continuare. Modulul bd_main.c este:
# include "local.h" void main() { char choice; init_list(); for (; ;) { choice = menu(); switch (choice) { case 'e' : enter(); break; case 'd' : display(); break; case 's' : save(); break; case 'l' : load(); break; case 'q' : exit(); }}}

Fiierul local.h con ine:


# include <stdio.h> # include <ctype.h> # include <string.h> # define SIZE 100 struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; } addr_info[SIZE]; FILE *fp; extern void init_list(); extern char menu(); extern void enter(),save(),load(); extern void display(),exit();

191

Modulul bd_bib.c este:


# include "local1.h" /* Functia init_list() */ void init_list() { register int t; for (t = 0; t < SIZE; t++) *addr_info[t].name = '\0';

/* Functia menu() */ char menu() { char s[5],ch; do { printf ("(E)nter\n"); printf ("(D)isplay\n"); printf ("(L)oad\n"); printf ("(S)ave\n"); printf ("(Q)uit\n"); printf (" Alegeti optiunea: "); gets(s); ch=s[0]; } while (!strrchr("edlsq",ch)); return tolower(ch); } /* Functia enter() */ void enter() { register int i; for (i=0; i < SIZE; i++) if (!*addr_info[i].name) break; if (i == SIZE) { printf ("addr_info full \n"); /* Lista plina */ return;} printf ("Name: "); gets (addr_info[i].name); printf ("Street: "); gets (addr_info[i].street); printf ("City: "); gets (addr_info[i].city); printf ("State: "); gets (addr_info[i].state); printf ("Zip: "); scanf ("%d",&addr_info[i].zip);} /* Functia save() */ void save() { register int i; if ((fp = fopen("maillist", "wb")) == NULL) { printf (" Cannot open file\n "); return;}

192

for (i = 0; i <= SIZE; i++) if(*addr_info[i].name) if(fwrite(&addr_info[i],sizeof(struct addr),1,fp) !=1) printf (" File write error \n "); fclose (fp);} /* Functia load() */ void load() { register int i; if ((fp = fopen("maillist","rb")) == NULL) { printf("Cannot open file\n "); return;} for (i = 0; i < SIZE; i++) if(fread(&addr_info[i],sizeof(struct addr),1,fp)==1); else if (feof(fp)) { fclose (fp); return;} else printf ("File read error\n"); } /* Functia display() */ void display() { register int t; printf("\n%20s","Name"); printf("%30s","Street"); printf("%15s","City"); printf("%10s","State"); printf("%5s\n","Zip"); for (t=0;t<SIZE;t++) { if (*addr_info[t].name!='\0') { printf("%20s",addr_info[t].name); printf("%30s",addr_info[t].street); printf("%15s",addr_info[t].city); printf("%10s",addr_info[t].state); printf("%5d",addr_info[t].zip); getchar();}}}

Fiierul local1.h con ine:


# include <stdio.h> # include <ctype.h> # include <string.h> # define SIZE 100 extern struct addr { char name[20]; char street[30]; char city[15]; char state[10]; unsigned int zip; } addr_info[SIZE]; extern FILE *fp;

Se poate verifica cum fiecare modul n parte este compilabil fr erori, iar la link-editare nu se semnaleaz, de asemenea, erori.

193

Capitolul X INTRRI/IEIRI
10.1. Func ii de intrare i ieire - stdio.h
Limbajul C nu dispune de instruc iuni de intrare/ieire. Aceste opera ii se realizeaz prin intermediul unor func ii din biblioteca standard a limbajului C. Aceste func ii pot fi aplicate n mod eficient la o gam larg de aplica ii datorit multiplelor facilit i pe care le ofer. De asemenea, ele asigur o bun portabilitate a programelor, fiind implementate ntr-o form compatibil pe toate sistemele de operare. O alt caracteristic a limbajului C const n faptul c nu exist un sistem de gestionare a fiierelor care s permit organizri de date, aa cum n alte limbaje exist fiiere cu organizare relativ sau indexat. n limbajul C toate fiierele sunt tratate ca o niruire de octe i, neexistnd structuri de date specifice care s se aplice acestor fiiere. Programatorul poate s interpreteze datele dup cum dorete Prin urmare, prin scrierea/citirea datelor se scriu/citesc un numr de octe i fr o interpretare specific. Func iile de intrare/ieire, tipurile i macrodefini iile din "stdio.h" reprezint aproape o treime din bibliotec. n C, intrarea standard respectiv ieirea standard sunt n mod implicit reprezentate de terminalul de la care s-a lansat programul. Prin fiier n elegem o mul ime ordonat de elemente pstrate pe diferite suporturi. Aceste elemente se numesc nregistrri. Suporturile cele mai des utilizate sunt cele magnetice (floppy sau harddiscuri). Ele se mai numesc suporturi reutilizabile deoarece zona utilizat pentru pstrarea nregistrrilor unui fiier poate fi ulterior reutilizat ulterior pentru pstrarea nregistrrilor unui alt fiier. n C un fiier reprezint o surs sau o destina ie de date, care poate fi asociat cu un disc sau cu alte periferice. Biblioteca accept fiiere de tip text i binar, dei n anumite sisteme, de exemplu UNIX, acestea sunt identice. Un fiier de tip text este o succesiune de linii, fiecare linie avnd zero sau mai multe caractere terminate cu ' \n '. ntr-o alt

194

reprezentare, anumite caractere pot fi convertite ntr-o succesiune de caractere, adic s nu existe o rela ie unu la unu ntre caracterele scrise (citite) i ac iunea perifericului. De exemplu, caracterul NL (new line), ' \n ', corespunde grupului CR (carriage return) i LF (line feed). n aceeai idee, se consider c datele introduse de la un terminal formeaz un fiier de intrare. nregistrarea se consider c este format de datele unui rnd tastate de la terminal (tastatur, keyboard), deci caracterul de rnd nou NL se consider ca fiind terminator de nregistrare. n mod analog, datele care se afieaz pe terminal (monitor, display) formeaz un fiier de ieire. i n acest caz nregistrarea este format din caracterele unui rnd. Ceea ce este important de subliniat este c fiierele text pot fi accesate la nivel de octet sau de caracter, ele putnd fi interpretate drept o colec ie de caractere, motiv pentru care se i numesc fiiere text. Toate func iile de intrare/ ieire folosite pn acum se pot utiliza i pentru fiierele text. Un fiier de tip binar este o succesiune de octe i neprelucra i care con in date interne, cu proprietatea c dac sunt scrise i citite pe acelai sistem, datele sunt egale. Aceste fiiere sunt organizate ca date binare, adic octe ii nu sunt considera i ca fiind coduri de caractere. La fiierele binare nregistrarea se consider c este o colec ie de date structurate numite articole. Structurile de date sunt pretabile pentru stocarea n astfel de fiiere Tratarea fiierelor se poate face la dou nivele, inferior i superior. Nivelul inferior de prelucrare a fiierelor ofer o tratare a fiierelor fr zone tampon (buffere), fcnd apel direct la sistemul de operare. Rezervarea de zone tampon este lsat pe seama utilizatorului. Fiierele de tip text se preteaz la o astfel de tratare. Nivelul superior de prelucrare a fiierelor se bazeaz pe utilizarea unor proceduri specializate n prelucrarea fiierelor care printre altele pot rezerva i gestiona automat zonele tampon necesare. Fiierele binare se pot manipula cu facilitate la acest nivel. Func iile specializate de nivel superior au denumiri asemntoare cu cele de nivel inferior, doar prima liter a numelui este f. n practic opera iile de intrare/ieire (I/O) cu memoria extern (hard-disk sau floppy-disk) sunt mult mai lente dect cele cu memoria intern. Din aceast cauz, pentru a spori viteza de lucru, se ncearc

195

s se reduc numrul de opera ii de acces la disc. n acest scop se folosesc bufferele. Un buffer este o zon de memorie n care sistemul memoreaz o cantitate de informa ie (numr de octe i), n general mai mare dect cantitatea solicitat de o opera ie de I/O. Dac un program efectueaz o opera ie de citire a 2 octe i dintr-un fiier, atunci sistemul citete ntr-un buffer ntreg sectorul de pe disc (512 octe i) n care se gsesc i cei 2 octe i solicita i, eventual chiar mai mult, n func ie de dimensiunea bufferului (zonei tampon). Dac n continuare se vor solicita nc 2 octe i, acetia vor fi prelua i din bufferul din memorie, fr a mai fi nevoie s mai accesm discul pe care se afl fiierul din care se face citirea. Opera iile de citire continu n acest mod pn la citirea tuturor octe ilor din buffer, moment n care se va face o nou umplere a bufferului cu noi date prin citirea urmtorului sector de pe disc. Invers, dac un program efectueaz o opera ie de scriere a unui numr de octe i pe disc, acetia se vor nscrie de fapt secven ial n buffer i nu direct pe disc. Scrierea va continua astfel pn la umplerea bufferului, moment n care sistemul de operare efectueaz o opera ie de scriere a unui secto de pe disc cu cei 512 octe i din buffer (se golete bufferul prin scriere). n acest fel, reducnd numrul de opera ii de acces la disc (pentru citire sau scriere) crete viteza de execu ie a programelor i fiabilitatea dispozitivelor de I/O. Bufferele au o mrime implicit, dar ea poate fi modificat prin program. Dimensiunea trebuie aleas n func ie de aplica ie innd cont de faptul c prin mrirea bufferului crete viteza de execu ie dar scade dimensiunea memoriei disponibile codului programului i invers, prin micorarea sa crete memoria cod disponibil dar scade viteza de lucru. Bufferul de tastatur are, spre exemplu, dimensiunea de 256 octe i, din care 254 sunt pui la dispozi ie. Orice fiier are o nregistrare care marcheaz sfritul de fiier. n cazul fiierelor de intrare ale cror date se introduc de la terminal, sfritul de fiier se genereaz n func ie de sistemul de operare considerat. Pentru sistemele de operare MS-DOS sau MIX i RSX11 se tasteaz CTRL/Z iar pentru UNIX se tasteaz CTRL/U. Un fiier stocat pe suport magnetic se mai numete i fiier extern. Cnd se prelucreaz un astfel de fiier se creaz o imagine a acestuia n memoria intern (RAM) a calculatorului. Aceast imagine se mai numete i fiier intern.

196

Un fiier intern este conectat la un fiier extern sau dispozitiv prin deschidere; conexiunea este ntrerupt prin nchidere. Deschiderea unui fiier ntoarce un pointer la un obiect de tip FILE, care con ine toate datele necesare pentru controlul fiierului. Opera iile de deschidere i nchidere a fiierelor se poate realiza n C prin func ii specializate din biblioteca standard I/O a limbajului. Alte opera ii care sunt executate frecvent n prelucrarea fiierelor sunt: Crearea unui fiier (acest fiier nu exist n format extern) Actualizarea unui fiier (deja existent) Adugarea de nregistrri unui fiier deja existent Consultarea unui fiier Pozi ionarea ntr-un fiier Redenumirea unui fiier tergerea unui fiier Ca i opera iile de deschidere i nchidere de fiiere, opera iile indicate mai sus pot fi realizate printr-un set de func ii aflate n biblioteca standard I/O a limbajului. Aceste func ii realizeaz ac iuni similare sub diferite sisteme de operare, dar multe dintre ele pot depinde de implementare. n cele ce urmeaz se prezint func iile care au o utilizare comun pe diferite medii de programare i sunt cele mai frecvent utilizate.

10.2. Opera ii cu fiiere


n acest subcapitol vom detalia principalele opera ii efectuate asupra unor fiiere. n timpul lucrului cu fiierele, sistemul de operare pstreaz un indicator de fiier care indic pozi ia curent n fiier, pozi ie la care se va face urmtoarea opera ie de scriere sau citire. De exemplu, la deschiderea unui fiier pentru citire indicatorul de fiier va indica la nceputul fiierului. Dac se va face o opera ie de citire a 2 octe i se vor citi octe ii cu numrul de ordine 0 i 1 iar indicatorul va indica spre urmtorul octet, adic cel cu numrul de ordine 3. Pentru o mai corect n elegere a acestor func ii le vom structura dup nivelul la care se utilizeaz: inferior sau superior. n momentul nceperii execu iei unui program, interfe ele standard (cu ecranul, tastatura i porturile seriale i paralele) sunt deschise n mod text. Principalele func ii sunt grupate n tabelul de mai jos:

197

Descriere
Deschidere Creare Citire Scriere nchidere Pozi ionare

Nume func ie de nivel inferior _open _creat _read _write _close _lseek _unlink _rename

Nume func ie de nivel superior fopen fcreate fread fwrite fclose fseek remove rename

tergere Redenumire

n afara acestor func ii principale mai exist anumite func ii specializate, cum ar fi: - Func ii pentru prelucrarea pe caractere a unui fiier: putc (scriere caracter) i getc (citire caracter). - Func ii pentru Intrri/Ieiri cu format: fscanf i fprintf. - Func ii pentru Intrri/Ieiri de iruri de caractere: fgets i fputs. Pentru ca sistemul de operare s poat opera asupra fiierelor ca fluxuri (stream) de intrare/ieire trebuie s cunoasc anumite informa ii despre ele. Acest lucru se realizeaz prin opera ia de deschidere a fluxurilor (stream-urilor). Pointerul fiier n urma opera iei de deschidere se creaz n memorie o variabil de tip structur FILE care este o structur predefinit. n aceast variabil, care se numete bloc de control al fiierului, FCB (File Control Block) sistemul pstreaz informa ii despre fiierul deschis, precum: Nume Dimensiune Atribute fiier Descriptorul fiierului Un pointer-fiier este un pointer la informa iile care definesc diferitele aspecte ale unui fiier: nume, stare, pozi ie curent. Un pointer-fiier este o variabil pointer de tip FILE, definit n "stdio.h". Tipul FILE este un tip structurat care depinde de sistemul de operare. Dac facem abstrac ie de cazurile speciale de calculatoare tip

198

VAX sau U3B, pe majoritatea implementrilor tipul FILE se definete prin urmtoarea structur:
typedef struct { unsigned char *_ptr; int _cnt; unsigned char *_base; char _flag; char _file; } FILE;

Variabila de tip FILE este creat i gestionat de ctre suportul pentru exploatarea fiierelor n limbajul C. n urma deschiderii unui fiier, programul primete un pointer la variabila creat, deci un pointer la o structur de tip FILE. Se spune c s-a deschis un stream (flux de date). Toate opera iile care se fac pe acest stream se refer la fiierul asociat stream-ului. n limbajul C exist 5 stream-uri standard, definite n <stdio.h>: FILE *stdin; care se refer la dispozitivul standard de intrare (tastatura). Orice opera ie de citire de la stream-ul stdin nseamn citire de la tastatur. Bufferul folosit are o dimensiune de 254 de caractere i bufferul se golete la tastarea NL (\n). Se mai spune c stdin este cu buffer la nivel de linie. FILE *stdout; care se refer la dispozitivul standard de ieire (ecranul). Orice opera ie de scriere la stream-ul stdout nseamn scriere pe ecran. Spre deosebire de stdin, stdout este ne-bufferizat deoarece orice scriere pe ecran se face direct la scrierea unui caracter n fiierul stdout. FILE *stderr; care se refer la dispozitivul standard pentru afiarea mesajelor de eroare (ecranul). Este ne-bufferizat. FILE *stdprn; care se refer la primul port paralel PRN la care se conecteaz de obicei imprimanta (LPT). Este bufferizat la nivel linie. FILE *stdaux; care se refer la primul port serial COM1. Este ne-bufferizat.

10.3. Nivelul inferior de prelucrare a fiierelor


La acest nivel opera iile de prelucrare a fiierelor se execut fr o gestiune automat a zonelor tampon, fcndu-se apel direct la sistemul de operare. Programatorul are n gestiune o zon declarat

199

drept buffer i trebuie s in cont de faptul c aceast bufferizare este la nivel linie. Numele func iilor de nivel inferior, orientate pe text (transfer de octe i) ncep de obicei cu _ (underline). Dac un fiier se deschide n modul text, atunci, n cazul citirii dintr-un fiier, secven a de octe i CR-LF (0DH, 0AH) este translatat (nlocuit) cu un singur caracter LF, iar n cazul scrierii n fiier caracterul LF este expandat la secven a CR-LF. De asemenea, n cazul MS-DOS sau Windows CTRL/Z este interpretat n cazul citirii drept caracter de sfrit de fiier (EOF).

10.3.1. Deschiderea unui fiier


Orice fiier nainte de a fi prelucrat trebuie deschis, motiv pentru care opera ia de deschidere a unui fiier este de mare importan . Deschiderea unui fiier existent se realizeaz prin intermediul func iei _open. La revenirea din ea se returneaz un aa numit descriptor de fiier. Acesta este un numr ntreg. El identific n continuare fiierul respectiv n toate opera iile realizate asupra lui. n forma cea mai simpl func ia _open se apeleaz printr-o expresie de atribuire de forma: df = _open(spf,mod) unde: df este un numr ntreg care reprezint descriptorul de fiier spf este specificatorul fiierului care se deschide mod definete modul de prelucrare a fiierului Specificatorul de fiier este fie un ir de caractere, fie un pointer spre un astfel de ir de caractere. Con inutul irului de caractere depinde de sistemul de operare folosit. n cea mai simpl form el este un nume sau mai general o cale care indic plasamentul pe disc al fiierului care se opereaz. Fiierele deschise la acest nivel pot fi prelucrate n citire (consultare), scriere (adugare de nregistrri) sau citire/scriere (actualizare sau punere la zi). Calea spre fiier trebuie s respecte conven iile sistemului de operare MS-DOS n general. n cea mai simpl form ea este un ir de caractere care definete numele fiierului, urmat de extensia fiierului. Aceasta presupune c fiierul se gsete n directorul curent. Dac fiierul nu se afl n fiierul curent, atunci numele este precedat de o construc ie de forma:
litera:\nume_1\nume_2\\nume_k

unde:

200

litera definete discul (n general A, B pentru floppy-disk i C, D,.. pentru hard-disk) nume_i este un nume de subdirector. Deoarece calea se include ntre ghilimele, caracterul \ se dubleaz. Spre exemplu, putem folosi o comand de deschidere de forma:
int d; d=_open(A:\\JOC\\BIO.C,O_RDWR);

caz n care fiierul BIO.C din directorul JOC de pe dscheta A se deschide n citire/scriere. n func ie de opera ia dorit, mod poate avea valorile: 0 - pentru citire 1 - pentru scriere 2 - pentru citire/scriere Deschiderea unui fiier nu reuete dac unul dintre parametri este eronat. n acest caz func ia _open returneaz valoarea (-1). int _open( const char *filename, int oflag [, int pmode] ); este defini ia general a func iei _open. Modul de acces mod se poate furniza n mod explicit printr-o variabil de tip ntreg (oflag) care poate avea valorile:
Variabila mod
_O_RDONLY

Modul de deschidere a fiierului


Fiierul se deschide numai n citire (read-only) Nu se poate specifica mpreun cu _O_RDWR sau _O_WRONLY Fiierul se deschide numai n scriere (write-only) Nu se poate specifica mpreun cu _O_RDWR sau _O_RDONLY Fiierul se deschide n citire/scriere (read/write) Fiierul se deschide pentru adugarea de nregistrri la sfritul su. Creaz i deschide un nou fiier pentru scriere. Nu are nici un efect dac fiierul este deja existent. Fiierul se prelucreaz n mod binar Fiierul este de tip text, adic se prelucreaz pe caractere sau octe i (implicit)

_O_WRONLY _O_RDWR _O_APPEND _O_CREAT _O_BINARY _O_TEXT

201

Men ionm c n MSDN aceste variabile se mai numesc i oflag (open-flag) i sunt definite n fiierul header FCNTL.H. n cazul n care oflag este _O_CREAT, atunci este necesar specificarea constantelor op ionale pmode, care se gsesc definite n SYS\STAT.H. Acestea sunt: _S_IREAD - este permis numai citirea fiierului _S_IWRITE - este permis i citirea (permite efectiv citirea/scrierea fiierului) _S_IREAD | _S_IWRITE - este permis i scrierea i citirea fiierului. Argumentul pmode este cerut numai cnd se specific _O_CREAT. Dac fiierul exist deja, pmode este ignorat. Altcumva, pmode specific setrile de permisiune asupra fierului care sunt activate cnd fiierul este nchis pentru prima oar. _open aplic masca curent de permisiune la fiier nainte de setarea accesului la fiier. Pentru a crea un fiier nou se va utiliza func ia _creat pentru a-l deschide. De fapt se deschide prin creare un fiier inexistent. Func ia este definit astfel: int _creat( const char *filename, int pmode ); n care parametrii au fost descrii mai sus. Protec ia unui fiier este dependent de sistemul de operare. Spre exemplu, n UNIX protec ia se definete prin 9 bi i ataa i oricrui fiier, grupa i n 3 grupe de cte 3 bi i. Fiecare bit controleaz o opera ie de citire, scriere, execu ie. Protec ia opera iilor se exprim fa de proprietar, grup sau oricine altcineva. Numrul octal 0751 permite proprietarului toate cele 3 opera ii indicate mai sus (7 = 1112), grupul la care apar ine proprietarul poate citi i executa fiierul (5 = 1012) iar al i utilizatori pot numai executa fiierul (1 = 0012). Func ia _creat poate fi apelat i n cazul n care se deschide un fiier deja existent, caz n care se pierde con inutul vechi al fiierului respectiv i se creaz n locul lui unul nou cu acelai nume. Fiecare din func iile _open sau _creat returneaz un specificator de fiier (handle) pentru fiierul deschis. Acest specificator este o valoare ntreag pozitiv. Implicit, stdin are specificatorul 0, stdout i stderr au specificatorii 1 respectiv 2 iar fiierele disc care sunt deschise primesc pe rnd valorile 3, 4,..etc. pn la numrul maxim admis de fiiere deschise.

202

Valoarea returnat -1 indic o eroare de deschidere, n care caz variabila errno este setat la una din valorile: EACCES (valoare 13) s-a ncercat deschiderea pentru scriere a unui fiier read-only sau modul de partajare a fiierului nu permite opera ia specificat sau calea nu specific un nume de fiier ci de director. EEXIST (valoare 17) flagurile _O_CREAT i _O_EXCL sunt specificate, dar numele de fiier este al unui fiier deja existent. EINVAL (valoare 22) unul dintre argumentele oflag sau pmode sunt invalide. EMFILE (valoare 24) nu mai sunt disponibile specificatoare de fiier (prea multe fiiere deschise). ENOENT (valoare 2) fiierul sau calea nu au fost gsite. Variabila global errno pstreaz codurile de eroare folosite de func iile perror (print error) sau strerror (string error) pentru tratarea erorilor. Constantele manifest pentru aceste variabile sunt declarate n STDLIB.H dup cum urmeaz: extern int _doserrno; extern int errno; errno este setat de o eroare ntr-un apel de func ie la nivel de sistem (la nivelul inferior). Deoarece errno pstreaz valoarea setat de ultimul apel, aceast valoare se poate modifica la fiecare apel de func ie sistem. Din aceast cauz errno trebuie verificat imediat nainte i dup un apel care poate s-o modifice. Toate valorile errno, definite drept constante manifest n ERRNO.H, sunt compatibile UNIX. Valorile valide pentru aplica iile Windows pe 32 de bi i sunt un subset al acestor valori UNIX. Valorile specificate mai sus sunt valabile pentru aplica ii Windows pe 32 de bi i. La o eroare, errno nu este setat n mod necesar la aceeai valoare cu codul erorii de sistem. Numai pentru opera ii de I/O se folosete _doserrno pentru a accesa codul erorilor sistemului de operare echivalent cu codurile semnalate de errno. Exemplu: Acest program foloseste _open pentru a deschide un fisier numit OPEN.C pentru citire si un fisier numit OPEN.OUT scriere. Apoi fisierele sunt inchise
#include #include #include #include <fcntl.h> <sys/types.h> <sys/stat.h> <io.h>

203

#include <stdio.h> void main( void ) { int fh1, fh2; fh1 = _open( "OPEN.C", _O_RDONLY ); if( fh1 == -1 ) perror( "open failed on input file" ); else { printf( "open succeeded on input file\n" ); _close( fh1 );}
fh2=_open("OPEN.OUT",_O_WRONLY|_O_CREAT,_S_IREAD|_S_IWRITE);

if( fh2 == -1 ) perror( "Open failed on output file" ); else {printf( "Open succeeded on output file\n" ); _close( fh2 );}}

Prin execu ia acestui program se vor ob ine urmtoarele mesaje pe display:


open failed on input file: No such file or directory Open succeeded on output file Press any key to continue

10.3.2. Scrierea ntr-un fiier


Scrierea ntr-un fiier se realizeaz folosind func ia _write. Se presupune c fiierul respectiv a fost n prealabil deschis prin func iile _open sau _creat. Ea este asemntoare cu func ia _read, doar c se realizeaz transferul de date n sens invers i anume din memorie pe suportul fiierului. Func ia _write, ca i _read, se apeleaz printr-o atribuire de forma: nr = _read(df,zt,n) unde: nr este o variabil de tip ntreg creia i se atribuie numrul de octe i scrii n fiier. df este descriptorul de fiier returnat de func ia _open la deschiderea sau _creat la crearea fiierului. zt - este un pointer spre zona tampon definit de utilizator, zon din care se face scrierea. n este dimensiunea zonei tampon sau numrul de octe i care se dorete s se scrie. Defini ia func iei este: int _write( int handle, const void *buffer, unsigned int count );

204

Func ia _write scrie count octe i din buffer n fiierul asociat cu descriptorul handle. Opera ia de scriere ncepe la pozi ia curent a pointerului de fiier asociat cu fiierul dat. Dac fiierul este deschis cu indicatorul _O_APPEND, opera ia de scriere ncepe la sfritul fiierului. Dup o opera ie de scriere pointerul de fiier este incrementat cu numrul de bi i scrii efectiv. Dac fiierul a fost deschis n mod text (implicit), atunci _write trateaz CTRL/Z drept un caracter ce indic sfritul logic al fiierului. Cnd se scrie ntr-un dispozitiv, _write trateaz CTRL/Z din buffer drept terminator al opera iei de ieire. n general trebuie ca la revenirea din func ia _write s avem nr=n, ceea ce semnific faptul c s-au scris pe disc exact numrul de bi i din buffer. n caz contrar scrierea este eronat: aceasta semnific faptul c pe disc a rmas mai pu in spa iu (n octe i) dect numrul de octe i ai bufferului. Dac valoarea returnat este -1, se semnalizeaz eecul opera iei de scriere. n acest caz variabila global errno poate avea una din valorile EBADF (care semnific un descriptor de fiier invalid sau c fiierul nu a fost deschis pentru scriere) sau ENOSPC (care semnific lipsa spa iului pe disc pentru opera ia de scriere). Func ia _write poate fi utilizat pentru a scrie pe ieirile standard (display). Astfel, pentru a scrie pe ieirea standard identificat prin stdout se folosete descriptorul 1, iar pentru a scrie pe ieirea standard pentru erori, stderr, se folosete descriptorul de fiier 2. De asemenea, n acest caz nu este nevoie s apelm func ia _open sau _creat deoarece fiierele respective se deschid automat la lansarea programului. Exemplu:
/*Acest program deschide un fisier pentru scriere si foloseste _write pentru a scrie octeti in fisier*/ #include <io.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> char buffer[]="This is a test of '_write' function"; void main( void ) { int fh; unsigned byteswritten; if((fh=_open("write.o",_O_RDWR|_O_CREAT, _S_IREAD|_S_IWRITE))!=-1) { if((byteswritten = write(fh,buffer,sizeof(buffer)))== -1)

205

perror( "Write failed" ); else printf( "Wrote %u bytes to file\n", byteswritten ); _close( fh );}}

n urma execu iei programului, se va afia mesajul:


Wrote 36 bytes to file Press any key to continue

10.3.3. Citirea dintr-un fiier


Citirea dintr-un fiier deschis n prealabil cu func ia _open se realizeaz cu ajutorul func iei _read. Ea returneaz numrul efectiv al octe ilor citi i din fiier. Func ia _read se poate apela folosind o expresie de atribuire de forma: nr = _read(df,zt,n) cu defini ia general: int _read( int handle, void *buffer, unsigned int count ); unde: nr este o variabil de tip ntreg creia i se atribuie numrul de octe i citi i din fiier. df este descriptorul de fiier returnat de func ia open la deschiderea sau creat la crearea fiierului. zt - este un pointer spre zona tampon definit de utilizator, zon n care se face citirea. n reprezint numrul de bi i care se citesc Func ia _read citete maximum count octe i n buffer din fiierul asociat cu descriptorul handle. Opera ia de citire ncepe de pe pozi ia curent ndicat de pointerul de fiier asociat cu fiierul dat. Dup opera ia de citire, pointerul de fiier indic spre urmtorul octet necitit din fiier. Dac fiierul a fost deschis n mod text, citirea se termin la ntlnirea caracterului CTRL/Z, care este interpretat drept indicator de sfrit de fiier. _read returneaz numrul de bi i citi i din fiier, care poate fi mai mic dect count dac sunt mai pu ini dect count octe i rmai n fiier sau dac fiierul este deschis n mod text. n acest caz fiecare pereche CR-LF (carriage returnlinefeed) (CR-LF) este nlocuit cu un singur caracter LF. Numai acest caracter LF se consider n valoarea returnat. nlocuirea nu afecteaz pointerul de fiier. Dac func ia ncearc s citeasc dup sfritul fiierului, se returneaz valoarea 0. Dac descriptorul de fiier (handle) este invalid sau dac

206

fiierul nu este deschis pentru citire sau dac este blocat, func ia returneaz valoarea negativ -1 i seteaz variabila errno la EBADF. Tipul erorii i depistarea ei este dependent de sistemul de operare utilizat. Dac n = 1, se citete un singur octet. De obicei, nu este eficient s se citeasc cte un octet dintr-un fiier, deoarece apelurile multiple ale func iei _read pot conduce la un consum de timp apreciabil. Dimensiunea maxim a lui n este dependent de sistemul de operare. O valoare utilizat frecvent este 512, valoare optim pentru MS-DOS sau pentru UNIX. Func ia _read citete maximum count bi i n zona buffer din fiierul cu descriptorul handle. Opera ia de citire ncepe de la pozi ia curent a pointerului de fiier asociat cu fiierul respectiv. Dup o opera ie de citire, pointerul fiier indic spre urmtorul caracter (octet) necitit din fiier. Dac fiierul a fost deschis n mod text, _read se termin cnd se ntlnete indicatorul de fiier CTRL/Z. Func ia _read poate fi utilizat pentru a citi de la intrarea standard (tastatur). n acest caz descriptorul de fiier are valoarea 0. De asemenea, n acest caz nu este nevoie s apelm func ia _open deoarece fiierul se deschide automat la lansarea programului. Exemplu:
/* Acest program deschide fisierul WRITE.O creat anterior si incearca sa citeasca 60000 octeti din fisier folosind _read. Apoi va afisa numarul de octeti cititi */ #include <fcntl.h> /* _O_RDWR */ #include <io.h> #include <stdlib.h> #include <stdio.h> char buffer[60000]; void main( void ) { int fh; unsigned int nbytes = 60000, bytesread; /* Deschide fisierul in citire: */ if( (fh = _open( "write.o", _O_RDONLY )) == -1 ) { perror( "open failed on input file" ); exit( 1 ); } /* Read in input: */ if((bytesread = _read(fh,buffer,nbytes)) <= 0) perror( "Problem reading file" ); else Necesara numai pentru definirea

207

printf( "Read %u bytes from file\n", bytesread ); _close( fh );}

La execu ia programului se va afia urmtorul mesaj:


Read 36 bytes from file Press any key to continue

10.3.4. nchiderea unui fiier


Dup terminarea prelucrrii unui fiier el trebuie nchis. Acest lucru se realizeaz automat dac programul se termin prin apelul func iei exit. Programatorul poate nchide un fiier folosind func ia _close. Se recomand ca programatorul s nchid orice fiier de ndat ce s-a terminat prelucrarea lui, deoarece numrul fiierelor ce pot fi deschise simultan este limitat ntre 15 i 25, n func ie de sistemul de operare. Men ionm c fiierele corespunztoare intrrilor i ieirilor standard nu trebuie nchise de programator. Defini ia func iei este:
int _close( int handle );

Func ia _close nchide fiierul asociat cu descriptorul handle. Func ia _close returneaz valoarea 0 la o nchidere reuit i -1 n caz de incident. Apelul ei se realizeaz printr-o expresie de atribuire de forma: v =_ close(df) unde: v este variabila de tip ntreg ce preia valoarea returnat de func ie df este descriptorul de fiier (handle) al fiierului pe care dorim s-l nchidem.

10.3.5. Pozi ionarea ntr-un fiier


Opera iile de citire/scriere ntr-un fiier se execut secven ial, astfel nct: - fiecare apel al func iei _read citete nregistrarea din pozi ia urmtoare din fiier - fiecare apel al func iei _write scrie nregistrarea n pozi ia urmtoare din fiier. Acest mod de acces la fiier se numete secven ial i el este util cnd dorim s prelucrm fiecare nregistrare a fiierului. n practic apar ns i situa ii n care noi dorim s scriem i s citim nregistrri ntr-o ordine oarecare. n acest caz se spune c accesul la fiier este aleator. Pentru a realiza un acces aleator este nevoie s ne putem

208

pozi iona oriunde n fiierul respectiv O astfel de pozi ionare este posibil pe hard-uri i floppy-uri prin func ia _lseek. Defini ia func iei este:
long _lseek( int handle, long offset, int origin );

Func ia _lseek mut pointerul de fiier asociat cu descriptorul handle (df) pe o nou loca ie care este situat la offset octe i de origin. Urmtoarea opera ie de citire/scriere se va efectua de la noua loca ie. Argumentul origin trebuie s fie una dintre urmtoarele constante, definite n STDIO.H: SEEK_SET nceputul fiierului (valoare 0) SEEK_CUR pozi ia curent a pointerului de fiier (valoare 1) SEEK_END sfritul fiierului (valoare implicit 2) Func ia _lseek returneaz valoarea 0 la pozi ionare corect i -1 la incident. Ea poate fi apelat prin:
v = _lseek(df, deplasament, origine)

unde: v este o variabil de tip ntreg creia i se atribuie valoarea returnat de ctre func ie (0 sau -1) df este descriptorul de fiier (handle) a crui valoare a fost definit la deschiderea sau crearea fiierului. deplasament este o variabil de tip long i con ine numrul de octe i peste care se va deplasa capul de citire/scriere al discului. origine are una din valorile 0 - deplasamentul se socotete de la nceputul fiierului; 1 - deplasamentul se socotete din pozi ia curent a capului de citire/scriere; 2 - deplasamentul se socotete de la sfritul fiierului. Men ionm c prin apelul lui _lseek nu se realizeaz nici un fel de transfer de informa ie ci numai pozi ionarea n fiier. Opera ia urmtoare realizat prin apelul func iei _read sau _write se va realiza din aceast pozi ie. Spre exemplu, apelul:
v = _lseek(df, 0l, 2)

permite s se fac pozi ionarea la sfritul fiierului. n continuare se pot aduga articole folosind func ia _write. Pozi ionarea la nceputul fiierului se face prin apelul:
v = _lseek(df, 0l, 0)

Exemplu:
#include <io.h> #include <fcntl.h> #include <stdlib.h>

209

#include <stdio.h> void main( void ) { int fh; long pos; /* Pozitia pointerului fisier */ char buffer[10]; fh = _open( "write.o", _O_RDONLY ); /* Pozitionare la inceputul fisierului: */ pos = _lseek( fh, 0L, SEEK_SET ); if( pos == -1L ) perror( "_lseek inceput nu a reusit!" ); else printf("Pozitia pentru inceputul fisierului = %ld\n", pos ); /* Muta pointerul fisier cu 10 octeti */ _read( fh, buffer, 10 ); /* Gaseste pozitia curenta: */ pos = _lseek( fh, 0L, SEEK_CUR ); if( pos == -1L ) perror( "_lseek pozitia curenta nu a reusit!" ); else printf( "Pozitia curenta = %ld\n", pos ); /* Pozitionare pe ultima pozitie: */ pos = _lseek( fh, 0L, SEEK_END ); if( pos == -1L ) perror( "_lseek sfarsit nu a reusit!" ); else printf( "Pozitia ultima este = %ld\n", pos ); _close( fh );}

n urma execu iei programului se va afia:


Pozitia pentru inceputul fisierului = 0 Pozitia curenta = 10 Pozitia ultima este = 36 Press any key to continue

10.3.6 tergerea unui fiier


Un fiier poate fi ters apelnd func ia _unlink astfel: v = _unlink(spf) unde v este o variabil de tip ntreg creia i se atribuie valoarea 0 pentru tergere reuit i (-1) pentru tergere nereuit. spf este specificatorul de fiier folosit la deschidere a fiierului. Defini ia func iei este: int _unlink( const char *filename ); Func ia _unlink terge de pe disc fiierul specificat prin filename. Exemplu:

210

/* Acest program sterge fisierul WRITE.O creat si prelucrat anterior. */


#include <stdio.h> void main( void ) { if( _unlink( "write.o" ) == -1 ) perror( "Nu se poate sterge 'WRITE.O'" ); else printf( "S-a sters 'WRITE.O'\n" );}

n urma execu iei programului se afieaz:


S-a sters 'WRITE.O' Press any key to continue

10.3.7. Exemple de utilizare a func iilor de intrare/ieire de nivel inferior


1. S se scrie un program care copiaz intrarea standard la ieirea standard. Aceast problem se poate rezolva uor prin folosirea func iilor getchar i putchar. Acum o vom rezolva folosind func iile _read i _write.
# include <stdio.h> # include <io.h> void main() /* copiaza standard */ { char c[1]; while (_read(0,c,1)>0) _write(1,c,1);} intrarea standard la iesirea

Men ionm c cel de-al doilea parametru al func iei _read sau _write trebuie s fie pointer spre caractere. Lucrul la nivelul inferior nu este chiar att de simplu pe ct pare. Vom ilustra n continuare responsabilitatea pe care o are programatorul n gestionarea zonelor tampon. S considerm exemplul anterior n care zona tampon o mrim la 3 caractere, deci programul arat astfel:
# include <stdio.h> # include <io.h> void main() { char c[3]; while (_read(0,c,3)>0) _write(1,c,3);}

Citirea nu se va opri dup 3 caractere, ci func ia _read va continua s func ioneze pn la tastarea ENTER (CR+LF). Imediat func ia _read va tipri grupele de 3 caractere introduse, inclusiv grupul final CR+LF. Zona tampon definit este supranscris de fiecare dat cnd se introduc noi caractere.

211

Dac de la tastatur vom introduce 123456<CR><LF> atunci se va tipri primul grup (prima nscriere a zonei tampon) 123, apoi a doua grup 456 i grupul <CR> i <LF> va supranscrie primele dou caractere ale bufferului, anume codurile ASCII ale lui 4 i 5 i se va tipri <CR><LF>6. 123456 123456 6 Primul grup 123456 este scris prin ecou de la tastatur, iar urmtoru se nscrie de ctre program. Dac n continuare vom introduce 1<ENTER> atunci se va tipri 1 urmat de dou rnduri noi deoarece fiecare CR sau LF sunt expandate de stdout n perechi <CR><LF>. Dac mrim la 5 dimensiunea bufferului i de la tastatur introducem 12<ENTER>, atunci se va tipri 12 12 deoarece cel de-al 5-lea octet al bufferului nu a fost alocat prin citire, avnd o valoare nedefinit. Problemele de mai sus legate de gestiunea bufferului n/din care se face citirea/scrierea pot fi depite cu o modificare simpl, prezentat mai jos. Prin scriere nu se vor trimite spre stdout dect numrul de caractere citit de la stdin.
# include <stdio.h> # include <io.h> # define LZT 10 // lungime zona tampon void main() /* copiaza intrarea standard standard */ { char zt[LZT]; int n; while ((n=_read(0,zt,LZT))>0) _write(1,zt,n);}

la

iesirea

Programatorul trebuie s in cont ns i de alte amnunte cum ar fi dimensiunea implicit a bufferului stdin, care este de 254 de caractere. 2. S se scrie un program care citete un ir de numere flotante de la intrarea standard i creaz 2 fiiere fis1.dat i fis2.dat, primul con innd numerele de ordin impar citite de la intrarea standard

212

(primul, al 3-lea, al 5-lea, etc.) iar cel de-al doilea pe cele de ordin par citite de la aceeai intrare. Apoi s se listeze, la ieirea standard, cele dou fiiere n ordinea fis1.dat, fis2.dat cte un numr pe un rnd n formatul numr de ordine: numr Vom scrie programul folosindu-ne de func ii definite de utilizator care s fac apel la func iile de nivel inferior. Programul arat astfel:
# include <stdio.h> # include <io.h> # include <sys/types.h> # include <sys/stat.h> # include <fcntl.h> #include <stdlib.h> char nume1[]="fis1.dat"; char nume2[]="fis2.dat"; union unr { float nr; char tnr[sizeof(float)];}; union unr nrcit; int creare_fis(const char *nume) { int df; if ((df=_creat(nume,_S_IWRITE|_S_IREAD))==-1) { printf("%s: ",nume); printf("Nu se poate deschide fisierul in creare\n"); exit(1);} return df;} void scrie_fis(int df,char *nume) {if(_write(df,nrcit.tnr,sizeof(float))!=sizeof(float)) { printf("%s: ",nume); printf("Eroare la scrierea fisierului\n");exit(1);}} void date_fis(int df1,char *nume1,int df2,char *nume2) { int j=1,i; while ((i=scanf("%f",&nrcit.nr))==1) { if(j%2) scrie_fis(df1,nume1); else scrie_fis(df2,nume2); j++;}} void inchid_fis(int df, char *nume) { if (_close(df)<0) { printf("%s: ",nume); printf("eroare la inchiderea fisierului\n"); exit(1);}} int deschid_fis_cit(char *nume) { int df; if ((df=_open(nume,_O_RDONLY))==-1) {

213

printf("%s: ",nume); printf("Nu se poate deschide fisierul in citire\n"); exit(1);} return df;} void list_fis(int df,char *nume,int ord) { int j,i; if (ord%2) j=1; else j=2; while ((i=_read(df,nrcit.tnr,sizeof(float)))>0) { printf("%6d: %g\n",j,nrcit.nr);j+=2;} if (i<0) { printf("%s: ",nume); printf("Eroare la citire din fisierul\n"); exit(1);} _close(df);} void main() { int df1,df2; df1=creare_fis(nume1); df2=creare_fis(nume2); date_fis(df1,nume1,df2,nume2); inchid_fis(df1,nume1);inchid_fis(df2,nume2); df1=deschid_fis_cit(nume1); df2=deschid_fis_cit(nume2); list_fis(df1,nume1,1);list_fis(df2,nume2,2);}

3. S se realizeze programul de mai sus folosind un singur fiier fis.dat. Programul va diferi fa de cel anterior prin faptul c nregistrrile se stocheaz ntr-un singur fiier, deci func ia de listare se va modifica pentru citirea din 2 n 2 a nregistrrilor. Dup fiecare citire din fiier, se va face un salt cu o nregistrare pentru a pozi iona capul de citire/scriere peste nregistrarea urmtoare.
# include <stdio.h> # include <io.h> # include <sys/types.h> # include <sys/stat.h> # include <fcntl.h> #include <stdlib.h> char nume[]="fis.dat"; union unr { float nr; char tnr[sizeof(float)];}; union unr nrcit; int creare_fis(const char *nume) { int df; if ((df=_creat(nume,_S_IWRITE|_S_IREAD))==-1) { printf("%s: ",nume); printf("Nu se poate deschide fisierul in creare\n");

214

exit(1);} return df;} void scrie_fis(int df,char *nume) { if (_write(df,nrcit.tnr,sizeof(float))!=sizeof(float)) { printf("%s: ",nume); printf("Eroare la scrierea fisierului\n");exit(1);}} void date_fis(int df,char *nume) { while (scanf("%f",&nrcit.nr)==1) { scrie_fis(df,nume);}} void inchid_fis(int df, char *nume) { if (_close(df)<0) { printf("%s: ",nume); printf ("eroare la inchiderea fisierului\n"); exit(1);}} int deschid_fis_cit(char *nume) { int df; if ((df=_open(nume,_O_RDONLY))==-1) { printf("%s: ",nume); printf("Nu se poate deschide fisierul in citire\n"); exit(1);} return df;} void list_fis(int df,char *nume) { int j,i; j=1; while ((i=_read(df,nrcit.tnr,sizeof(float)))>0) { printf("%6d: %g\n",j,nrcit.nr); // avans peste o inregistrare if(_lseek(df,(long)sizeof(float),1)==-1l) break; j+=2;} if (i<0) { printf("%s: ",nume); printf("Eroare la citire din fisierul\n"); exit(1);} j=2; // pozitionare pe prima inregistrare _lseek(df,0l,0); // avans la inregistrarea a doua _lseek(df,(long)sizeof(float),1); while((i=_read(df,nrcit.tnr,sizeof(float)))>0) {printf("%6d: %g\n",j,nrcit.nr); // avans peste o inregistrare if(_lseek(df,(long)sizeof(float),1)==-1l) break; j+=2;} if (i<0) { printf("%s: ",nume); printf("Eroare la citire din fisierul\n"); exit(1);} _close(df);}

215

void main() { int df; df=creare_fis(nume); date_fis(df,nume); inchid_fis(df,nume); df=deschid_fis_cit(nume); list_fis(df,nume);}

Atragem aten ia asupra modului n care lucreaz func iile de intrare/ieire pentru stdin i stdout fa de cele pentru disc. Dac intrrile i ieirile pentru perifericele standard le putem executa n formatul dorit cu ajutorul func iilor specializate scanf i printf, pentru lucrul cu discul variabila float este tratat sub forma unui grup de 4 octe i care se scriu sau se citesc pe disc aa cum este reprezentarea lor intern. Exist func ii specializate pentru scrierea/citirea pe disc cu format, dar care sunt de nivel superior. Ceea ce merit s subliniem este faptul c echivalentele de nivel superior pentru fiiere ale func iilor printf() i scanf() sunt fprintf() i fscanf(). Echipamentele periferice pot fi considerate fiiere externe i deci func iile specializate pentru I/O cu fiiere pot fi folosite i pentru opera ii de I/O cu echipamentele periferice. Func iile printf i scanf sunt proiectate pentru a lucra implicit cu fiierele stdout respectiv stdin, deci cu monitorul i tastatura.

10.4. Nivelul superior de prelucrare a fiierelor


Nivelul superior de prelucrare a fiierelor se refer la aa numitul format binar de reprezentare a informa iei n fiiere care la rndul su face apel la informa ia structurat. Bufferul este alocat automat i gestionat de func ii C specializate.

10.4.1. Func ia fopen()


Func ia fopen se apeleaz printr-o expresie de atribuire de forma: pf = fopen(spf,mod) unde: pf - este un pointer spre tipul FILE spf este specificatorul fiierului care se deschide mod este un ir de caractere care definete modul n care se deschide fiierul. Forma general de declarare a func iei fopen() este:
FILE *fopen(char *filename, char *mode);

216

Func ia deschide fiierul al crui nume este specificat prin "filename" (de obicei un fiier disc) i ntoarce un pointer la FILE pentru opera ie reuit i NULL pentru opera ie nereuit. Varibilele permise pentru modul "mode" sunt:
a a+ r r+ w w+ b t c n _O_WRONLY | _O_APPEND (usual _O_WRONLY | _O_CREAT | _O_APPEND) _O_RDWR | _O_APPEND (usual _O_RDWR | _O_APPEND | _O_CREAT ) _O_RDONLY _O_RDWR _O_WRONLY(usual _O_TRUNC) _O_WRONLY | _O_CREAT |

_O_RDWR (usual _O_RDWR | _O_CREAT | _O_TRUNC) _O_BINARY _O_TEXT Nimic Nimic

Modul "a" nu terge markerul de sfrit d fiier EOF nainte de a aduga la sfritul fiierului. Dup ce s-a fcut o adugare, comanda MS-DOS TYPE tiprete datele pn la markerul original EOF i nu pn la ultima dat adugat. Modul "a+" terge identificatorul de sfrit de fiier EOF nainte de adugarea de nregistrri la sfritul fiierului. Dup adugare comanda MS-DOS TYPE va tipri toate datele con inute n fiier. Modul "a+" este cerut pentru adugarea la sfritul unui fiier care are marker terminator CTRL/Z = EOF. Dac modul "mode" include "b" dup litera ini ial, ca n "rb" sau "w+b" se indic un fiier binar. Numele fiierului con ine cel mult FILENAME_MAX caractere. La un moment dat pot fi deschise cel mult FOPEN_MAX fiiere. Men ionm c stdin, stdout i stderr sunt pointeri spre tipul FILE i permit ca func iile de nivel superior de prelucrare a fiierelor s poat trata intrarea standard, ieirea standard i ieirea standard pentru erori, la fel ca i restul fiierelor. Singura deosebire const n

217

faptul c n acest caz programatorul nu trebuie s deschid sau s nchid fiierele respective. Exemplu:
FILE *fp, *fopen(); /* se declara pointerii de tip file *fp si *fopen() */ fp = fopen("test","w"); /* se deschide fisierul " test " pentru screiere */

Pentru detectarea unei erori la deschiderea unui fiier se utilizeaz secven a:


if ((fp = fopen("test", "w")) == NULL) { puts ("Cannot open file\n"); exit(1); }

Dac pentru opera ia de citire se ncearc deschiderea unui fiier inexistent, fopen() va returna o eroare.

10.4.2. Func ia fclose()


Forma general de declarare a func iei fclose() este:
int fclose(FILE *fp);

unde "fp" este pointerul la fiier returnat dup apelul func iei fopen(). Func ia fclose() se utilizeaz pentru a nchide un fiier deschis cu fopen(). Func ia fclose() scrie mai nti n fiier datele rmase n fiierul buffer apoi nchide fiierul. fclose() ntoarce zero (0) pentru nchidere reuit i EOF (-1) dac apare o eroare. La execu ia func iei exit se nchid automat toate fiierele deschise. Cu toate acestea, se recomand ca programatorul s nchid un fiier de ndat ce s-a terminat prelucrarea lui, altfel se poate ajunge n situa ia de a se depi numrul limit admis pentru fiierele care pot fi simultan deschise ntr-un program. Exemplu:
/* Acest program deschide fisierele numite "test1.c" si "test2.c".Apoi foloseste fclose pentru a inchide "test1.c" si _fcloseall pentru a inchide restul fisierelor deschise */
#include <stdio.h> FILE *stream1, *stream2; void main( void ){ int numclosed; /* Deschidere in citire (esec daca fisierul "test1.c" nu exista) */ if( (stream1 = fopen( "test1.c", "r" )) == NULL ) printf( "Fisierul 'test1.c' nu a fost deschis\n" ); else printf( "Fisierul 'test1.c' a fost deschis\n" ); /* Deschidere pentru scriere */ if( (stream2 = fopen( "test2.c", "w+" )) == NULL ) printf( "Fisierul 'test2.c' nu a fost deschis\n" ); else

218

printf( "Fisierul 'test2.c' a fost deschis\n" ); /* Inchide fisierul cu pointerul stream1 */ if( fclose( stream1 ) ) printf( "Fisierul 'test1.c' nu a fost inchis\n" ); /* Toate celelalte fisiere se inchid: */ numclosed = _fcloseall( ); printf( "Numarul fisierelor inchise cu _fcloseall: %u\n", numclosed );}

n urma execu iei programului se ob ine:


Fisierul 'test1.c' a fost deschis Fisierul 'test2.c' a fost deschis Numarul fisierelor inchise cu _fcloseall: 1 Press any key to continue

10.4.3. Func iile rename() i remove()


Func ia rename() schimb numele vechi al fiierului, "oldname", cu numele nou, "newname". ntoarce o valoare diferit de zero dac incercarea nu reuseste.
int rename (char *oldname, char *newname);

Func ia remove()
int remove(char *filename);

Func ia remove() elimin fiierul cu numele specificat, astfel nct o incercare ulterioar de deschidere a fiierului va eua. ntoarce o valoare diferit de zero dac ncercarea reuete.

10.4.4. Func ii de tratare a erorilor


a) Func ia ferror() Aceasta func ie determin dac n urma unei opera ii cu fiiere sa produs sau nu o eroare. Forma general de declarare este:
int ferror(FILE *fp)

unde "fp" este un pointer la fiier. Func ia ferror() ntoarce o valoare diferit de zero dac s-a detectat o eroare i o valoare 0 dac nu s-a detectat nici o eroare. b) Func ia feof()
int feof(FILE *fp)

Func ia feof() ntoarce o valoare diferit de zero dac indicatorul de sfrit de fiier este valid i o valoare zero dac indicatorul de sfrit de fiier nu este valid. c) Func ia perror()
void perror(const char *s)

219

Func ia perror() scrie s i un mesaj de eroare care depinde de implementare, corespunzator cu intregul din errno.h, astfel:
fprintf(stderr,"%s %s\n, s, "error message")

10.4.5. Func ii cu acces direct


a) Func ia fread() Permite citirea unui bloc de date. Forma general de declarare:
int fread(void *buffer,int num_bytes,int count,FILE *fp)

Func ia fread() citete din fiierul specificat prin "fp" cel mult "count" obiecte, fiecare obiect avnd lungimea egal cu "num_bytes" i i trimite n zona de memorie indirectat prin "buffer" . *fp este un pointer fiier la fiierul deschis anterior cu fopen(). Func ia ntoarce numrul de obiecte citite, acesta putnd fi mai mic dect cele cerute. Pentru a determina starea func iei se pot utiliza func iile feof(), ferror(). b) Func ia fwrite() Permite scrierea unui bloc de date. Forma general de declarare:
int fwrite(void *buffer,int num_bytes,int count, FILE *fp)

Func ia fwrite() scrie din zona (tabloul) "buffer" n fiierul indirectat prin "fp", "count" obiect de lungime "nr_bytes". Func ia ntoarce numrul de obiecte scrise, care, n caz de eroare este mai mic dect "count". Exemplu: Programul urmtor scrise un numr real pe disc
# include "stdio.h" void main() { FILE *fp; float f = 12.23; if ((fp = fopen ("test", "wb")) == NULL){ printf (" Cannot open file\n "); return; } fwrite (&f, sizeof (float), 1, fp); fclose (fp); }

Aa cum se vede din acest program, "buffer" poate fi o simpl variabil. Exemplu: Programul urmtor copiaz un tablou de numere reale "balance", n fiierul "balance":
# include "stdio.h" void main() { FILE *fp; float balance[100]; /* tabloul balance */ if ((fp = fopen("balance", "w+")) == NULL) { printf ("Cannot open file\n"); return;}

220

. . . . . . . . . . . . . . . . . fwrite (balance, sizeof (balance), 1, fp); . . . . . . . . . . . . . . . . . fclose (fp); }

Exemplu: Programul urmtor deschide fiierul FREAD.OUT i scrie n el 25 de caractere i apoi l redeschide i citete din nou caracterele din fiier dup care afieaz numrul caracterelor citite i con inutul.
#include <stdio.h> void main( void ) { FILE *stream; char list[30]; int i, numread, numwritten; /* Deschide fisierul in mod text: */ if( (stream = fopen( "fread.out", "w+t" )) != NULL ) { for ( i = 0; i < 25; i++ ) list[i] = (char)('z' - i); /* Scrie 25 caractere in fisier */ numwritten = fwrite(list,sizeof(char),25,stream ); printf( "S-au scris %d caractere\n", numwritten ); fclose( stream );} else printf( "Probleme cu deschiderea fisierului\n" ); if( (stream = fopen( "fread.out", "r+t" )) != NULL ) { /* Incearca sa citeasca 25 caractere */ numread = fread( list, sizeof( char ), 25, stream ); printf("Nr. caracterelor citite = %d\n", numread); printf( "Continutul bufferului = %.25s\n", list ); fclose( stream );} else printf( "Fisierul nu a putut fi deschis\n" );}

n urma execu ie programului se ob ine:


S-au scris 25 caractere Nr. caracterelor citite = 25 Continutul bufferului = zyxwvutsrqponmlkjihgfedcb Press any key to continue

10.4.6. Func ii pentru pozi ionare


a) Func ia fseek() Determin pozi ionarea fiierului la citire sau scriere, ncepnd cu pozi ia selectat. Forma func iei:
int fseek(FILE *fp, long offset, int origin)

unde "fp" este un pointer-fiier returnat prin apelul func iei fopen(), "offset" este deplasamentul (numr octe i) noii pozi ii fa de "origin", iar "origin" este una din urmtoarele macrodefini ii: SEEK_SET - nceput de fiier;

221

SEEK_CUR - pozi ie curent; SEEK_END - sfrit de fiier. Func ia returneaz 0 dac se execut cu succes i o valoare nenul n caz de eroare. Dac nu s-a efectuat nici o opera ie de I/O de la deschiderea fiierului n mod APPEND (adugare), atunci pointerul indic nceputul fiierului. Nu se recomanda utilizarea func iei fseek() pentru fiiere text; se sugereaz utilizarea acesteia numai pentru fiiere binare. Transla iile CR-LF efectuate n mod text pot cauza func ionarea defectoas a func iei fseek. Func ia fopen i toate celelalte func ii vor cuta s nlture caracterul CTRL/Z terminator de fiier (EOF). Singurele opera ii garantate s func ioneze corect cnd se utilizeaz fseek asupra fiierelor deschise n mod text este pozi ionarea cu offset 0 relativ la orice pozi ie din fiier i pozi ionarea fa de nceputul fiierului cu un offset returnat de func ia ftell(). Func ia ftell() este definit astfel:
long ftell( FILE *stream );

Func ia returneaz valoarea curent a pointerului fiier. Pozi ia este exprimat prin offsetul fa de nceputul fiierului. n cazul fiierelor deschise n mod text, acest offset nu reflect ntotdeauna exact numrul de octe i datorit transla iei CR-LF. Este preferat folosirea simultan a func iilor fseek i ftell pentru a opera asupra fiierelor text, dar se recomand folosirea lor n special asupra fiierelor binare. Exemplu: Pentru a citi cel de-al 235 byte din fiierul numit "test" se poate folosi urmtorul program:
func1() /* se declara func ia func1() */ { FILE *fp; if ((fp = fopen("test", "rb")) == NULL) { printf ("Cannot open file\n"); exit (1); } fseek(fp, 235L, 0); return getc(fp);} /* se citeste un caracter de la pozitia 235 */

Observa ie: L modific constanta 235 la tipul long int. Exemplu:


/* Acest program deschide fisierul FSEEK.OUT pointerul in diverse locuri din fisier */ #include <stdio.h> void main( void ) { FILE *stream; char line[81]; si muta

222

int result; stream = fopen( "fseek.out", "w+" ); if( stream == NULL ) printf( "Fisierul fseek.out nu s-a deschis\n" ); else {fprintf( stream, "Fseek incepe aici: " "Acesta este fisierul 'fseek.out'.\n" ); result = fseek( stream, 19L, SEEK_SET); if( result ) perror( "Fseek esec" ); else { printf( "Pointerul fisier este plasat la mijlocul primei linii.\n" ); fgets( line, 80, stream ); printf( "%s", line );} fclose( stream );}}

n urma execu ie programului se ob ine:


Pointerul fisier este plasat la mijlocul primei linii. Acesta este fisierul 'fseek.out'. Press any key to continue

10.4.7. Ieiri cu format


Func iile de tip printf() asigur conversiile de ieire cu format. a) Func ia fprintf() Forma acestei func ii este:
int fprintf(FILE *fp, "format", lista_argumente)

Func ia fprintf() realizeaz conversia i scrierea la ieire n fiierul indirectat cu "fp" sub controlul formatului, "format". Valoarea ntoars de func ie este numrul de caractere scrise, sau orice valoare negativ, dac apare o eroare. irul "format" con ine dou tipuri de obiecte: caractere obinuite care sunt copiate n fiierul de ieire i descriptori de conversie, fiecare determinnd conversia i tiprirea argumentelor din lista de argumente. Fiecare descriptor ncepe cu caracterul % i se ncheie cu un caracter de conversie. ntre % i caracterul de conversie pot exista: 1) Indicatori (n orice ordine): "-" - determin alinierea la stnga a argumentului convertit n cmpul de reprezentare; "+" - precizeaz c numrul va fi reprezentat cu semn; " " - dac primul caracter nu este un semn se va scrie un blanc la nceput; "0" - se utilizeaz n conversiile numerice i indic umplerea cu zerouri la nceputul cmpului;

223

"#" - indic o form de ieire alternativ : pentru "0", prima cifra va fi zero; pentru "x" sau "X", la nceputul fiecrui numr nenul se va scrie "0x" sau "0X"; pentru "e", "E", "g", "G", "f" ieirea va avea ntotdeauna un punct zecimal; pentru "g" i "G" nu se vor elimina zerourile de la sfrit. 2) Un numr care indic lungimea minim a cmpului de reprezentare. Argumentul convertit va fi tiprit ntr-un cmp cu o lungime cel pu in egal cu cea specificat, dac va fi nevoie i mai mare. Dac argumentul specificat are mai pu ine caractere, atunci cmpul va fi umplut la stnga sau la dreapta, func ie de aliniere. Caracterul de umplere este de obicei spatiul, dar este 0 dac s-a ales aceast optiune (exemplu: %05d). 3) Un punct ce separ lungimea cmpului de precizie. 4) Un numr, precizia, care indic numrul maxim de caracetre care se vor tipri dup virgul pentru "e", "E", sau "f", sau numrul de cifre semnificative pentru conversiile "g" sau "G", sau numrul maxim de caractere ce se vor tipri dintr-un ir. Lungimea, sau precizia, sau amndoua se pot specifica i prin "*". De exemplu: %10.4f - va afia un numr de cel pu in 10 caractere cu 4 caractere dup virgul; %5.7s - va afia un ir de cel pu in 5 caractere dar nu mai lung de 7 caractere; %-10.2f - va alinia la stnga un numr real cu 2 zecimale ntrun cmp de reprezentare de cel pu in 10 caractere. Descriptorii de conversie utiliza i de C sunt: %c - un singur caracter. %d, %i - nota ie zecimala cu semn. %x, %X - nota ie hexazecimal fr semn (fr 0x sau 0X). %u - nota ie zecimal fr semn. %s - caracterele din ir sunt tiprite pn se ntlnete '\0' sau ct timp numrul de caractere tiprit precizia. %f - nota ie zecimal de forma [-]mmm.ddd, unde numrul d-urilor este indicat de precizie; precizia implicit este 6, iar o precizie egal cu zero elimina punctul zecimal. %e, %E - nota ie zecimal de forma: [-]m.dddddde+/-xx sau [-]m.ddddddE+/-XX

224

unde numrul de d-uri este indicat de precizie (precizia implicit este 6, iar o precizie egal cu 0 va elimina punctul zecimal). %g, %G - se utilizeaz %e sau %E dac exponentul este mai mic dect -4, sau precizie, n caz contrar se utilizeaz %f. %p - afiseaza un pointer. %o - nota ie octala fr semn (fr 0 la nceput). %% - nu se face conversie, se tiprete "%". %n - nu se realizeaz conversie de argument; numrul de caractere scrise pn n acel moment este scris n argument. Exist doi modificatori de format care permit func iei fprintf() s afieze ntregii long i short. Aceti modificatori se pot aplic caracterelor de conversie d, i, o, u i x, precedndu-i pe acetia (exemplu: %ld, %li, %lu). Modificatorul l poate prefixa i caracterele de conversie e, f i g i indic faptul c numerele tiparite sunt de tip double. Modificatorul h comand func iei fprintf() s afieze short int. Atunci %hu va preciza c data este de tip short unsigned int.] b) Func ia printf() Forma func iei :
int printf("format", lista-argumente)

Func ia printf() este echivalent cu :


fprintf(stdout, "format", lista_argumente)

Exemplu:
printf() ("%-5.2f", 123.456) ("%5.2f", 3.4565) ("%10s", "hello") ("%-10s", "hello") (%5.7s", "123456789") ieire 123.45 3.45 hello hello 1234567

Exemplu de utilizare a functiei fprintf.


/* Acest program foloseste fprintf pentru scrierea datelor cu diferite formate intr-un fisier si apoi tipareste fisierul folosind functia sistem system ce apeleaza comanda TYPE a sistemului de operare */
#include <stdio.h> #include <process.h> FILE *stream; void main( void ) { int i = 10; double fp = 1.5; char s[] = "this is a string";

225

char c = '\n'; stream = fopen( "fprintf.out", "w" ); fprintf( stream, "%s%c", s, c ); fprintf( stream, "%d\n", i ); fprintf( stream, "%f\n", fp ); fclose( stream ); system( "type fprintf.out" );}

10.4.8. Intrri cu format


Func iile de tip scanf() realizeaz conversiile de intrare cu format a) Func ia fscanf() Forma acestei func ii este:
int
fscanf(FILE *fp, "format", lista_argumente)

Func ia fscanf() citete din fiierul indirectat prin "fp" sub controlul formatului "format" i atribuie valorile citite argumentelor urmtoare, fiecare argument trebuind s fie un pointer. Func ia ntoarce EOF dac se detecteaz sfritul de fiier sau apare o alt eroare nainte de orice conversie. n caz contrar, func ia ntoarce numrul de elemente care au primit valori. irul "format" poate con ine: - specificatori de conversie, constnd dintr-un caracter %, un caracter op ional de suprimare a atribuirii; un numr op ional care indic lungimea cmpului, un caracter op ional h, l sau L, care indic lungimea argumentului i un caracter de conversie; - spa ii sau caractere HT sau VT care se ignor; - caractere obinuite (diferite de %) care indic urmtorul caracter diferit de caracterele albe cu care ncepe fiierul de intrare. Un cmp de intrare se definete ca un ir de caractere diferite de cele albe i se ntinde pn la urmtorul caracter alb (spa iu, tab-uri, CR, LF, VT, FF). Rezultatul conversiei unui cmp de intrare este plasat n variabil indicat de argumentul corespunztor din lista de argumente. Dac se indic suprimarea atributului prin "*" ca n %*s, cmpul de intrare este ignorat, fr a se face nici o atribuire. Descriptorii de conversie utiliza i n C pentru citire sunt: %c - citete un singur caracter; caracterele urmtoare sunt plasate n tablourile indicate, respectndu-se numrul de caractere indicat de lungimea cmpului; implicit este 1. Nu se adaug '\0'. %d - citete un numr ntreg zecimal. %u - citete un numr ntreg zecimal fr semn.

226

%i - citete un numr ntreg (intregul poate fi octal, cu 0 la nceput, sau hexazecimal, cu 0x sau 0X la nceput). %o - ntreg octal (cu sau fr zero la nceput). %x - ntreg hexazecimal (cu sau fr 0x sau 0X la nceput). %s - ir de caractere diferite de caracterele albe, indicnd spre un tablou de caractere destul de mare pentru a memora irul i caracterele terminator '\0' care se va adauga. %e, %f, %g - numere zecimale n virgul mobil. %p - citete valoarea pointerului. %n - se scrie n argument numerele de caractere citite pn n acel moment. Nu se citete nimic din intrare. %h - citete un ntreg scurt. Un caracter obinuit n irul "format" determin ca func ia fscanf() s citeasc un caracter ce coincide cu cele din "format". De exemplu, "%d, %d" face c fscanf() s citeasc un ntreg, apoi caracterul "," i apoi un alt ntreg. Dac calculatorul nu gsete caracterul specificat, fscanf() va fi ncheiat. Toate variabilele menite s primeasc valori prin fscanf() trebuie s fie transmise prin adresele lor. Aceasta nseamn c toate argumentele trebuie s fie pointeri la variabilele utilizate ca argumente. b) Func ia scanf() Forma func iei:
int scanf("format", lista-argumente)

Func ia scanf() este echivalenta cu:


fscanf(stdin, "format", lista-argumente)

Exemple:
scanf ("%d", &count); /* se citete un ntreg n variabil count */ scanf ("%s", address); /* se citete un ir de caractere n vectorul address */ scanf ("%d %d", &r, &c); /* se citesc doua numere separate prin spa iu, tab sau linie noua */

Un * plasat ntre % i caracterul de conversie, va suspenda atribuirea datei citite. Astfel, instruc iunea :
scanf("%d%*c%d", &x, &y);

face ca, dac de la tastatur se introduce 10/20, 10 s fie atribuit lui x, %*c este ignorat (nu este atribuit), iar 20 se atribuie lui y. Instruc iunea :
scanf("%20s", sir);

227

citete nu mai mult de 20 caractere n variabil ir. Dac se introduce un ir de mai mult de 20 caractere, vor fi re inute numai primele 20, iar restul se pierd. Pentru caracterele rmase se poate apela din nou func ia scanf() sub forma :
scanf("%s", sir);

care va plasa restul caracterelor tot n "ir". Dac de la tastatura se introduce 10#20, instruc iunea :
scanf("%s#%s", &x, &y);

va plasa 10 n x i 20 n y. Instruc iunea :


scanf("%s ", name);

nu se ncheie dect dac dup ultimul caracter se introduce un spa iu. Exemplu de utilizare a func iilor fscanf i fprintf.
#include <stdio.h> FILE *stream; void main( void ) { long l; float fp; char s[81]; char c; stream = fopen( "fscanf.out", "w+" ); if( stream == NULL ) printf( "Fisierul fscanf.out nu a fost deschis\n" ); else { fprintf( stream, "%s %ld %f%c", "a-string", 65000, 3.14159, 'x' ); /* Seteaza pointerul la inceputul fisierului: */ fseek( stream, 0L, SEEK_SET ); /* Citeste datele inapoi din fisierul disc: */ fscanf( stream, "%s", s ); fscanf( stream, "%ld", &l ); fscanf( stream, "%f", &fp ); fscanf( stream, "%c", &c ); /* Tipareste datele citite din fisier: */ printf( "%s\n", s ); printf( "%ld\n", l ); printf( "%f\n", fp ); printf( "%c\n", c ); fclose( stream ); }}

/* Acest program scrie date cu format cu printf intr-un fisier. Apoi foloseste fscanf pentru a citi datele din fisier */

10.4.9. Func ii de citire i scriere a caracterelor


a) Func ia fgetc()
int fgetc(FILE *fp)

228

Func ia fgetc() ntoarce urmtorul caracter al fiierului indirectat cu "fp", caracter de tip unsigned char (convertit la int) sau EOF dac s-a detectat sfritul de fiier sau a aprut o eroare. Exemplu de utilizare a func iei fgetc().
/* Acest program foloseste getc pentru a citi 80 de caractere dintr-un fisier si apoi le plaseaza dintr-un buffer la intrarea standard */
#include <stdio.h> #include <stdlib.h> void main( void ) { FILE *stream; char buffer[81]; int i, ch; /* Deschide fisierul pentru a citi o inregistrare */ if( (stream = fopen( "fgetc.c", "r" )) == NULL ) exit( 0 ); /* Citeste primele 80 de caractere si le plaseaza in "buffer": */ ch = fgetc(stream); for(i=0;(i<80) && (feof(stream)==0); i++ ) { buffer[i] = (char)ch; ch = fgetc( stream ); } /* Adauga null la sfarsitul fisierului */ buffer[i] = '\0'; printf( "%s\n", buffer ); fclose( stream );}

b) Func ia getc()
int getc (FILE *fp)

Aceast func ie este identic cu fgetc() cu deosebirea c este o macrodefini ie, putnd s evalueze fiierul mai mult dect o dat. Observa ie: "fp" este un pointer-fiier returnat de func ia fopen(). Exemplu: Pentru a citi un fiier text pn la ntlnirea indicatorului de sfrit de fiier se scrie:
ch = getch (fp); while (ch != EOF) { ch = getc (fp); }

c) Func ia getchar()
int getchar(void)

Func ia getchar() este echivalent cu getc (stdin) . Dezavantajul func iei getchar() este c aceast poate pstra n bufferul de intrare nu unul, ci mai multe caractere, primul caracter fiind preluat dup apasarea tastei CR. d) Func iile getche() i getch()
int getche(void)

229

int

getch(void)

Func iile introduc un caracter de la tastatur. Func iile asteapt pn se apas o tast i apoi ntorc valoarea acesteia. Func ia getche() preia un caracter cu "ecou" iar getch() preia caracterul fr ecou. e) Func ia gets()
char *gets(char *s)

Func ia gets() citete un ir de caractere introduse de la tastatur i l plaseaz n vectorul indirectat prin s. irul se termin cu '\n' ce va fi nlocuit cu '\0'. Func ia ntoarce vectorul s sau EOF, n caz de eroare. f) Func ia fgets()
char *fgets(char *s, int n, FILE *fp)

Func ia fgets() citete n tabloul s cel mult n-1 caractere, oprindu-se dac a detectat NL (New Line) care este inclus n tablou, nlocuit prin'\0'. Func ia ntoarce tabloul s, sau NULL, dac apare o eroare sau sfrit de fiier. Exemplu de folosire a func iei fgets.
/* Acest program utilizeaza fgets pentru afisarea unei linii dintr-un fisier la display */
#include <stdio.h> void main( void ) { FILE *stream; char line[100]; if( (stream = fopen( "fgets.c", "r" )) != NULL ) { if( fgets( line, 100, stream ) == NULL) printf( "fgets error\n" ); else printf( "%s", line); fclose( stream ); }}

a') Func ia

fputc()

int fputc(int ch, FILE *fp)

Func ia fputc() scrie caracterul "ch" convertit la unsigned char, n fiierul indirectat prin "fp". ntoarce valoarea caracterului scris, sau EOF, dac apare vreo eroare. Exemplu de utilizare a func ie fputc.
/* Acest program foloseste fputc si _fputchar pentru a trimite un sir de caractere la stdout. */
#include <stdio.h> void main( void ) { char strptr1[] = "Test pentru fputc !!\n"; char strptr2[] = "Test pentru _fputchar!!\n"; char *p; /* Tipareste linia folosind fputc. */

230

p = strptr1; while((*p != '\0') && fputc(*(p++), stdout)!=EOF); /* Identic cu _fputchar. (Aceasta functie nu compatibila ANSI */ p = strptr2; while((*p != '\0') && _fputchar(*(p++))!=EOF);}

este

n general, func iile care ncep cu _ (subliniere) dar i cu f (de la file) sunt destinate lucrului cu interfe ele standard stdin i stdout. b') Func ia putc()
int putc(int ch, FILE *fp)

Func ia putc() este echivalenta cu fputc() cu excep ia c este o macrodefinitie, putnd evalua fiierul mai mult dect o dat. c') Func ia putchar()
int putchar(int ch)

Func ia putchar(ch) este echivalenta cu putc (ch, stdout). d') Func ia de la punctul d) nu are un corespondent pentru ieire. e') Func ia puts()
int puts(const char *s)

Func ia puts() scrie irul "s" i NL n "stdout". ntoarce EOF, dac apare o eroare, sau o valoare nenegativ n caz contrar. f') Func ia fputs()
int fputs(const char *s,FILE *fp)

Func ia fputs() scrie irul "s" (care nu este neaprat necesar s con in '\n') n fiierul "fp". ntoarce EOF, n caz de eroare, o valoare nenegativ, n caz contrar. Spre exemplu,
/* Acest program foloseste fputs pentru a scrie o linie la terminalul standard */
#include <stdio.h> void main( void ) { fputs( "Hello world from fputs.\n", stdout );}

Func ia ungetc()
int ungetc(int ch,FILE *fp)

Func ia ungetc() pune argumentul "ch" inpoi n fiier, de unde va fi preluat la urmtoarea citire. Se poate pune inapoi n fiier doar un singur caracter. Marcajul EOF nu poate fi pus napoi. Func ia ntoarce caracterul ce trebuie pus, sau EOF, n caz de eroare. Func iile getw() i putw() Aceste func ii se folosesc pentru a citi, respectiv a scrie ntregi dintr-un sau ntr-un fiier disc. Aceste func ii lucreaz exact c func iile getc() i putc(). Exemplu de utilizare a functiilor fprintf() i fscanf() :

231

Programul permite actualizarea unei agende telefonice.


# include "stdio.h" void ad_num(void); /*prototipul functiilor */ void cauta(void); char menu(void); void main() { char choice; do { choice = menu(); switch (choice) { case 'a' : ad_num(); break; case 'c' : cauta(); break; } } while (choice != 'q'); } char menu() {/* Afiseaza meniul si preia optiunea */ char ch; do { printf ("A(dauga), C(auta), Q(uit) : "); ch = tolower (getche()); printf ("\n"); } while (ch != 'q' && ch != 'a' && ch != 'c'); return ch; } void ad_num() /* Adauga un nou numar */ {FILE *fp; char name[80]; int a_code, schimb, numar; if ((fp = fopen ("telefon", "a")) == NULL) { printf ("Cannot open file \n"); exit (1);} printf("Introduceti numele si numarul: "); fscanf(stdin,"%s%d%d%d",nume,&a_code,&schimb, &numar); fscanf(stdin,"%*c"); /* inlatura CR din sirul de intrare */ /* Se scrie in fisier */ printf("%d",fprintf(fp,"%s %d %d %d\n", nume, a_cod, schimb, numar)); fclose (fp); } void cauta() /* Cauta un numar dandu-se un nume */ { FILE *fp; char nume[80], nume2[80]; int a_code, schimb, numar; /* Se deschide fisierul pentru citire */ if ((fp = fopen ("telefon", "r")) == NULL) { printf("Cannot open file\n "); exit (1); } printf ("Nume ?"); gets (nume); /* Se cauta numarul */ while (!feof (fp)) { fscanf(fp,"%s%d%d%d", nume2, &a_cod, &schimb, &numar); if (!strcmp(nume, nume2)) { printf("%s : (%d) %d %d\n", nume, a_code, schimb, numar); break;} } fclose (fp);}

232

Capitolul XI UTILIZAREA ECRANULUI N MOD TEXT


Biblioteca standard a limbajului C i C++ con ine func ii pentru gestiunea ecranului. Acesta poate fi gestionat n dou moduri: n mod text sau n mod grafic. Prezentm func iile standard mai importante utilizate la gestiunea ecranului n mod text. Toate func iile standard de gestiune a ecranului n mod text au prototipurile n fiierul antet <conio.h>. Modul text presupune c ecranul este compus dintr-un numr de linii i un numr de coloane. n mod curent se utilizeaz 25 de linii a 80 de coloane fiecare, deci ecranul are o capacitate de 25*80=2000 de caractere. Pozi ia pe ecran a unui caracter se definete printr-un sistem de dou coordonate ntregi (x,y) unde: x este numrul coloanei n care este situat caracterul y este numrul liniei n care este situat caracterul Col ul din stnga sus al ecranului are coordonatele (1,1), iar col ul din dreapta jos are coordonatele (80,25). n mod implicit, func iile de gestiune a ecranului n mod text au acces la tot ecranul. Accesul poate fi ns limitat la o parte din ecran utiliznd aa numitele ferestre. Fereastra este o parte a ecranului n form de dreptunghi i care poate fi gestionat separat de restul ecranului. Atributele unui caracter de pe ecran sunt urmtoarele: coordonatele x i y; culoarea caracterului; culoarea fondului pe care este afiat caracterul; clipirea caracterului.

11.1. Setarea ecranului n mod text


Se realizeaz cu ajutorul func iei textmode care are urmtorul prototip:
void textmode (int modtext)

233

unde modtext poate fi exprimat simbolic sau numeric n modul urmtor:


Modul text Caractere albe pe fond negru; 40 de coloane Color 40 de coloane Caractere albe pe fond negru; 80 de coloane Color 80 de coloane Monocrome 80 de coloane Color cu 50 de linii pentru placa VGA Modul precedent Constant simbolic BW40
C40 BW80 C80 MONO C4350 LASTMODE

Valoare 0
1 2 3 7 64 -1

Modul MONO se poate seta pe un adaptor monocolor, n timp ce celelalte moduri se pot seta pe adaptoare color.

11.2. Definirea unei ferestre


Dup setarea ecranului n mod text, este activ tot ecranul i acesta are caracteristicile indicate n paragraful precedent. De multe ori ns se dorete partajarea ecranului n zone care s poat fi gestionate n mod independent. Acest lucru poate fi realizat cu ajutorul ferestrelor. O fereastr se definete cu ajutorul func iei window care are urmtorul prototip:
void window (int x1, int y1, int x2, int y2);

unde: (x1,y1) reprezint coordonatele col ului stnga sus ; (x2,y2) reprezint coordonatele col ului dreapta jos. La un moment dat este activ o singur fereastr i anume acea definit la ultimul apel al func iei window. Dac parametri de la apelul func iei window sunt erona i, aceasta nu are nici un efect.

11.3. tergerea unei ferestre


O fereastr activ se terge utiliznd func ia clrscr care are urmtorul prototip: void clrscr(void) Dup apelul acestei func ii, fereastra activ (sau tot ecranul dac nu s-a definit n prealabil o fereastr cu func ia window) devine vid. Fondul ei are culoarea definit prin culoarea de fond curent.

234

Func ia clrscr pozi ioneaz cursorul pe caracterul din stnga sus al ferestrei active, adic n pozi ia de coordonate (1,1).

11.4. Deplasarea cursorului


Cursorul poate fi plasat pe un caracter al ferestrei active folosind func ia gotoxy ce are urmtorul prototip:
void gotoxy (int x, int y);

unde (x, y) reprezint coordonatele caracterului pe care se plaseaz cursorul. Dac la apelul func iei coordonatele sunt definite n afara ferestrei active, func ia este ignorat. Pozi ia cursorului din fereastra activ poate fi determinat cu ajutorul func iilor wherex i wherey care returneaz numrul coloanei, respectiv liniei, din fereastra activ pe care se afl cursorul, i care au urmtoarele prototipuri:
int wherex(void); int wherey(void);

n cazul n care se dorete ascunderea cursorului (facerea invizibil a cursorului) se utilizeaz o secven ce utilizeaz func ia geninterrupt:
{ _AH=1; _CH=0x20; geninterrupt(0x10); } Cursorul poate fi rafiaat utiliznd urmtoarea secven : { _AH=1; _CH=6; _CL=7; geninterrupt(0x10);}

11.5. Setarea culorilor


Culoarea fondului se seteaz textbackground ce are urmtorul prototip: cu ajutorul func iei

void textbackground(int culoare);

unde culoare este un ntreg n intervalul [0, 7] i are semnifica ia din tabelul de mai sus. Pot fi setate ambele culori precum i clipirea caracterului folosind func ia textattr ce are urmtorul prototip:
void textattr (int atribut)

unde atribut se definete cu ajutorul rela iei:

atribut=16*culoare_fond+culoare_caracter+clipire; 235

Culoarea caracterelor se seteaz cu ajutorul func iei textcolor ce are urmtorul prototip:
void textcolor(int culoare);

unde culoare este un ntreg n intervalul [0,15] a crui semnifica ie este explicat de tabelul urmtor:
Culoare Negru Albastru Verde Turcoaz Rou Purpuriu Maro Gri deschis Gri nchis Albastru deschis Verde deschis Turcoaz deschis Rou deschis Purpuriu deschis Galben Alb Clipire Constant simbolic BLACK BLUE GREEN CYAN RED MANGETA BROWN LIGHTGRAY DARKGRAY LIGHTBLUE LIGHTGREEN LIGHTCYAN LIGHTRED LIGHTMANGETA YELLOW WHITE BLINK Valoare
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 128

11.6. Func ii pentru gestiunea textelor


Pentru afiarea caracterelor se pot folosi func iile: - int putch (int c); afieaz un singur caracter; - int cputs (const char *str); afieaz un ir de caractere n mod similar func iei puts; - int cprintf (const char *format); afieaz date sub controlul formatelor n mod similar func iei printf. - void clreol (void); - terge sfritul liniei ncepnd cu pozi ia cursorului; - void delline (void); - terge toat linia pe care este pozi ionat cursorul; - int gettext (int left, int top, int right, int bottom, void *destination); - copiaz textul cuprins n dreptunghiul definit de coordonatele (left, top) stnga sus i (right, bottom) dreapta jos la adresa de memorie indicat de pointerul destination;

236

- int puttext( int left, int top, int right, int bottom, void *source ); - citete textul cuprins n dreptunghiul definit de coordonatele (left, top) stnga sus i (right, bottom) dreapta jos de la adresa de memorie indicat de pointerul source; - int movetext( int left, int top, int right, int bottom, int destleft, int desttop ); - mut textul cuprins n dreptunghiul definit de coordonatele (left, top) stnga sus i (right, bottom) dreapta jos n dreptunghiul cu coordonatele col ului din stnga sus (destleft, desttop); - void insline (void); - insereaz o linie vid n fereastra activ; - int getch (void); - citete un caracter fr ecou de la tastatur, adic dup ce este citit caracterul nu mai este afiat pe ecran; func ia returneaz codul ASCII al caracterului citit de la tastatur. - int getche (void); - citete un caracter cu ecou de la tastatur, adic dup ce este citit caracterul este afiat automat pe ecran; func ia returneaz codul ASCII al caracterului citit de la tastatur. - int kbhit (void); - controleaz dac s-a tastat ceva la tastatur. Dac a fost apsat o tast se returneaz o valoare diferit de zero, altfel se returneaz valoarea 0. Exemplu: Urmtorul program deseneaz o fereastr i scrie un numr n aceasta.
#include #include #include #include #include <stdio.h> <stdlib.h> <conio.h> <alloc.h> <dos.h>

#define MAX 100 #define SIMPLU 1 #define DUBLU 2 typedef struct{ int x,y,u,v; void *zonfer; }ELEM; ELEM *stiva[MAX]; int istiva; void orizontal(int,int); void vertical(int,int,int,int); void fereastra(int st,int sus,int dr,int jos,int fond,int culoare, int chenar,int n) //Afiseaza o fereastra limitata de un chenar { extern ELEM *stiva[]; extern int istiva;

237

//memoreaza partea din ecran pe care se va afisa fereastra if(istiva==MAX){ printf("\nPrea multe ferestre!"); exit(1); } if ((stiva[istiva]=(ELEM *)farmalloc(sizeof(ELEM)))==0){ printf("\nMemorie insuficienta\n"); exit(1); } stiva[istiva]->x=st; stiva[istiva]->y=sus; stiva[istiva]->u=dr; stiva[istiva]->v=jos; if((gettext(st,sus,dr,jos,stiva[istiva]->zonfer))==0){ printf("\nEroare la memorarea ecranului!"); exit(1); } istiva++; //Activeaza fereastra si o afiseaza pe ecran window(st,sus,dr,jos); textattr(16*fond+culoare); clrscr(); //Trasare chenar if (chenar){ textcolor(WHITE); highvideo(); switch(chenar){ case SIMPLU: putch(218); break; case DUBLU: putch(201); break; } orizontal(dr-st-2,chenar); switch(chenar){ case SIMPLU: putch(191); break; case DUBLU: putch(187); break; } vertical(jos-sus,1,2,chenar); gotoxy(1,jos-sus+1); switch(chenar){ case SIMPLU: putch(192); break; case DUBLU: putch(200);

238

break; } orizontal(dr-st-2,chenar); vertical(jos-sus-1,dr-st,2,chenar); gotoxy(dr-st,jos-sus+1); switch(chenar){ case SIMPLU: putch(217); break; case DUBLU: putch(188); break; } normvideo(); textattr(16*fond+culoare); } gotoxy(3,3); cprintf("%d",n); //Ascunde cursorul _AH=1; _CH=0x20; geninterrupt(0x10); } void orizontal(int a,int chenar) { while(a--) switch(chenar){ case SIMPLU: putch(196); break; case DUBLU: putch(205); break; } } void vertical(int a,int col,int lin,int chenar) { while(a--) { gotoxy(col,lin++); switch(chenar){ case SIMPLU: putch(179); break; case DUBLU: putch(186); break; } } } void main(void) { clrscr(); fereastra(4,4,60,20,3,10,2,6); getch(); }

239

Capitolul XII UTILIZAREA ECRANULUI N MOD GRAFIC


Pentru aplica iile grafice limbajul C pune la dispozi ie peste 60 de func ii standard ce au prototipul n fiierul graphics.h. n continuare sunt prezentate cele mai importante func ii ce permit gestiunea ecranului n mod grafic.

12.1. Ini ializarea modului grafic


Pentru a se putea lucra n mod grafic trebuie realizat o ini ializare utiliznd func ia initgraph. Aceasta poate fi folosit singur sau mpreun cu o alt func ie numit detectgraph care determin parametrii adaptorului grafic. Prototipul ei este:
void far detectgraph(int far *gd, int far *gm);

unde: Pointerul gd pstreaz adresa uneia din valorile din tabelul urmtor (n func ie de adaptorul grafic utilizat):
Constant simbolic CGA MCGA EGA EGA64 EGAMONO IBM8514 HERCMONO ATT400 VGA PC3270 Valoare
1 2 3 4 5 6 7 8 9 10

Valorile spre care pointeaz gd definesc nite func ii standard corespunztoare adaptorului grafic. Aceste func ii se numesc drivere. Ele se afl n subdirectorului BGI. Func ia detectgraph detecteaz adaptorul grafic prezent la calculator i pstreaz valoarea corespunztoare acestuia n zona spre care pointeaz gd.

240

Zona spre care pointeaz gm memoreaz una din valorile:


Adaptor
CGA

Constant simbolic Valoare


CGAC0 0 CGAC1 1 CGAC2 2 CGAC3 3 CGAHI 4 EGALO 0 EGAHI 1 VGALO 0 VGAMED 1 VGAHI 2

Rezolu ie
320*200 320*200 320*200 320*200 640*200 640*200 640*350 640*200 640*350 640*480

EGA VGA

Modul grafic se definete n aa fel nct el s fie cel mai performant pentru adaptorul grafic curent. Cele mai utilizate adaptoare sunt cele de tip EGA i VGA. Apelul func iei detectgraph trebuie s fie urmat de apelul func iei initgraph. Aceasta seteaz modul grafic n conformitate cu parametri stabili i de apelul prealabil al func iei detectgraph i are urmtorul prototip:
void far *cale); initgraph(int far *gd,int far *gm, int far

unde:

gd i gm sunt pointeri ce au aceeai semnifica ie ca i n cazul func iei detectgraph; cale este pointer spre irul de caractere care definete calea subdirectorului BGI care con ine driverele. De exemplu dac BGI este subdirector al directorului BORLANDC, atunci se utilizeaz irul de caractere:
C:\\BORLANDC\\BGI

Exemplu: Pentru setarea n mod implicit a modului grafic se poate utiliza urmtoarea secven de instruc iuni:
int gd,gm; detectgraph(&gd,&gm); initgraph(&gd,&gm, C:\\BORLANDC\\BGI);

Doar dup apelul func iei initgraph pot fi utilizate i alte func ii de gestionare a ecranului n mod grafic.

241

Utilizatorul poate defini el nsui parametri pentru ini ializarea modului grafic. De exemplu, secven a urmtoare:
int gd=1,gm=0; initgraph(&gd,&gm, C:\\BORLANDC\\BGI);

seteaz modul grafic corespunztor unui adaptor grafic CGA cu rezolu ia 320*200 de puncte. n afara acestor func ii mai pot fi utilizate i urmtoarele func ii: void far setgraphmode (int mode) utilizat pentru setarea modului grafic unde mode are valorile 0 4 pentru VGA, 0-1 pentru EGA, 0 2 pentru VGA; void far retorecrtmode(void) ce permite revenirea la modul precedent; void far graphdefaults(void) repune parametri grafici la valorile implicite; int far getgraphmode(void) returneaz codul modului grafic; char *far getmodename(int mod) returneaz pointerul spre numele modului grafic definit de codul numeric mod; char *far getdrivername(void) returneaz pointerul spre numele drieverului corespunztor adaptorului grafic curent; void far getmoderange(int grafdriv,int far *min, int far *max) definete valorile minimale i maximale ale modului grafic utilizat. void far closegraph(void) se utilizeaz pentru a iei din modul grafic.

12.2. Gestiunea culorilor


Adaptoarele grafice sunt prevzute cu o zon de memorie n care se pstreaz date specifice gestiunii ecranului. Aceast zon de memorie poart denumirea de memorie video. n mod grafic, ecranul se consider format din puncte luminoase numite pixeli. Pozi ia pe ecran a unui pixel se definete prin dou valori ntregi: (x,y) unde: x definete coloana n care este afiat pixelul; y definete linia n care este afiat pixelul. Fiecrui pixel i corespunde o culoare ce este ptrat n memoria video. Numrul maxim de culori care pot fi afiate cu ajutorul unui adaptor EGA este 64.. Culorile se codific prin numere ntregi din intervalul [0, 63] i prin constante simbolice. Cele 64 de

242

culori nu pot fi afiate simultan. n cazul adaptorului EGA pe ecran se pot afia cel mult 16 culori ce formeaz o palet. Paleta implicit este dat de tabelul urmtor:
Denumire simbolic BLACK BLUE GREEN CYAN RED MANGETA BROWN LLIGHTGRAY DARKGRAY LIGHTBLUE LIGHTGREEN LIGHTCYAN LIGHTRED LIGHTMANGETA YELLOW WHITE Valoare 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

n mod implicit, culoarea fondului este ntotdeauna cea corespunztoare indicelui zero, iar culoarea pentru desenare este cea corespunztoare indicelui 15. Pentru controlul culorilor pot fi utilizate urmtoarele func ii: void far setbkcolor(int culoare) modific culoarea fundalului; int far getbkcolor(void) returneaz indexul din tabloul care definete paleta pentru culoarea fundalului; void far setcolor(int culoare) seteaz culoarea utilizat pentru desenare; int far getcolor(void) returneaz indexul din tabloul care definete paleta pentru culoarea de desenare; void far setpalette(int index,int cod) seteaz o nou culoare n paleta ce este utilizat la colorare (index ia valori ntre [0, 15] iar cod ntre [0, 63]); void far setallpalette(struct palettetype far* paleta) modific mai multe culori din palet. Palettetype este o structur definit ca mai jos:
struct palettetype { unsigned char size;

243

signed char colors[MAXCOLORS+1]; };

unde size este dimensiunea paletei; colors este un tablou ale crui elemente au ca valori codurile culorilor componente ale paletei care se definete. Modificarea paletei curente cu ajutorul func iei setpalette sau setallpalette conduce la schimbarea corespunztoare a culorilor afiate pe ecran n momentul apelului func iilor respective. void far getpalette(struct palettetype far* paleta) determin codurile culorilor componente ale paletei curente; int far getmaxcolor(void) returneaz numrul maxim de culori diminuat cu 1; int far getpalettesize(void) returneaz numrul culorilor componente ale paletei.

12.3. Setarea ecranului


n mod grafic, ecranul se compune din n*m puncte luminoase (pixeli), adic pe ecran se pot afia m linii a n pixeli fiecare. Pozi ia unui pixel este dat de dou numere ntragi (x,y) numite coordonatele pixelului. Pixelul aflat n stnga sus are coordonatele (0,0). Coloanele se numeroteaz de la stnga la dreapta, iar liniile de sus n jos. Informa ii referitoare la ecran pot fi ob inute cu ajutorul urmtoarelor func ii: int far getmaxx(void) returneaz coordonta maxim pe orizontal; int far getmaxy(void) returneaz coordonta maxim pe vertical; int far getx(void) returneaz pozi ia pe orizontal a pixelului curent; int far gety(void) returneaz pozi ia pe vertical a pixelului curent.

12.4. Utilizarea textelor n mod grafic


Afiarea textelor n modul grafic presupune definirea unor parametri care pot fi controla i prin intermediul func iilor descrise n continuare: a) void far settextstyle(int font,int direc ie,int charsize) unde: font definete setul de caractere i poate lua urmtoarele valori:

244

Constant simbolic DEFAULT_FONT TRIPLEX_FONT SMALL_FONT SANS_SERIF_FONT GOTHIC_FONT

Valoare 0 1 2 3 4

direc ie definete direc ia de scris a textului, astfel: - de la stnga la dreapta: HORIZ_DIR; - de jos n sus: VERT_DIR. charsize definete dimensiunea caracterului n pixeli, astfel:
Valoarea parametrului 1 2 3 . 10 Matricea utilizat pentru afiarea caracterului (n pixeli) 8*8 16*16 24*24 .. 80*80

b) void far settextjustify(int oriz, int vert) definete cadrajul textului; oriz definete ncadrarea pe orizontal, astfel: - n stnga: LEFT_TEXT; - n centru: CENTER_TEXT; - n dreapta: RIGHT_TEXT. vert definete ncadrarea pe vertical, astfel: - marginea inferioar: BOTTOM_TEXT; - n centru: CENTER_TEXT; - marginea superioar: TOP_TEXT. Dup setarea acestor parametri pot fi afiate texte folosind func iile outtext i outtextxy care au urmtoarele prototipuri: void far outtext(char far* ir) , unde ir este un pointer spre zona de memorie n care se pstreaz caracterele de afiat, afieaz caracterele ncepnd cu pozi ia curent de pe ecran; void far outtextxy(int x,int y,char far* ir) , unde ir este un pointer spre zona de memorie n care se pstreaz caracterele de afiat, x,y definete pozi ia de pe ecran unde se face afiarea. Dimensiunile n pixeli ale unui ir de caractere se pot determina utiliznd func iile textheight i textwidth:

245

void far textheight(char far* ir) returneaz nl imea n pixeli a irului pstrat n zona spre care pointeaz ir, void far textwidth(char far* ir) returneaz ll imea n pixeli a irului pstrat n zona spre care pointeaz ir.

12.5. Gestiunea imaginilor


n modul grafic, ecranul poate fi partajat n mai multe pr i ce pot fi gestionate independent. Aceste pr i se numesc ferestre grafice. Urmtoarele func ii sunt utilizate pentru prelucrarea ferestrelor grafice: void far setviewport(int st, int sus, int dr, int jos, int d) definete o fereastr grafic, unde: - (st,sus) coordonatele col ului stnga sus al ferestrei; - (dr,jos) coordonatele col ului dreapta jos al ferestrei; - d indicator cu privire la decuparea desenului. Dac d are valoarea 1, atunci func iile de afiare a textelor i de desenare nu pot scrie sau desena n afara limitelor ferestrei. void far clearviewport(void) terge fereastra activ; dup apelul acestei func ii, to i pixelii ferestrei au aceeai culoare, i anume culoarea de fond, iar pozi ia curent a cursorului este punctul de coordonate relative (0,0); void far cleardevice(void) terge tot ecranul iar pozi ia curent a cursorului este col ul din stnga sus al ecranului; void far getviewsettings(struct viewporttype far* fereastra) returneaz parametri ferestrei active. Imaginea ecranului se pstreaz n memoria video a adaptorului grafic i formeaz o pagin. Func iile urmtoare sunt utilizate pentru gestionarea paginilor void far setactivepage(int nrpag) activeaz o pagin al crei numr este specificat de parametrul nrpag; void far setvisualpage(int nrpag) cu toate c n mod normal este vizualizat pe ecran pagina activ, utilizatorul are posibilitatea de a vizualiza alt pagin dect cea activ utiliznd aceast func ie (aceast func ie poate fi util pentru anima ie); void far getimage(int st, int sus, int dr, int jos,void far* zt) salveaz o zon dreptunghiular de pe ecran, unde: - (st,sus) coordonatele col ului stnga sus a zonei de pe ecran ce se salveaz;

246

(dr,jos) coordonatele col ului dreapta jos a zonei de pe ecran ce se salveaz; - zt pointer spre zona de memorie n care se salveaz imaginea de pe ecran. unsigned far imagesize(int st, int sus, int dr, int jos) determin dimensiunea unei zone dreptunghiulare de pe ecran, unde: - (st,sus) coordonatele col ului stnga sus a zonei de pe ecran; - (dr,jos) coordonatele col ului dreapta jos a zonei de pe ecran. void far putimage(int st, int sus, int jos,void far* zt, int op) afieaz oriunde pe ecran o zon dreptunghiular salvat cu func ia getimage, unde: - (st,sus) coordonatele col ului stnga sus a zonei de pe ecran ce se salveaz; - zt pointer spre zona de memorie n care se pstreaz imaginea ce se va afia pe ecran; - op definete opera ia ntre datele aflate n zona spre care pointeaz zt i cele existente pe ecran n zona dreptunghiular definit de parametri st, sus. Parametrul op se definete astfel:
Constant simbolic COPY_PUT XOR_PUT
OR_PUT AND_PUT NOT_PUT

Valoare
0 1 2 3 4

Ac iune

copiaz imaginea din memorie pe ecran sau exclusiv ntre datele de pe ecran i cele din memorie sau ntre datele de pe ecran i cele din memorie i ntre datele de pe ecran i cele din memorie copiaz imaginea din memorie pe ecran completnd datele aflate n memorie

12.6. Desenarea i colorarea figurilor geometrice


Biblioteca standard pune la dispozi ia utilizatorului o serie de func ii care permit desenarea i colorarea unor figuri geometrice: void far putpixel(int x, int y, int culoare) afieaz un pixel pe ecran n punctul de coordonate (x,y) (relativ la fereastra activ) i avnd culoarea culoare; unsigned far getpixel(int x, int y) determin culoarea unui pixel aflat pe ecran n pozi ia (x,y); void far moveto(int x, int y) mut cursorul n dreptul pixelului de coordonate (x,y);

247

void far moverel(int dx, int dy) mut cursorul n dreptul pixelului de coordonate (x+dx,y+dy), unde (x,y) reprezint coordonatele pixelului curent; void far line(int xi, int yi, int xf, int yf) traseaz un segment de dreapt ntre punctele de coordonate (xi,yi) i (xf,yf); void far lineto(int x, int y) traseaz un segment de dreapt ntre punctul curent i punctul de coordonate (x,y); void far linerel(int dx, int dy) traseaz un segment de dreapt ntre punctul curent i punctul de coordonate (x+dx,y+dy), unde (x,y) sunt coordonatele punctului curent; void far arc(int xcentru, int ycentru, int unghistart, int unghifin,int raza) traseaz un arc de cerc, unghiurile fiind exprimate n grade sexagesimale; void far circle(int xcentru, int ycentru, int raza) traseaz un cerc, cu (xcentru,ycentru) coordonatele centrului i raza raza acestuia; void far ellipse(int xcentru, int ycentru, int unghistart, int unghifin,int semiaxamare, int semiaxamic) traseaz un arc de elips cu centrul n punctul de coordonate (xcentru,ycentru), semiaxa mare definit de parametrul semiaxamare iar semiaxa mic definit de parametrul semiaxamic; void far rectangle(int st, int sus, int dr, int jos) traseaz un dreptunghi definit de col urile diagonal opuse; void far drawpoly(int nr, int far* tabpct) traseaz o linie polignal, parametrul nr specificnd numrul de laturi iar tabpct este un pointer spre un tablou de ntregi ce definesc vrfurile liniei poligonale pstrate sub forma: abscisa_i,ordonata_i unde i are valorile 1,2,., nr+1; void far setlinestyle(int stil, unsigned ablon, int grosime) definete stilul utilizat pentru trasarea liniilor, unde: stil este un ntreg din intervalul [0,4] care definete stilul liniei conform urmtorului tabel:
Constant simbolic SOLID_LINE DOTTED_LINE CENTER_LINE
DASHED_LINE USERBIT_LINE

Valoare 0 1 2
3 4

Stil Linie continu Linie punctat Linie ntrerupt format din liniu e de dou dimensiuni Linie ntrerupt format din liniu e de aceeai dimensiune Stil definit de utilizator prin ablon

248

ablon definete stilul liniei i are sens doar cnd parametrul stil are valoarea 4; grosime definete l imea liniei n pixeli, astfel: NORM_WIDTH valoarea 1 pixel i THICK_WIDTH valoarea 3 pixeli. void far getlinesettingstype(struct linesettingstype far* linieinfo) este utilizat pentru a determina stilul curent; void far bar(int st, int sus, int dr, int jos) are aceeai semnifica ie cu func ia rectangle ns dreptunghiul este colorat; void far bar3d(int st, int sus, int dr, int jos, int profunzime, int ind) func ia deseneaz o prism colorat pentru ind diferit de zero; pentru ind=0, nu se traseaz partea de sus a prismei; void far pieslice(int xcentru, int ycentru, int unghistart, int unghifin,int raza) deseneaz un sector de cerc colorat; void far fillpoly(int nr, int far* tabpct) deseneaz un poligon colorat; void far fillellipse(int xcentru, int ycentru, int semiaxamare, int semiaxamic) deseneaz o elips colorat; void far setfillstyle(int haura, int culoare) definete modul de colorare al figurilor, astfel: culoare definete culoarea utilizat pentru haurare; haura definete haura utilizat pentru colorare conform tabelului:
Constant simbolic EMPTY_FILL SOLID_FILL LINE_FILL LTSLASH_FILL SLASH_FILL BKSLASH_FILL LTBKSLASH_FILL HATCH_FILL XHATCH_FILL INTERLEAVE_FILL WIDE_DOT_FILL CLOSE_DOT_FILL USER_FILL Valoare 0 1 2 3 4 5 6 7 8 9 10 11 12

void far setfillpattern(char far *h_utilizator,int culoare) este utilizat pentru a defini o haur a utilizatorului, astfel:

249

culoare definete culoarea de haurare; h_utilizator este un pointer spre o zon de memorie care definete haura utilizatorului; void far getfillsettings(struct fillsettingstype far* stilculoare) este utilizat pentru determinarea stilului curent de colorare; void far floodfill(int x, int y, int culoare) este o func ie utilizat pentru colorarea unui domeniu nchis, astfel: (x,y) reprezint coordonatele unui punct din interiorul domeniului nchis; culoare definete culoarea utilizat la trasarea conturului figurii (interiorul este colorat n conformitate cu setrile efectuate de func ia setfillstyle). Exemplu: Prezentm n acest exemplu un model de utilizare a modului grafic pentru trasarea graficelor unor func ii matematice elementare.
#include <stdio.h> #include <math.h> #include <graphics.h> #include <conio.h> int x,y; float a,b; void desen(void) //functia care deseneaza axele si //coloreaza ecranul { cleardevice(); setbkcolor(14); setcolor(12); line(0,y,2*x,y); line(x,0,x,2*y); line(2*x-4,y-4,2*x,y); line(2*x-4,y+4,2*x,y); line(x,0,x-4,4); line(x,0,x+4,4); } void interval(int l1, int l2) //functia care verifica // daca intervalele pe care sunt definite functiile // trigonometrice, sunt respectate { while ((a<l1)||(b>l2)) { clrscr(); cleardevice(); setbkcolor(0); printf("reintroduce-ti intervalul astfel incat sa cuprins intre -1 si 1:\n "); printf("a="); scanf("%f",&a); printf("\n");

fie

250

printf("b="); scanf("%f",&b); printf("\n"); } desen();} void grafic(float (*trig)(float))//functia care traseaza // graficul functiilor trigonometrice { float ymax,i,i1,h,y0,y1,lx,ly; h=0.001*(b-a); if (abs(a)>abs(b)) lx=(x-25)/(abs(a)+1); else lx=(x-25)/(abs(b)+1); ymax=0; for(i=a;i<=b;i+=h) if (ymax<abs(trig(i))) ymax=abs(trig(i)); if (ymax>y/2) ymax=y-25; ly=(y-25)/(ymax+1); if (ly>lx) ly=lx; for(i=a;i<=b;i+=h) { y0=trig(i); i1=i*lx ; y1=y0*ly; putpixel(x+i1,y-y1,4);} } float sinx (float x) { return sin(x);} float cosx(float x) { return cos(x);} float tanx(float x) { return tan(x);} float ctanx(float x) { return 1/tan(x);} float acosx(float x) { return acos(x);} float asinx(float x) { return asin(x);} float atanx(float x) { return atan(x);} float actanx(float x) { return atan(1/x);} void main() { int p,l,t,dr=DETECT, modgr; initgraph(&dr,&modgr,"c:\\borlandc\\bgi"); setbkcolor(1); x=getmaxx()/2,y=getmaxy()/2; p=0; while(p==0) { setbkcolor(1); p=1; printf("1 : sin(x)\n");

251

printf("2 : cos(x)\n"); printf("3 : tg(x)\n"); printf("4 : ctg(x)\n"); printf("5 : arcsin(x)\n"); printf("6 : arccos(x)\n"); printf("7 : arctg(x)\n"); printf("8 : arcctg(x)\n"); printf("Alegeti nr. corespunzator functiei dorite: "); scanf("%d",&t); while(t<1 || t>8 ) { printf("Reintroducet t-ul cuprins intre 1 si 8\n "); printf("t="); scanf("%d",&t);} printf("Dati intervalul: \n"); do{ printf("a="); scanf("%f",&a); printf("\n"); printf("b="); scanf("%f",&b); printf("\n"); if (a>b) printf("Reintroduce-ti intervalul astfel incat a sa fie mai mic ca b:\n "); } while(a>b); desen(); switch(t) { case 1:grafic(sinx); break; case 2:grafic(cosx); break; case 3:grafic(tanx); break; case 4:grafic(ctanx); break; case 5:interval(-1,1); grafic(asinx); break; case 6:interval(-1,1); grafic(acosx); break; case 7:grafic(atanx); break; case 8:grafic(actanx); break; defalut: p=0 ; } getch(); clrscr(); cleardevice(); setbkcolor(0); printf("Doriti graficul altei functii? 1-DA 0-NU :"); scanf("%d", &l); if (l==1){clrscr();cleardevice(); p=0;}} closegraph(); }

252

Capitolul XIII FUNC II MATEMATICE

Limbajul C con ine mai multe func ii matematice care utilizeaz argumente de tip double i ntorc valori de tip double. Aceste func ii se mpart n urmtoarele categorii: - func ii trigonometrice; - func ii hiperbolice; - func ii exponen iale i logaritmice; - alte tipuri. Toate func iile matematice sunt incluse n fiierul antet "math.h". Acesta mai con ine o serie de macrodefini ii cum ar fi EDOM, ERANGE i HUGE_VAL. Macrodefini iile EDOM i ERANGE se gsesc n fiierul "errno.h" i sunt constante ntregi diferite de zero, utilizate pentru a semnala erorile de domeniu i de plaj ale func iei. HUGE_VAL (aflat tot n "errno.h") este o valoare pozitiv de tip double. Dac un argument al unei func ii matematice nu este n domeniul pentru care a fost definit func ia, atunci func ia ntoarce 0 i n domeniul de eroare, "errno" este modificat la EDOM. Dac o func ie produce un rezultat prea mare pentru a fi reprezentat printr-un double, apare o depire, func ia returnnd HUGE_VAL cu semnul adecvat iar "errno" este modificat la ERANGE. Dac se produce subdepire, func ia ntoarce zero, iar "errno" este modificat la ERANGE n func ie de implementare.

13.1 Func ii trigonometrice


- sin(x) , - cos(x) , - tan(x) , x n radiani x n radiani x n radiani - sinusul lui x; - cosinusul lui x. - tangenta lui x;

Exemplu: Programul urmtor afieaz valorile sinusului, cosinusului i tangentei unghiului a[-1,+1] radiani, din 0.1 n 0.1.
# include <math.h> void main() { double val = -1.0;

253

do { printf("sinusul lui %f este %f\n", val, sin(val)); printf("cosinusul lui %f este %f\n",val, cos(val)); printf("tangenta lui %f este %f\n", val, tan(val)); val += 0.1;} while (val <= 1.0); }

13.2 Func ii trigonometrice inverse


- asin(x) , cu x [-1,1] - arcsinusul lui x; - acos(x) , cu x [-1,1] - arccosinusul lui x; - atan(x) , x R - arctangenta lui x; - atan(y,x) , - returneaza arctg (y/x). Exemplu: Programul urmtor afieaz valorile arcsinusului, arccosinusului i arctangentei unghiului a[-1,+1], din 0.1 n 0.1.
# include <math.h> void main() { double val = -1.0; do { printf("arcsin lui %f este %f\n", val, asin(val)); printf("arccos lui %f este %f\n", val, asin(val)); printf("arctg lui %f este %f\n", val, asin(val)); val += 0.1; } while (val <= 1.0); }

13.3 Func ii hiperbolice


- sinh(x) , x R - cosh(x) , x R - tanh(x) , x R - sinus hipebolic de x; - cosinus hipebolic de x; - tangenta hipebolica de x.

13.4 Func ii exponen iale i logaritmice


- exp(x) , x R - exponentiala lui x. - log(x) , x > 0 - logaritmul natural al lui x; - log10(x) , x>0 - logaritmul zecimal al lui x. Exemplu: printf ("Valoarea lui e este: %f", exp(1.0)); Exemplu: Programul urmtor afieaz valorile logaritmului natural i logaritmului zecimal din 1 n 1 al numerelor de la 1 la 10.
# include <math.h> void main() { double val =1.0; do{printf("%f %f %f\n",val,log(val),log10(val));

254

val ++; } while (val < 11.0);}

- pow(x,y); func ia calculeaza xy. O eroare de domeniu apare dac x = 0 i y = 0 sau dac x < 0 i y nu este ntreg. Exemplu: Programul urmtor afieaza primele 11 puteri ale lui 10.
# include <math.h> void main() { double x =10.0, y = 0.0; do { printf ("%f\n", pow(x,y)); y ++;} while (val < 11.0); }

13.5 Generarea de numere aleatoare


n multe aplica ii este necesar generarea de numere aleatoare. Pentru asemenea cazuri limbajul C dispune de dou func ii, rand i random, care returneaz numere ntregi aleatore. Func ia rand are urmtorul prototip:
int rand(void)

i returneaz un numr ntreg, aleator, cuprins n intervalul de la 0 la RAND_MAX (valoare definit n fiierul antet stdlib.h). Func ia random are prototipul:
int random(int val_maxima)

i returneaz un numr ntreg, aleator, cuprins n intervalul [0, val_maxima]. Pentru generarea de numere aleatoare n virgul mobil se mparte rezultatul func iei random la o valoare ntreag. Urmtorul program exemplific utilizarea acestor func ii: Exemplu:
#include <stdio.h> #include <stdlib.h> void main(void) { int k; printf(Valorile furnizate de functia rand\n); for(k=0;k<100;k++) printf(%d ,rand()); printf(Valorile furnizate de functia random(100)\n); for(k=0;k<100;k++) printf(%d ,random(100)); printf(Valori reale intre 0 si 1\n); for(k=0;k<10;k++) printf(%f ,random(10)/10.0); printf(Valori intregi intre -10 si 10\n); for(k=0;k<10;k++) printf(%d ,10-random(20));}

255

13.6 Alte tipuri de func ii matematice


Nume func ie sqrt(x) ceil(x) floor(x) fabs(x) ldexp(x, n) fmod(x, y) Caracterizarea func iei
- radicalul lui x, sqrt(x)= x - cel mai mic ntreg, mai mare c x, convertit la double (ex. ceil(1.05) va returna valoarea 2). - cel mai mare ntreg, mai mic sau egal cu x, convertit la double (exemplu: floor(1.02) va returna valoarea 1.0, floor(-1.02) va returna valoarea -2.0). - modulul numrului x; - calculeaza x*2n , unde n este de tip int.

- restul n virgul mobila a lui x/y, cu acelai semn ca x. modf(x, double *ip) - mparte pe x n parte ntreag i parte frac ionar, fiecare cu acelai semn c x; memoreaz partea ntreag n "*ip" i ntoarce partea frac ionar. modf(x, double *ip) - ntoarce x ntr-o func ie normalizat n intervalul [1/2, 1] i o putere a lui 2, care se memoreaza n "*exp"; dac x este 0, ambele pr i ale rezultatului sunt 0.

Modul de utilizare al acestor func ii este similar cu al celorlalte func ii matematice descrise n acest capitol.

256

Capitolul XIV ELEMENTE DE PROGRAMARE AVANSAT


14.1 Gestionarea memoriei
Un calculator poate avea trei tipuri de memorie: conven ional, extins i expandat. n programare memoria constituie un factor important ce influen eaz viteza de lucru a programelor. Fiecare tip de memorie are diferite viteze de acces, ceea ce afecteaz performan a programelor. Volumul i tipul de memorie instalat poate fi determinat utiliznd comanda DOS: C:>MEM /CLASSIFY (pentru versiuni ale sistemului de operare DOS mai mari de varianta 5). Sistemul de operare DOS dispune de capacit i de gestionare a memoriei ce pot maximiza performan ele calculatorului.

14.1.1 Memoria conven ional


Primul PC compatibil IBM utiliza de obicei ntre 64Kb i 256Kb memorie RAM (Read Only Memory). Pe atunci aceast memorie era mai mult dect suficient. Astzi, memoria conven ional a unui PC este format din primul 1Mb de RAM. Programele DOS ruleaz, n mod obinuit, cu primii 640Kb de memorie conven ional. PC-ul utilizeaz restul de 384Kb de memorie (numit memorie rezervat sau memorie superioar) pentru memoria video a calculatorului, driverele de dispozitive, alte dispozitive HARD mapate n memorie i BIOS (Basic Input-Output Services servicii intrareieire de baz). Sistemul de operare Windows utilizeaz modelul de memorie virtual pentru a gestiona memoria, ceea ce nseamn c eliberarea memoriei conven ionale nu are semnifica ie sub acest sistem de operare. ns, memoria conven ional este important cnd se ruleaz programe n cadrul unei ferestre DOS sub Windows. Structura memoriei conven ionale a unui calculator personal este urmtoarea:

257

BIOS ROM Memorie rezervat Memorie video COMMAND.COM Memorie pentru programe Intrri CONFIG.SYS Nucleul DOS Zona de comunica ii BIOS Vectori de ntrerupere BIOS

PC-ul mparte memoria n blocuri de 64Kb numite segmente. n mod obinuit, programul utilizeaz un segment de cod (ce con ine instruc iunile programului) i un al doilea segment de memorie pentru date. Dac un program este foarte mare compilatorul va trebui s dispun de mai multe segmente de cod sau de date, sau de ambele. Modelul de memorie definete numrul de segmente pe care le poate folosi pentru fiecare. Modele sunt foarte importante deoarece, dac se utilizeaz un model de memorie necorespunztor, programul poate s nu de in suficient memorie pentru execu ie. Compilatorul va alege un model de memorie suficient de mare pentru a rula programul, ns cu ct memoria utilizat este mai mare cu att viteza de execu ie a programului scade. Din aceast cauz trebuie ales modelul cel mai mic pentru necesit ile programului. Majoritatea compilatoarelor accept urmtoarele modele de memorie: a) tiny combin datele i codul programului ntr-un singur segment de 64Kb (este cel mai mic i mai rapid model de memorie); b) small utilizeaz un segment de memorie pentru cod i un segment pentru date (este cel mai obinuit model de memorie); c) medium utilizeaz un segment de 64Kb pentru date i dou sau mai multe segmente pentru codul programului. n acest caz datele sunt accesate rapid prin utilizarea de adrese near, n schimb ns, apelurile de func ii se fac utiliznd adrese far; d) compact aloc un segment de 64Kb pentru codul programului i dou sau mai multe segmente pentru date (este un model utilizat pentru programe mici ce manipuleaz un numr mare de date);

258

e) large aloc mai multe segmente att pentru date ct i pentru cod i este cel mai lent model de memorie din cele prezentate pn acum. El trebuie utilizat doar ca ultim resurs; f) huge este un model utilizat doar n cazul utilizrii unor matrici mai mari de 64Kb. Pentru a stoca o astfel de matrice programul trebuie s utilizeze cuvntul cheie huge pentru a crea un pointer, astfel:
int huge *matrice_uria

dup care programul trebuie s utilizeze func ia halloc pentru alocarea memoriei i func ia hfree pentru eliberarea acesteia. Exemplul urmtor aloc o matrice de 400000 octe i: Exemplu:
#include <stdio.h> #include <malloc.h> void main(void) { long int k; int huge *matrice_uriasa; if ((matrice_uriasa=(int huge*) halloc(100000L,sizeof (long int)))==NULL) printf(Eroare la alocarea matricii); else{ printf(Completeaza matricea\n); for(k=0;k<100000L;k++) matrice_uriasa[k]=k%32768; for(k=0;k<100000L;k++) printf(%d ,matrice_uriasa[k]); hfree(matrice_uriasa); } }

Pentru selectarea unui anumit model de memorie se include, de regul, o op iune n cadrul liniei de comand a compilatorului. Majoritatea compilatoarelor predefinesc o constant specific pentru a determina modelul curent de memorie. n tabelul urmtor sunt prezentate aceste constante definite de compilatoarele Microsoft C i Borland C:
Model de memorie Small Medium Compact Large Microsoft C M_I86SM M_I86MM M_I86CM M_I86LM Borland C _SMALL_ _MEDIUM_ _COMPACT_ _LARGE_

Programul poate verifica modelul de memorie utilizat folosind urmtoarea secven de instruc iuni:
#ifndef _MEDIUM_ printf(Programul cere modelul de memorie medium\n);

259

exit(1); #endif

Atunci cnd un program trebuie s aloce memorie n mod dinamic se utilizeaz fie func ia malloc pentru a aloca memorie din zona near (din segmentul curent), fie func ia fmalloc pentru a aloca memorie far.

14.1.2 Memoria expandat


n cazul programelor mari o memorie de numai 1Mb este insuficient. Pentru a permite accesul la mai mult de 1Mb de memorie, companiile Lotus, Intel i Microsoft au creat o specifica ie pentru memoria expandat, care combin software i o platform special de memorie expandat pentru a pcli PC-ul n scopul accesrii unor volume mari de memorie. Mai nti n zona de memorie superioar se aloc un bloc de 64Kb dup care acest bloc de memorie este mpr it n patru sec iuni de 16Kb, numite pagini n care se ncarc paginile logice ale programului. De exemplu un program de 128Kb este mpr it n opt pagini de 16Kb fiecare care sunt ncrcate n func ie de necesit ile programului n zona rezervat de 64Kb.

14.1.3 Memoria extins


Calculatoarele cu procesoare peste 386 utilizeaz adresarea pe 32 de bi i ceea ce le d posibilitatea de accesare direct de pn la 4Gb de memorie. Programatorii au numit memoria de peste 1Mb memorie extins. Pentru a accesa memoria extins, trebuie ncrcat un driver de dispozitiv pentru memoria extins, care n DOS este de obicei himem.sys. Pentru a utiliza ns memoria extins este necesar trecerea la modul protejat de lucru al procesorului, mod de lucru n care datele unui program nu pot fi scrise peste datele altui program ce ruleaz simultan cu acesta.

14.1.4 Stiva
Stiva este o regiune de memorie n cadrul creia programele pstreaz temporar datele pe durata execu iei. De exemplu, atunci cnd programele transmit parametri ctre o func ie, C plaseaz aceti parametri n stiv. Cnd func ia i ncheie execu ia acetia sunt scoi din stiv. Stiva este numit astfel deoarece ultimele valori depuse sunt primele extrase. n func ie de modelul de memorie utilizat, spa iul de memorie ocupat de stiv difer. Valoarea minim a stivei este 4Kb. n

260

cazul modelelor compact sau large, C aloc pentru stiv un ntreg segment de 64Kb. Dac un program plaseaz n stiv mai multe informa ii dect poate re ine aceasta, va aprea o eroare de depire a stivei (stack-overflow). Dac programul a dezactivat testarea stivei, datele depuse n stiv pot fi suprapuse peste datele programului. Exemplul urmtor prezint modul de determinare a dimensiunii stivei utiliznd func ia _stklen. Exemplu:
#include <stdio.h> #include <dos.h> void main(void) { printf(Dimensiunea stivei este de %d octeti,_stklen); }

14.2 Servicii DOS i BIOS


Aa cum am men ionat n paragraful anterior, BIOS-ul reprezint serviciile de intrare-ieire de baz. Pe scurt, BIOS este un cip din cadrul calculatorului ce con ine instruc iunile pe care calculatorul le utilizeaz pentru a scrie pe ecran sau la imprimant, pentru a citi caractere de la tastatur sau pentru a citi sau scrie pe disc. Programatorii au au proiectat rutinele BIOS pentru a fi utilizate de programe n limbaj de asamblare, totui, majoritatea compilatoarelor de C dispun de func iide bibliotec ce permit utilizarea acestor servicii fr a avea nevoie de limbaje de asamblare. DOS este un sistem de operare pentru calculatoarele compatibile IBM PC. Sistemul DOS permite rularea programelor i pstreaz informa ia pe disc. n plus, sistemul DOS pune la dispozi ie servicii ce permit programelor s aloce memorie, s acceseze dispozitive, cum ar fi imprimanta, i s gestioneze alte resurse ale sistemului. Biblioteca limbajului C ofer o interfa la multe servicii DOS, prin intermediul func iilor. Mul i programatori confund serviciile DOS cu serviciile BIOS. Tabelul urmtor prezint rela ia dintre componenta HARD a calculatorului, serviciile BIOS, DOS i componenta SOFT.
Programe DOS BIOS HARDWARE Nivel nalt | | Nivelul cel mai jos

261

Aa cum se observ, BIOS este situat imediat deasupra componentei hardware, serviciiile DOS deasupra serviciilor BIOS, iar programele deasupra sistemului DOS. Uneori ns, programele pot evita serviciile DOS i BIOS i pot accesa direct o component hardware (cum este cazul memoriei video). Se recomand ca ori de cte ori poate fi utilizat o func ie de bibliotec C n locul unui serviciu DOS sau BIOS, aceasta s fie utilizat pentru a mri portabilitatea programelor i la calculatoarele ce utilizeaz alte sisteme de operare (WINDOWS, UNIX, etc.). n acest caz, programul nu va mai trebui modificat pentru a putea fi rulat sub WINDOWS sau UNIX. Toate versiunile de WINDOWS vor apela propriile lor servicii de sistem. ns, serviciile de sistem WINDOWS apeleaz pn la urm serviciile BIOS penttru a accesa componentele hardware ale cal;culatorului.

14.2.1 Serviciile BIOS


Prezentm n continuare o serie de servicii BIOS ce pot fi accesate utiliznd func ii de bibliotec ale limbajului C. 1) accesul la imprimant nainte ca un program s scrie ieirea la imprimant utiliznd indicatorul de fiier stdprn se poate face o verificare dac imprimanta este conectat i dac are hrtie utiliznd func ia biosprint din fiierul antet bios.h:
int biosprint(int comanda,int octet,int nr_port)

unde comanda specific una din urmtoarele opera ii: 0 tiprete octetul specificat; 1 ini ializeaz portul imprimantei; 2 citete starea imprimantei. Parametrul octet specific valoarea ASCII a caracterului ce se dorete a fi scris la imprimant iar nr_port specific portul imprimantei care poate fi 0 pentru LPT1, 1 pentru LPT2, .a.m.d. Func ia biosprint returneaz o valoarea nteag pe un octet ai crui bi i au urmtoarea semnifica ie: 0 dispozitiv n pauz; 3 eroare I/O; 4 imprimant selectat; 5 lips hrtie;

262

6 confirmare dispozitiv; 7 dispozitivul nu este ocupat. 2) opera ii intrare/ieire Opera iile intrare/ieire de nivel jos pot fi realizate utiliznd func ia biodisk ce are urmtoarea sintax:
int biodisk(int operatie, int unitate, int head, int track, int sector, int nr_sector, void *buffer)

unde parametrul unitate precizeaz numrul unit ii, care este 0 pentru A, 1 pentru B, i aa mai departe. Parametrii head, track, sector i nr_sector precizeaz sectoarele fizice ale disculuice trebie scris sau citit. Parametru buffer este un pointer la bufferul din care sunt citite sau n care sunt scrise datele. Parametru operatie specific func ia dorit astfel:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Ini ializeaz sistemul de disc Returneaz starea ultimei opera ii pe disc Citete numrul precizat de sectoare Scrie numrul precizat de sectoare Verific numrul precizat de sectoare Formateaz pista specificat Formateaz pista specificat i marcheaz sectoarele defecte Formateaz unitatea ncepnd cu pista specificat Returneaz parametrii unit ii de disc Ini ializeaz unitatea de disc Execut o citire lung 512 octe i de sector plus patru suplimentari Execut o scriere lung 512 octe i de sector plus patru suplimentari Execut o pozi ionare pe disc Ini ializarea alternativ a discului Citete bufferul sectorului Scrie bufferul sectorului Testeaz dac unitatea este pregtit Recalibreaz unitatea Execut diagnosticarea unit ii de RAM Execut diagnosticarea unit ii Execut diagnosticarea intern a controlerului

Dac se execut cu succes, func ia returneaz valoarea 0. Dac apare o eroare, valoarea returnat precizeaz eroarea. 3) servicii de tastatur din BIOS Pentru accesul la serviciile de tastatur din BIOS, C-ul pune la dispozi ie func ia _bios_keybrd ce are urmtoarea sintax:
unsigned _bios_keybrd(unsigned comanda)

263

unde parametrul comanda specific opera ia dorit i poate avea una din urmtoarele valori:
_KEYBRD_READ _KEYBRD_READY

_KEYBRD_SHIFTSTATUS

_NKEYBRD_READ

_NKEYBRD_READY

_NKEYBRD_SHIFTSTATUS

Indic func iei s citeasc un caracter de la tastatur Determin dac este prezent un caracter la bufferul tastaturii. Dac func ia returneaz 0, nseamn c nici o intrare de la tastatur nu este prezent. Dac valoarea returnat este 0xFFFF, utilizatorul a apsat CTRL C Returneaz starea tastelor de control: Bit 7 INS este activat Bit 6 CAPSLOCK este activat Bit 5 NUMLOCK este activat Bit 4 SCRLLOCK este activat Bit 3 ALT este apsat Bit 2 CTRL este apsat Bit 1 SHIFT stnga este apsat Bit 0 SHIFT dreapta este apsat Indic func iei s citeasc un caracter de la tastatur, inclusiv tastele speciale, cum ar fi tastele cu sge i Determin dac este prezent un caracter la bufferul tastaturii. Dac func ia returneaz 0, nseamn c nici o intrare de la tastatur nu este prezent. Dac valoarea returnat este 0xFFFF, utilizatorul a apsat CTRL C Func ia accept inclusiv tastele speciale, cum ar fi tastele cu sge i Returneaz starea tastelor de control, inclusiv a tastelor speciale: Bit 15 SYSREQ este activat Bit 14 CAPSLOCK este activat Bit 13 NUMLOCK este activat Bit 12 SCRLLOCK este activat Bit 11 ALT dreapta este apsat Bit 10 CTRL dreapta este apsat Bit 9 ALT stnga este apsat Bit 8 CTRL stnga este apsat

4) ob inerea listei cu echipamente din BIOS Unele programe necesit determinarea caracteristicilor hardware ale calculatorului. Pentru aceasta se utilizeaz func ia _bios_equiplist care are urmtoarea sintax:

264

unsigned _bios_equiplist(void);

Func ia returneaz o valoare pe 16 bi i a cror valoare are urmtoarea semnifica ie:


15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

15:14 numrul de imprimante paralele instalate (de la 0 la 3); 13 imprimanta serial; 12 adaptorul de jocuri; 11:10:9 numrul de porturi seriale COM (de la 0 la 7); 8 prezen a DMA (Direct Memory Acces); bitul are valoarea 0 dac exist DMA i 1 dac nu exist; 7:6 numrul drieverelor de disc; 5:4 modul video: 00-neutilizat, 01-mod video 40x25 mono, 10-mod video 80x25 color, 11-mod video 80x25 mono; 3:2 dimensiunea memorie RAM: 00-16Kb, 01-32Kb, 10-48Kb, 1164Kb; 1 prezen a coprocesorului matematic; 0 prezen a unit ii de disc flexibile. 5) controlul intrrilor i ieirilor pentru portul serial Pentru a executa opera ii intrare/ieire utiliznd portul serial se utilizeaz func ia bioscom ce are urmtoarea sintax:
unsigned bioscom(int comanda,int port,char octet);

Parametrul comanda specific opera ia dorit i poate avea una din urmtoarele valori:
_COM_INIT _COM_RECEIVE _COM_SEND _COM_STATUS Stabilete valorile pentru comunicare ale portului Primete un octet de la port Trimite un octet la port Returneaz valorile portului

Parametrul port specific portul serial ce se dorete a fi utilizat, unde 0 corespunde lui COM1, 1 lui COM2 i aa mai departe. Parametrul octet specific fie octetul pentru ieire, fie valorile de comunicare dorite. 6) determinarea volumului de memorie conven ional BIOS Pentru a determina memoria conven ional ce poate fi utilizat de ctre un proggram se utilizeaz func ia biosmemory ce are urmtoarea sintax:
int biosmemory(void);

Valoarea returnat de aceast func ie nu cuprinde memoria extins, expandat sau superioar. 7) citirea cronometrului BIOS

265

BIOS are incorporat un ceas intern ce bate de 18.2 ori pe secund. Acest cronometru este util pentru a genera punctul ini ial al unui generator de numere aleatoare. Multe compilatoare de C pun la dispozi ie dou func ii pentru accesul la cronometrul BIOS: biostime i _bios_timeofday. Sintaxa acestor func ii este urmtoarea:
long biostime(int operatie,long timp_nou);

Parametrul opera ie poate lua dou valori: 0 dac se dorete ca func ia s citeasc valoarea curent a cronometrului; 1 pentru a fixa valoarea cronometrului la valoarea timp_nou.
long _bios_timeofday(int operatie,long *batai);

Aceast func ie poate fi, de asemenea, utilizat pentru a citi sau a fixa cronometrul BIOS.

14.2.2 Serviciile DOS


n acest paragraf prezentm o serie de servicii DOS ce pot fi accesate utiliznd func ii de bibliotec ale limbajului C. 1) suspendarea temporar a unui program Execu ia unui program poate fi suspendat temporar utiliznd func ia sleep.h din fiierul antet dos.h:
void sleep(unsigned secunde);

parametrul secunde specificnd numrul de secunde pe care este suspendat programul. 2) utilizarea sunetelor Generarea de sunete ce utilizeaz difuzorul calculatorului se realizeaz utiliznd func iile sound i nosound:
void sound(unsigned frecventa)

genereaz un sunet cu frecven a frecventa;


void sound(unsigned frecventa)

deconecteaz difuzorul. Programul urmtor genereaz un sunet de siren dezactivat la apsarea unei taste: Exemplu:
#include <dos.h> #include <conio.h> void main() { unsigned frecventa; do{ for (frecventa=500;frecventa<=1000;frecventa+=50) { sound(frecventa); delay(50); } for (frecventa=1000;frecventa>=500;frecventa-=50) { sound(frecventa); delay(50); } }

266

while(!kbhit()); nosound(); }

3) ob inerea de informa ii despre erori n DOS n cazul n care un serviciu al sistemului DOS eueaz, programele pot cere informa ii suplimentare despre acea eroare folosind func ia dosexterr:
int dosexterr(struct DOSERROR *info_eroare);

unde structura DOSERROR are urmtoarele cmpuri:


struct DOSERROR{ int de_exterror; //eroare int de_class; //clasa erorii int de_action;//actiune recomandata int de_locus;//sursa erorii };

Dac func ia returneaz valoarea 0, apelul serviciului DOS nu a avut nici o eroare. Clasa erorii descrie categotia erorii, astfel: 01H Resurse depite 02H Eroare temporar 03H Eroare de autorizare 04H Eroare de sistem 05H Eroare hardware 06H Eroare de sistem nedatorat programului curent 07H Eroare de aplica ie 08H Articol nentlnit 09H Format nevalid 0AH Articol blocat 0BH Eroare de suport 0CH Articolul exist 0DH Eroare necunoscut Parametrul de_action indic programului cum s rspund erorii, astfel: Mai nti ncearc din nou, apoi cere interven ia utilizatorului 01H ncearc din nou, cu o ntrziere, apoi cere interven ia 02H
utilizatorului Cere interven ia utilizatorului pentru solu ie 03H Renun i elimin 04H Renun , dar nu elimina 05H Ignor eroarea 06H ncearc din nou dup interven ia utilizatorului 07H Parametrul de_locus specific sursa erorii, astfel:

267

01H Surs necunoscut 02H Eroare de dispozitiv bloc 03H Eroare de re ea 04H Eroare de dispozitiv serial 05H Eroare de memorie 4) citirea valorilor registrului segment Codul programului, datele i stiva sunt controlate de compilator utiliznd patru registre de segment: CS, DS, ES, SS. n unele cazuri este necesar s se cunoasc valoarea acestor registre. Pentru astfel de cazuri se utillizeaz func ia segread:
void segread(struct SREGS *segs);

Structura SREGS are urmtoarele cmpuri:


struct SREGS { unsigned int es; unsigned int cs; unsigned int ss; unsigned int ds; }

5) accesul la valorile de port Pentrul controlul hardware de nivel inferior, compilatoarele de C pun la dispozi ie urmtoarele func ii: - int inport (int adresa_port); - citete un cuvnt de la portul specificat de parametrul adresa_port; - int inportb (int adresa_port); - citete un octet de la portul specificat de parametrul adresa_port; - int outport (int adresa_port); - scrie un cuvnt de la portul specificat de parametrul adresa_port; - int outportb (int adresa_port); - scrie un octet de la portul specificat de parametrul adresa_port; 6) suspendarea unui program Pentru suspendarea unui program pe un anumit interval de timp se poate utiliza func ia delay, similar func iei sleep. Func ia delay are ns ca parametru o constant exprimat n milisecunde:
void delay(unsigned milisecunde);

7) apelarea unei comenzi interne DOS Pentru apelarea unei comenzi DOS sau a unui fiier pentru comenzi se utilizeaz func ia system:
int system(const char *comanda);

Parametrul comanda este un ir de caracter care con ine numele comenzii DOS sau a fiierului de comenzi. Dac func ia reuete s execute comanda, se returneaz valoarea 0, altfel returneaz -1.

268

Programul urmtor prezint utilizarea func iei system. Exemplu:


#include <stdlib.h> #include <stdio.h> void main(void) { if(system("DIR")) printf("EROARE!\n"); }

8) lucrul cu vectori de ntrerupere Un vector de ntrerupere este o adres de segment i de deplasament a codului care trateaz o anumit ntrerupere. Determinarea vectorului de ntrerupere se realizeaz utiliznd func ia _dos_getvect n modul urmtor:
void interrupt(* _dos_getvect(unsigned nr_intr))();

Parametrul nr_intr specific numrul ntreruperii dorite ce poate avea valori de la 0 la 255. Programul urmtor va afia vectorii pentru toate ntreruperile calculatorului: Exemplu:
#include <stdio.h> #include <dos.h> void main(void) { int k; for(k=0;k<=255;k++) printf(Intrerupere: %x Vector %lx\n,k, _dos_getvect(k)); }

Dac se dorete crearea unui program de tratare a unei ntreruperi, vectorul de ntrerupere trebuie atribuit acestui program. Aceast atribuire se realizeaz cu ajutorul func iei _dos_setvect:
void _dos_setvect(unsigned nr_intr, void interrupt(* handler)());

Parametrul nr_intr specific ntreruperea al crui vector trebuie modificat. Pentru activarea i dezactivarea ntreruperilor se utilizeaz func iile:
void _disable(void); void _enable(void);

Dac se dorete reactivarea ntreruperii originare se utilizeaz func ia _chain_interrupt:


void chain_interrupt(void(interrupt far *handler)());

Generarea unei ntreruperi se realizeaz folosind func ia geninterrupt:


void geninterrupt(int intrerupere);

unde parametrul intrerupere specific ntreruperea generat.

269

14.3 Bibliotecile C
Dac se examineaz fiierele ce nso esc un compilator C, se remarc multe fiiere cu extensia LIB. Aceste fiiere con in biblioteci obiect. Atunci cnd este compilat i link-editat un program, editorul de legturi examineaz fiierele LIB pentru a rezolva referin ele la func ii. Cnd sunt create func ii utile ce sunt necesare i n alte programe, se pot construi biblioteci n care aceste func ii s fie pstrate.

14.3.1 Reutilizarea unui cod obiect


n cazul crerii unei func ii utile care se dorete reutilizat, se poate compila fiierul ce con ine func ia respectiv pentru a crea codul obiect (de exemplu din fiierul func ie.c prin compilare se ob ine fiierul obiect func ie.obj). Func ia definit n acest fiier obiect poate fi reutilizat n alt program utiliznd urmtoarea instruc iune: C:\>bc fisier_nou.c func ie.obj Totui, acest mod de a reutiliza codul unor func ii este destul de dificil de utilizat n cazul n care se dorete reutilizarea unui numr mare de func ii aflate n fiiere obiect separate.

14.3.2 Lucrul cu fiiere bibliotec


Opera iile acceptate de fiierele bibliotec sunt urmtoarele: crearea unei biblioteci; adugarea unuia sau mai multor fiiere obiect la bibliotec; nlocuirea unui fiier obiect cu altul; tergerea unuia sau mai multor fiiere obiect din bibliotec; listarea rutinelor pe care le con ine biblioteca. n func ie de compilator, numele programului de bibliotec i op iunile liniei de comand pe care programul le accept vor diferi. n continuare prezentm opera iile ce pot fi realizate cu func iile bibliotec utiliznd programul TLIB al compilatorului Borland C. Presupunem c n urma compilrii am creat fiierul obiect func ie.obj ce con ine o serie de func ii pe care dorim s le pstrm ntr-o bibliotec. Crearea unei bilioteci biblioteca.lib care s con in acest fiier obiect se realizeaz cu urmtoarea linie de comand: C:\>tlib biblioteca.lib + functie.obj

Dup ce fiierul bibliotec a fost creat, func iile acestuia sunt disponibile pentru compilarea i legarea noilor programe.

270

Func ia de biliotec TLIB a compilatorului Borland C are urmtoarea sintax:


tlib cale comand, fiier

unde: -

cale este un ir de caractere care specific calea pn la bilioteca asupra creia se efectueaz opera ia; comand este format dintr-un simbol i numele unui fiier obiect. Simbol poate fi unul din caracterele: + (adaug un modul la bibliotec), - (elimin un modul din bibliotec), * (extrage un modul din bibliotec ntr-un fiier cu acelai nume, fr al elimina), -+ (nlocuiete un modul din bibliotec), -* (extrage i elimin un modul din bibliotec); fisier reprezint numele fiierul n care se scrie ieirea opera iei efectuate asupra bibliotecii.

14.3 Fiierele antet


Fiecare program folosete una sau mai multe instruc iuni #include pentru a cere compilatorului de C s foloseasc instruc iunile incluse ntr-un fiier antet. Cnd compilatorul ntlnete o instruc iune #include n program, el compileaz codul din fiierul antet ca i cum ar fi scris n fiierul surs. Fiierele antet con in defini ii frecvent utilizate i furnizeaz compilatorului informa ii referitoare la func iile sale. Dac la compilarea programului se afieaz un mesaj de eroare, avertiznd c nu se poate deschide un anumit fiier antet, trebuie verificat subdirectorul care con ine fiierele antet, pentru a vedea dac acel fiier exist sau nu. Dac se gsete fiierul respectiv, n linia de comand din sistemul de operare DOS trebuie scris urmtoarea instruc iune:
C:\>SET INCLUDE=C:\BORLANDC\INCLUDE

271

BIBLIOGRAFIE
1. 2. 3. 4. 5. 6. 7. 8. 9. Plum T., Learning to program in C, Prentice Hall, 1983 Auslander D.,Tham C., Real-time software for control: program examples in C, Prentice Hall, 1990. Schild H., Using Turbo C, Borland, Osborne / McGraw Hill, 1988. Holzner S., Borland C++ Programming, Brady Books, New York, 1992. Somnea D., Turturea D., Introducere n C++, Programarea orientat pe obiecte, Ed. Tehnic, Bucureti, 1993. Marian Gh., Bdic C., Pdeanu L., Limbajul PASCAL, Indrumar de laborator, Reprografia Universit ii din Craiova, 1993. Negrescu L., Introducere n limbajul MicroInformatica, Cluj Napoca, 1993. C, Editura

Petrovici V., Goicea F., Programarea n limbajul C, Editura Tehnic, Bucureti, 1993. Marian Gh., Muatescu C., Lacu M., Iordache t., Limbajul C, Editura ROM TPT, Craiova, 1999.

10. Mocanu M., Ghid de programare n limbajele C/C++, Editura SITECH, Craiova, 2001. 11. Zaharia, M.D., Structuri de date i algoritmi. Exemple n limbajele C i C++, Ed. Albastr, Cluj Napoca, 2002. 12. Kernighan, B.W., Ritchie, D.M., The C programming languages, Englewood. Cliffs, N.J. Prentice-Hall, 1978. 13. Bulac, C., Ini iere n Turbo C++ i Borland C, Editura Teora, Bucureti, 1995.

272

You might also like