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 fişiere .......................................................17 1.3.3. Construirea fişierului 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, scăderea ş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. Enumerări ................................................................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 (fişierul 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. Enumerări .....................................................................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. Reîntoarcerea 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 având ca argumente tablouri .............163 8.7. Argumentele argc şi argv ale func iei main() ...............166 8.8. Func ii care returnează valori neîntregi ........................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 INTRĂRI/IEŞIRI ........................................................................... 194

10.1. Func ii de intrare şi ieşire - stdio.h ...............................194 10.2. Opera ii cu fişiere .......................................................197 10.3. Nivelul inferior de prelucrare a fişierelor ...................199 10.3.1. Deschiderea unui fişier........................................200 10.3.2. Scrierea într-un fişier...........................................204 10.3.3. Citirea dintr-un fişier...........................................206 10.3.4. Închiderea unui fişier...........................................208 10.3.5. Pozi ionarea într-un fişier....................................208 10.3.6 Ştergerea unui fişier .............................................210 10.3.7. Exemple de utilizare a func iilor de intrare/ieşire de nivel inferior .......................................................211 10.4. Nivelul superior de prelucrare a fişierelor ..................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. Ieşiri cu format ....................................................223 10.4.8. Intrări 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

.270 14...3 Func ii hiperbolice ..1 Gestionarea memoriei ...2 Lucrul cu fişiere bibliotecă..........................5.254 13.........260 14..................3....... 272 VI ...4....................1 Memoria conven ională..............2.............................244 12.......2 Memoria expandată ..247 Capitolul XIII FUNC II MATEMATICE......1 Func ii trigonometrice .................................................................................................................................2 Serviciile DOS...................................246 12.3...........2 Servicii DOS şi BIOS ...........................2 Func ii trigonometrice inverse.........4 Func ii exponen iale şi logaritmice...270 14...............244 12..262 14.......................5..........................................11.....2....................261 14...236 Capitolul XII UTILIZAREA ECRANULUI ÎN MOD GRAFIC......271 BIBLIOGRAFIE .6..266 14.............260 14....................260 14.....................1 Reutilizarea unui cod obiect........................2........ Gestiunea culorilor......................................... Gestiunea imaginilor............. 253 13........ Setarea ecranului........1... Ini ializarea modului grafic..........................1...............................257 14..............235 11........ Desenarea şi colorarea figurilor geometrice .........................................................................1 Serviciile BIOS ..........257 14.5 Generarea de numere aleatoare............................................ Utilizarea textelor în mod grafic........................................ 240 12...................255 13...............254 13......................... Func ii pentru gestiunea textelor ............................3.................4 Stiva.......................................................240 12................................3 Bibliotecile C .................6 Alte tipuri de func ii matematice ........................................254 13..................................................270 14...........................................256 Capitolul XIV ELEMENTE DE PROGRAMARE AVANSATĂ ....1..............3 Fişierele antet..........242 12........................ 257 14.................1......................................... Setarea culorilor....6........1............253 13.......................................3 Memoria extinsă........

COBOL.Capitolul I INTRODUCERE ÎN ARHITECURA SISTEMELOR DE CALCUL 1. altele cu răspândire industrială masivă – ca de pildă FORTRAN şi COBOL – limbajul C a pătruns mai lent. 1 . Importan a limbajului C Creat în anul 1972 de programatorii de sistem Dennis M. jocuri cu facilită i grafice. ALGOL-60 sau PASCAL. Eficient. unele cu răspândire mai mult ”academică” – fiind folosite pentru a prezenta conceptele de bază sau conceptele avansate de programare – ca de pildă ALGOL-60 sau PASCAL. de la editoare de texte. Kernighan de la Bell Laboratories cu scopul de a asigura implementarea portabilă a sistemului de operare UNIX. până la programe de sistem. în momentul de fa ă. C-ul permite transferul programelor între calculatoare cu diferite procesoare şi în acelaşi timp facilitează utilizarea caracteristicilor specifice ale maşinilor particulare. LISP.modularizarea programelor – ce dă posibilitatea unui singur programator să stăpânească relativ uşor programe de zeci de mii de linii de sursă. Ritchie şi Brian W. programele scrise în C fiind considerate cele mai portabile. constituind unul dintre cele mai puternice instrumente de programare. economic şi portabil. Realitatea arată clar că. C-ul este astăzi unul din cele mai cunoscute şi puternice limbaje de programare. Elementele principale care au contribuit la succesul C-ului sunt următoarele: . dar foarte sigur. Dacă evolu ia limbajelor de programare a adus în prim plan nume ca FORTRAN. C-ul este o alegere bună pentru realizarea oricărui tip de programe.1. pia a producătorilor de programe este dominată net de C şi de variantele evoluate ale acestuia. programe de gestiune şi pentru calcule ştiin ifice. Adesea referit ca limbaj portabil.

În principiu. 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. limbajul Avalon/C++ destinat calculului distribuit. De exemplu. şi nu în ultimul rând binecunoscutul de acum limbaj Java. limbajul O ce încearcă să îmbine facilită ile de nivel înalt cu cele ale programării de sistem. Limbajul C++ a fost implementat pe microcalculatoarele compatibile IBM PC în mai multe variante. lucru deosebit de important pentru sistemele de gestiune a bazelor de date. Uneori s-au făcut mai multe extensii ale aceluiaşi limbaj. limbajul C++ are ca extensii limbajul E ce permite crearea şi gestiunea obiectelor persistente. La ora actuală.capacitatea de programare atât la nivel înalt cât şi la nivel scăzut – ceea ce dă posibilitatea utilizatorului de a programa fie fără a ”sim i” sistemul de operare şi maşina de calcul. Cele mai importante implementări ale limbajelor C++ pe aceste calculatoare sunt cele realizate de firmele Microsoft şi Borland.facilită ile de reprezentare şi prelucrare a datelor – materializate printr-un număr mare de operatori şi func ii de bibliotecă ce fac programarea mult mai uşoară. . specializat în aplica ii Internet. . ceea ce a condus la apari ia de limbaje care să permită utilizarea ei în scrierea programelor. se bucură de o portabilitate mare şi este implementat pe o gamă largă de calculatoare începând cu microcalculatoare şi până la cele mai mari supercalculatoare. 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.portabilitatea programelor – ce permite utilizarea programelor scrise în C pe o mare varietate de calculatoare şi sisteme de operare. majoritatea limbajelor de programare moderne au fost dezvoltate în direc ia programării orientate pe obiecte. multe limbaje au fost extinse astfel încât ele să admită conceptele mai importante ale programării orientate pe obiecte. Conceptele programării orientate pe obiecte au influen at în mare măsură dezvoltarea limbajelor de programare în ultimul deceniu. ele simplifică interac iunea dintre programe 2 . Interfe ele utilizator au atins o mare dezvoltare datorită facilită ilor oferite de componentele hardware ale diferitelor calculatoare. Limbajul C++. Prin anii ’80 interesul pentru programarea orientată pe obiecte a crescut. De obicei. ca şi limbajul C..

şi utilizatorii acestora. prin modul de structurare a aplica iei şi. folosind mouse-ul. etc. Toate acestea conduc la interfe e simple şi vizuale. iar aplica ia propriu-zisă se programează într-un limbaj de tip Basic. prin filozofia de rezolvare a 3 . implicit. Firma Borland comercializează o bibliotecă de componente standardizate care pot fi utilizate folosind limbajul C++. date de intrare sau rezultate pot fi exprimate simplu şi natural utilizând diferite standarde care con in ferestre. butoane. O astfel de interfa ă utilizator se numeşte interfa ă utilizator grafică. interfe ele utilizator gestionează ecranul în mod grafic. Microsoft C++. Importan a aplicării conceptului de reutilizare a codului rezultă din faptul că interfe ele utilizator adesea ocupă 40% din codul total al aplica iei. Una din cele mai populare interfe e utilizator grafice pentru calculatoarele IBM PC este produsul Windows oferit de firma Microsoft. accesibile unui segment foarte larg de utilizatori. aceasta mai ales datorită posibilită ii de a utiliza componente standardizate aflate în biblioteci specifice. diferite comenzi. Astfel. Pascal pentru Windows. Visual C şi Visual C++. prin stilul de programare. în condi iile folosirii limbajelor de programare orientate pe obiecte. prin conceptele pe care se bazează. Cu ajutorul mouse-ului toate acestea pot fi accesate extrem de rapid şi uşor fără a mai fi nevoie să cunoşti şi să memorezi o serie întreagă comenzi ale sistemului de operare sau ale limbajului de programare. bare de meniuri. cutii de dialoguri. Windows este un mediu de programare ce amplifică facilită ile oferite de sistemul de operare MS-DOS. bibliotecă cunoscută sub numele Turbo Vision. Aplica iile Windows se pot dezvolta folosind diferite medii de dezvoltare ca: Turbo C++ pentru Windows. Implementarea interfe elor este mult simplificată prin utilizarea limbajelor orientate spre obiecte. Microsoft Visual Basic. 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. C sau C++. a unei aplica ii. De obicei. în prezent. această medie a ajuns la peste 25 de mii de instruc iuni. a programului. Componentele Visual permit specificarea în mod grafic a interfe ei utilizator. 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.

Calculatorul poate executa o listă preînregistrată de instruc iuni.1. CD-ROM. FDD. datorită gradului de accesibilitate şi pre ului relativ scăzut. 1. Răspunde la un set specific de instruc iuni într-o manieră bine definită. Două dintre principalele caracteristici ale unui calculator sunt: 1. Echipamente de ieşire Echipamente de stocare date (HDD. 2.2 Arhitectura de bază a unui calculator Calculatoarele de tip PC (calculatoare personale) reprezintă cele mai răspândite şi mai utilizate dintre calculatoare.) UPC Unitatea de procesare ş i control Echipamente de intrare Fig.problemelor folosind limbajul. o necesitate vitală. Calculatorul este o maşină programabilă. Din aceste puncte de vedere. Partea de circuite electrice şi electronice precum şi conexiunile fizice dintre ele se numeşte hardware.1 Configura ia standard pentru utilizator 4 . modul general de concep ie. C-ul nu poate lipsi din cultura unui programator. şi mai mult. etc. numită program. de alcătuire şi func ionare este acelaşi. Indiferent de tipul calculatorului. iar pentru un profesionist Cul este. Totalitatea programelor precum şi datele aferente acestor programe poartă denumirea de software. Calculatoarele moderne sunt electronice şi numerice. acesta fiind piatra de temelie pentru în elegerea şi utilizarea eficientă a limbajelor de nivel înalt orientate pe obiecte şi Visual.

Aceste dispozitive reprezintă calea uzuală de introducere a datelor şi instruc iunilor care gestionează func ionarea unui calculator. Cele mai uzuale dispozitive de stocare externă sunt HDD (hard disk drives). unitatea de procesare şi control (UPC) : Este partea principală a unui calculator deoarece este componenta care execută instruc iunile. cel pu in temporar. Ecranul monitorului sau imprimanta sunt astfel de dispozitive uzuale. dispozitive de ieşire: Reprezintă modalitatea prin care calculatorul transmite utilizatorului uman rezultatele execu iei programelor. Magistrala reprezintă o colec ie de trasee electrice care leagă microprocesorul de dispozitivele de intrare/ieşire şi de dispozitivele interne/externe de stocare a datelor. În plus 5 . Calculatoarele pot fi în general clasificate după dimensiuni sau putere de calcul. În plus fa ă de aceste componente orice calculator este prevăzut cu o magistrală (bus) prin care se gestionează modalitatea de transmitere a datelor între componentele de bază ale calculatorului. bazat pe un microprocesor. magistrala de adrese şi magistrala de comandă şi control.Partea hardware a unui calculator este formată din totalitatea componentelor sale fizice. Putem distinge magistrala de date. Î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 figura 1. dispozitive de intrare : În mod uzual sunt reprezentate de tastatură (keyboard) şi de mouse.2 este prezentată interac iunea dintre componentele HARD principale ale unui calculator. dispozitive de stocare externe: Permit calculatoarelor să stocheze permanent programe şi mari cantită i de date. Toate calculatoarele de uz general necesită următoarele componente hardware: memorie: Permite calculatorului să stocheze. Nu se poate face însă la ora actuală o distinc ie netă între următoarele categorii de calculatoare: PC (Personal Computer): Un calculator de dimensiuni mici. date şi programe. monoutilizator (single-user). FDD (floppy disk drive) şi CD-ROM (Compact Disk-Read Only Memory) sau CD-R/W (Compact Disk-Read/Write).

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 (adunări. minicalculator (minicomputer): Un calculator multiutilizator (multi-user) capabil să lucreze simultan cu zeci sau chiar sute de utilizatori. împăr iri. supercomputer: Un computer extrem de rapid care poate executa sute de milioane de opera ii într-o secundă. Prima componentă realizează efectiv 6 .2. 1. înmul iri.2 Arhitectura minimală a unui sistem de calcul sta ii de lucru (workstation): Un calculator monoutilizator de mare putere. mouse. comparări de numere).acesta este dotat standard cu tastatură. Din punct de vedere fizic. monitor şi dispozitive periferice de stocare a datelor. mainframe: Un calculator multiutilizator capabil să lucreze simultan cu sute sau chiar mii de utilizatori. Memoria secundară Echipament de intrare UNITATEA CENTRALĂ Echipament de ieşire Memoria principală Fig.1 Microprocesorul Microprocesorul este cea mai importantă şi cea mai scumpă componentă a unui calculator de performan ele acesteia depinzând în mare măsură rezultatele întregului sistem. scăderi. 1. El este compus din două păr i importante: unitatea de execu ie (EU – Execution Unit) şi unitatea de interfa ă a magistralei de date (BIU – Bus Interface Unit). Aceasta este asemănător unui PC dar are un microprocesor mai puternic şi un monitor de înaltă calitate (rezolu ie mai mare).

cipul 4004 dezvolta mai multă putere de calcul decât ENIAC. Designerul Ted Hoff a propus o solu ie programabilă. iar cea de-a doua are func ia de transfer a datelor de la şi înspre microprocesor. Un copil care lucrează la o maşina ce incorporează un procesor Pentium Pro beneficiază de mult mai multă putere de calcul decât dispunea 7 . etc. creând 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. dând startul unui adevărat galop de inova ii tehnologice. Această inven ie a contribuit la revolu ionarea domeniilor de aplica ii ale computerelor. lansat la sfârşitul anului 1971.opera iile. transmite altor cipuri din sistem semnale de control. realizate de alte companii cum ar fi: AMD. când IBM a inclus un procesor Intel în arhitectura primului PC. Astăzi PC-urile sunt pretutindeni în jurul nostru. acum dispărută. În 1971 firma Intel a fost abordata de o companie Japoneza. procesorul 4004 putea dezvolta 60. Cipul Intel 4004 a fost primul procesor comercial. Calculatoarele IBM PC folosesc procesoare INTEL sau compatibile. La un pre de circa 200$ şi înglobând 2300 de tranzistori. numite registre. Numele microprocesorului este folosit la identificarea calculatorului. decodifică instruc iuni speciale. Se folosesc frecvent expresii de tipul calculator 386. Au urmat la scurt timp chipurile 4040 si 8008 dar lor le lipseau multe din caracteristicile microprocesoarelor aşa cum le ştim noi azi. Fa ă de cele 18. Toate aceste opera ii sunt executate cu ajutorul unor zone de memorie ale microprocesorului. Orice microprocesor are un set finit de instruc iuni pe care le recunoaşte şi pe care le poate executa. calculator 486.000 de opera ii pe secundă.000 de tuburi cu vacuum ce ocupau 900 metri cubi. calculator Pentium II. Doi dintre proiectan ii de la Intel au părăsit firma. Curând după aceea au apărut procesoarele Motorola 6800 şi 6502 de la MOS Technology. primul calculator electronic. şi astfel s-a născut circuitul Intel 4004. pentru a construi un circuit dedicat pentru un nou calculator. Microprocesorul reprezintă de fapt unitatea centrală a unui calculator şi îndeplineşte o serie de activită i specifice cum ar fi: execută opera ii aritmetice şi logice. de uz general. În 1974 Intel a prezentat pentru prima oară circuitul Intel 8080 care a fost folosit in sistemele Altair şi IMSAI. CYRIX. NexGen. Următorul pas a fost în 1980. cu 25 de ani în urma.

2. Gordon Moore. Se observă că rolul său principal este de a prelucra şi transmite informa iile şi rezultatele şi deci capacitatea sa de memorare este mică neputând stoca programe. M (mega). Aceasta evolu ie exponen ială a determinat o continuă creştere a performan elor PC-urilor şi o scădere a costului procesului de calcul. miliarde. Într-un număr aniversar al publica iei Communications of the ACM. 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. De aceea. 8 . aşa cum suntem obişnui i în mod normal să lucrăm.2 Memoria Microprocesorul are capacitatea de a memora date care urmează a fi prelucrate. Nu se întrevede nici o dificultate care să frâneze această rată de dezvoltare". şi multiplii unui byte vor fi puteri ale lui 2. un sistem desktop ce utilizează un cip Pentium Pro este evaluat la circa 7$ pe MIPS. Pentru citirea informa iilor nu se folosesc bi i în mod individual ci aceştia sunt grupa i într-o succesiune. Deoarece reprezentarea numerelor în calculator se face în baza 2 şi nu în baza 10.guvernul SUA în perioada lansării primelor echipaje umane către Lună. Astfel o succesiune de 8 bi i formează un octet (sau un byte) aceasta reprezentând unitatea de măsură a capacită ii de memorie. astfel: 1 KB=210B=1024 B 1 MB=210KB=1 048 576 B 1 GB=210MB=230 B Abrevierile K (kilo). de la apari ia primului prototip 4004. G (giga) se scriu de obicei cu litere mari şi reprezintă mii. astăzi. respectiv. era optimist în privin a evolu iei PC-urilor şi a microprocesoarelor: "complexitatea microprocesoarelor. 1. care se măsoară prin numărul de tranzistori pe cip. milioane şi. un calculator necesită şi o memorie care să găzduiască date şi programe. co-fondator al companiei Intel. cât şi rezultatele intermediare. Pe când în 1991 un PC bazat pe procesorul Intel 486 costa aproape 225$ pentru o performan ă de un milion de instruc iuni pe secundă (MIPS). s-a dublat aproape constant la fiecare 18 luni. 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.

Primele discuri apărute pentru PC-uri. Rolul acestora îl joacă discurile şi ele pot fi asemănate cu căr ile dintr-o bibliotecă pe care le putem consulta ori de câte ori avem nevoie de anumite informa ii. De exemplu. Acesta poate fi asemănat cu o dischetă de mare capacitate. În memoria RAM informa ia este stocată temporar. programele de aplica ii curente şi datele asociate acestor aplica ii sunt încărcate în memoria RAM înainte de a fi prelucrate de către microprocesor.5 inch cu capacitatea de 1. Circuitele de tip RAM (Random Acces Memory ) sunt memorii la care utilizatorul are acces şi al căror con inut se şterge la deconectarea calculatorului. Deoarece capacitatea de memorie a unui calculator nu poate fi atât de mare încât să poată păstra toate programele pe vrem să le executăm. Memoria internă este memoria ce poate fi accesată în mod direct de către microprocesor şi în care sunt încărcate programele înainte de a fi executate de către microprocesor. Înscrierea con inutului acestor memorii se face de către fabricant. care să fie solicitate la nevoie. dă semne că lucrurile nu se vor opri aici. din ce în ce mai rapidă. a apărut necesitatea existen ei unor memorii externe. Astăzi mai există doar dischete cu diametrul de 3. Principala componentă a unui calculator utilizată pentru memorarea programelor o reprezintă hard discul. numite şi dischete. Dischetele folosesc metode magnetice de memorare a informa iei motiv pentru care ele se mai numesc şi suporturi magnetice de informa ie. integrată într-o unitate 9 . floppy disk-uri sau discuri flexibile. Aceste memorii pot fi doar citite (con inutul lor nu poate fi modificat). iar opera iunea de înscriere cu programe se mai numeşte „arderea memoriilor”. Circuitele de tip ROM (Read Only Memory) au memorate programele care controlează ini ial calculatorul (sistemul de operare). Memoria principală este formată din două tipuri de circuite: cip-uri ROM şi cip-uri RAM. Existen a acestora pare a fi pusă însă în pericol de apari ia CD-urilor reinscriptibile a căror capacitate de memorare depăşeşte 700 MB iar evolu ia tehnologică.44 MB.Memoria unui calculator are două componente: memoria principală (internă) şi memoria secundară (externă). permiteau stocarea a maximum 160 KB de informa ie. Dacă primele procesoare puteau accesa doar 1 MB de memorie astăzi un procesor Pentium poate accesa peste 256 MB.

încapsulată. Din punct de vedere al dispunerii tastelor. Ele pot fi grupate în echipamente de intrare – cele prin care calculatorul primeşte informa ii sau comenzi (tastatură. În continuare sunt prezentate câteva caracteristici ale fiecărui echipament.3 Echipamentele periferice Comunicarea om-maşină se realizează cu ajutorul echipamentelor periferice prin intermediul cărora utilizatorul poate programa sau da anumite comenzi calculatorului sau poate vizualiza rezultatele ob inute de către anumite programe. ele să fie îmbogă ite prin dublarea tastelor existente sau adăugarea altora noi. . monitorul şi imprimanta. .2.taste cu scopuri speciale. ajungându-se la 101/102 taste. imprimantă). iar performan ele şi capacită ile au crescut. ulterior. tastatura se aseamănă destul de mult cu cea a unei maşini de scris dar are şi păr i care o individualizează. pentru ca.taste func ionale. Tastatura – este principalul dispozitiv de intrare al calculatorului prin intermediul căruia se transmit comenzi către unitatea centrală. Din punct de vedere al func ionalită ii lor ele pot fi împăr ite în patru categorii: . pu ine PC-uri prezentau hard discuri. dar cum pre urile acestora au scăzut considerabil. În clipa de fa ă. Cuplarea la calculator a tastaturii se face prin intermediul unui cablu de conectare. Tastele alfanumerice con in literele.şi echipamente de ieşire – cele prin care calculatorul transmite informa ii în exterior (monitor. Principalele echipamente periferice ale unui calculator sunt următoarele: tastatura. Tastele cu scopuri speciale sunt aşezate în acelaşi bloc cu tastele alfanumerice şi determină efectuarea anumitor ac iuni fără 10 . cifrele şi semnele de punctua ie şi ocupă partea centrală a tastaturii.taste alfanumerice. 1. . Ini ial. Ac ionarea unei astfel de taste determină apari ia caracterului corespunzător pe ecranul calculatorului. mouse. scanner) . mouse-ul. Primele tastaturi au avut 83/84 de taste. scanner-ul.taste direc ionale şi numerice. capacitatea de memorare a unui hard disc a depăşit valoarea de 40 de GB. în prezent toate calculatoarele prezintă acest dispozitiv.

micşorată. putându-se realiza chiar o stocare a lor sub formă de arhivă. etc. foarte multe aplica ii (în special aplica iile grafice) nu mai pot fi concepute fără mouse. colorată. Astfel se pot manevra imagini foto. Totuşi. Mouse-ul – este tot un echipament de intrare mai uşor de manevrat decât tastatura dar care poate efectua mai pu ine opera ii. nemaifiind necesar ca aceştia să memoreze numărul relativ mare de comenzi corespunzător fiecărui produs. Mişcarea maouse-ului pe o suprafa ă plană este corelată cu deplasarea pe ecran a unui cursor cu o formă deosebită: cruciuli ă. WINDOWS este un sistem de operare creat special pentru lucrul cu mouse-ul. De altfel. imaginea poate fi prelucrată. mutată. uşor manevrabil.. După captare. care nu se pot ob ine prin metode tradi ionale. Cu un software de recunoaştere optică a caracterelor datele sau documentele tipărite pe coli de hârtie pot fi transformate în fişiere. având dedesubt o bilă pozi ionabilă. aflate deasupra lor. Tastele func ionale sunt un grup de 12 taste situate în partea de sus a tastaturii având pe ele litera F urmată de un număr între 1 şi 12. Un mouse are aspectul unei bucă i de săpun. în vederea unei prelucrări ulterioare. umbrită. Folosirea mouse-ului uşurează mult munca utilizatorilor. în caz contrar fiind comutat pe semnifica ia direc ională. Tastele de mişcare se află situate în partea dreaptă a tastaturii şi ele func ionează în două moduri.înscrierea de caractere pe ecran. săgeată. rotită. fotografii. se pot crea efecte grafice speciale. texte etc. Ac ionarea acestor taste determină efectuarea unor opera ii specifice de la program la program. Acum există mouse-uri cu 5 butoane şi 2 roti e ce îndeplinesc o serie de func ii corespunzătoare unor taste speciale. Declanşarea unei ac iuni se face prin pozi ionarea cursorului în zona corespunzătoare şi apăsarea unuia dintre butoanele aflate pe partea posterioară. Ini ial un mouse avea două sau trei butoane. prin intermediul unui software adecvat. care pot fi comutate prin ac ionarea altei taste. Utilitatea mouse-ului este şi mai evidentă în cazul aplica iilor grafice. 11 . mărită. suprapusă cu altă imagine etc. Scanner-ul – reprezintă dispozitive care se cuplează la un PC şi cu care. cu sensibilitate şi viteză reglabile. pe care scrie NumLock. Dacă ledul corespunzător acestei taste este aprins. se pot capta imagini. modul de lucru este numeric. ca în situa ia în care se foloseşte numai tastatura.

Cea mai importantă îmbunătă ire adusă de standardul VGA a fost rezolu ia superioară a caracterelor în modul text. precum şi posibilitatea de a afişa 256 de culori la un moment dat. un monitor VGA. în mod video. în cazul de fa ă puncte – pixeli – care pot fi afişate pe ecran. Dacă semnalele digitale prezintă niveluri care indică prezen a sau absen a unui bit. Adaptorul video realizează o rezolu ie orizontală şi una verticală. putând fi considerată un fel de maşină de scris automată. denumit uneori şi display. CGA şi EGA folosesc monitoare digitale.Reprezintă un dispozitiv care poate fi ataşat unui calculator. Adaptorul video reprezintă dispozitivul care realizează legătura (interfa a) cu calculatorul şi se află în interiorul acestuia. Până în prezent au fost realizate un număr destul de mare de tipuri de imprimante pentru PC-uri. De exemplu. iar modurile video VGA reprezintă un superset al standardelor video anterioare. Fiecare dintre ele prezintă avantaje şi dezavantaje. fiind încadrat în categoria echipamentelor periferice de ieşire. permite vizualizarea datelor introduse de la tastatură sau rezultate în urma execu iei unor comenzi sau programe. Monitorul.Sistemul video este format din două păr i: un adaptor (placă) video şi un monitor sau display. Rezolu ia reprezintă numărul de elemente. are o rezolu ie de 640 x 480 pixeli. monitorul con ine un tub de vacuum. Standardul VGA (Video Graphics Array) a fost introdus de către IBM o dată cu calculatoarele PS/2.Monitorul . ele diferind atât prin performan e. cât şi prin modalită ile tehnice de ralizare. cu scopul tipăririi de texte şi grafică. 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. Imprimanta . Standardele video MDA. Ca piesă principală. 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. semnalele analogice pot prezenta orice valoare între una minimă şi una maximă. similar cu cel de la televizor şi trei tunuri de electroni (corespunzătoare celor trei culori fundamentale). CGA (Color Graphics Adapter) şi EGA (Enhanced Graphics Adapter). El va fi corespunzător tipului de monitor video care îi este ataşat. ideal fiind a o folosi pe cea care corespunde cel mai bine tipului de lucrări 12 .

interac ionează direct cu utilizatorul. Programarea calculatorului Programele de calculator. . simplificând îndeplinirea acelor sarcini care sunt 13 . corespunzător caracterului care urmează a fi imprimat. fiind folosite mai mult la sisteme de calcul de dimensiuni mari. .imprimante cu jet de cerneală – func ionează prin pulverizarea fină a unor picături de cerneală pe hârtia de imprimat. Rolul programului de sistem este acela de a uşura sarcina programatorului. programele de tehnoredactare asistată de calculator. care la fel ca programele de aplica ie. dar în mod corespunzător şi pre ul echipamentului.executate. Utilitarele realizează o serie de opera ii de „gospodărie” cum ar fi: copierea fişierelor. sunt constituite dintr-o serie de instruc iuni pe care le execută calculatorul. spre deosebire de acestea. Calitatea imprimării creşte de la primul la ultimul tip prezentat. de grafică etc. • programe de sistem – realizează legătura între componentele electronice ale calculatorului şi programele de aplica ie şi utilitare. 1. 18 sau 24 de ace – realizează imprimarea prin impactul acelor peste o bandă de hârtie. .3. dar. • utilitare – programe. sunt programe de aplica ie. În func ie de modul în care este realizată imprimarea se disting următoarele tipuri de imprimante: . realizează prelucrări de uz general.imprimante matriceale cu 9. specializate în realizarea unei categorii de prelucrări. cunoscute sub numele de software. pregătirea discurilor magnetice pentru utilizare. Când se creează un program. Editoarele de texte.imprimante rapide de linii – ce imprimă mai multe linii odată. etc.imprimante laser – ce utilizează o rază laser sau mici diode luminiscente care încarcă electrostatic un tambur de imprimare. programele pentru gestiunea bazelor de date. testarea echipamentului. Procesul de definire a instruc iunilor pe care le execută calculatorul se numeşte programare. crearea de copii de salvare. Programele executate pe un calculator pot fi împăr ite în trei categorii: • programe de aplica ie – sunt acele programe care interac ionează direct cu utilizatorul. trebuie specificate instruc iunile pe care calculatorul trebuie să le execute pentru a realiza opera iile dorite.

3: Sistemul de operare este cel mai important program care rulează pe un calculator. Lan ul de comunicare utilizator – calculator este prezentat în Figura 1. Sistemul de operare permite rularea programelor şi păstrarea informa iilor pe disc. 1. 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. care mai cuprinde un număr variabil de programe utilitare selectate conform cu necesită ile programatorilor. afişarea caracterelor pe ecran şi la imprimantă. cum ar fi imprimanta. etc.calculator 14 . Orice calculator de uz general este dotat cu un sistem de operare care permite execu ia altor programe. şi să gestioneze alte resurse ale calculatorului.1.comune marii majorită i a programelor de aplica ie: alocarea memoriei. Un sistem de operare trebuie să aibă capacitatea de a se adapta rapid la modificările tehnologice. gestionarea fişierelor şi a directoarelor pe disc (floppy-disk sau hard-disk).3. controlul fluxului de date cu echipamentele periferice ca drivere de disc sau imprimante. citirea caracterelor de la tastatură.3. 1. rămânând în acelaşi timp compatibil cu hardware-ul anterior. În plus. trimiterea unui caracter pentru afişare pe ecranul monitorului. accesul la informa iile stocate pe disc magnetic. Sistemul de operare este un program cu func ii de coordonare şi control asupra resurselor fizice ale calculatorului şi care intermediază dialogul om-calculator. Comunicarea utilizator . fiecare sistem de operare pune la dispozi ia aplica iilor o serie de servicii care permit programelor să aloce memorie. Sistemele de operare execută opera iuni de bază precum: recunoaşterea unei intrări de la tastatură (preluare caracter). Sistemul de operare Sistemul de operare este o parte componentă a software-ului unui calculator. să acceseze diferite echipamente periferice.

sistemele de operare au responsabilită i şi capabilită i şi mai mari. Un calculator nu poate func iona decât sub gestiunea unui sistem de operare. 1.Aplica ie Disk-drive Sistem de operare Mouse Monitor Tastaturã Imprimantã Fig. Anumite sisteme de operare permit sute sau chiar mii de utilizatori concuren i. Ele ac ionează ca un gestionar al traficului de date şi al execu iei programelor. Sistemele de operare se pot clasifica după cum urmează: multi-user: Permit ca doi sau mai mul i utilizatori să ruleze în acelaşi timp programe (utilizatori concuren i). Sistemul de operare interfa ează calculatorul cu operatorul uman de o manieră cât mai transparentă cu putin ă astfel încât utilizatorul nu trebuie să facă eforturi mari de adaptare dacă lucrează cu arhitecturi hardware diferite. Pentru sisteme mai mari.4 Rolul sistemului de operare Sistemul de operare al unui calculator este partea de software necesară şi suficientă pentru execu ia oricăror alte aplica ii dorite de utilizator. 15 . În principal sistemul de operare asigură ca diferite programe şi diferi i utilizatori să nu interfereze unele cu altele. Orice aplica ie lansată în execu ie de către un utilizator apelează la resursele puse la dispozi ie de către sistemul de operare. Sistemul de operare este de asemenea responsabil cu securitatea. asigurând inaccesibilitatea persoanelor neautorizate la resursele sistemului. multiprocesor: Permit execu ia unui program pe mai mult de un microprocesor. multitasking: Permit mai multor programe să ruleze în acelaşi timp (execu ie concurentă).

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

dar lungimea lor nu poate să depăşească 64 K. un text. O parte dintre fişierele executabile sunt programe şi sunt recunoscute prin extensia lor care poate fi EXE sau COM. Fişierele EXE pot să ajungă la dimensiuni mai mari prin segmentarea programului în fragmente a căror dimensiune să fie de maximum 64K.ext. Un fişier este o colec ie de informa ii grupate sub acelaşi nume. Numele unui fişier este format dintr-un şir de caractere (care în func ie de sistemul de operare este limitat la un anumit număr maxim de caractere). sistemul de operare creează nişte fişiere speciale. Fişierele COM. De asemenea. • fişiere cu extensia SYS sau DRV. Dintre fişierele neexecutabile vom aminti câteva mai importante: • fişiere text . altele fiind constituite în fişiere de comenzi proprii sistemului de operare. Un fişier poate fi un program executabil.3. 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. 1. informa iile sunt scrise pe disc sub forma unor fişiere. Ele sunt mai compacte şi mai rapide decât fişierele EXE. numite adesea şi comenzi. 17 . care pot fi asemănate cu cuprinsul unei căr i. În prima categorie intră acele fişiere al căror nume scris în dreptul prompterului (în cazul sistemului de operare DOS) determină executarea unor activită i de către sistemul de operare. Tipuri de fişiere Fişierele se pot împăr i în două categorii – executabile şi neexecutabile. urmate eventual de semnul punct (. etc. deoarece ele con in numele fişierelor şi adresa de început a acestora. un director poate con ine la rândul său alte directoare creându-se astfel o structură arborescentă de directoare în care poate fi găsit foarte repede un anumit fişier.). con in informa ii în formatul imagine de memorie. o imagine. un grup de comenzi sau orice altceva. numite directoare. • surse de programe scrise în diferite limbaje (cu extensiile PAS – limbajul Pascal. Pentru a putea avea acces rapid la fişiere. din punctul de vedere al utilizatorului. ca de exemplu: nume. numite extensie. Un fişier este identificat prin numele său.) şi de încă maximum 4 caractere. C – limbajul C.Indiferent de sistemul de operare utilizat. a căror extensie este BAT. CPP – limbajul C++.2.

• fişiere ce con in sunete (extensiile WAV. deoarece programatorii scriau toate programele folosind cifre binare. adică a programului scris într-un limbaj de programare de nivel înalt. în cod obiect (object code). alături de fişierul sursă apare şi fişierul cod obiect (object file. BMP). programatorii trebuiau să în eleagă modul în care calculatorul interpreta diferitele combina ii de 0 şi 1. De aceea au fost create limbaje de programare care permit exprimarea instruc iunilor calculatorului într-o formă mai accesibilă programatorului. Pentru a ob ine un program executabil. Pe scurt. 1. un al doilea program – numit compilator.• fişiere care con in informa ii intermediare între cele în limbaj sursă şi cele executabile (extensiile OBJ.) Această translatare sau traducere este efectuată de către compilatoare. Compilatorul este folosit pentru transformarea codului sursă. un compilator este un program special care procesează instruc iuni scrise într-un limbaj de programare particular şi le transformă în limbaj maşină sau cod maşină pe care îl poate executa microprocesorul. MP3) etc. După ce programatorul scrie instruc iunile într-un fişier . interpretoare sau asambloare. Acest cod sursă parcurge apoi câ iva paşi înainte de a deveni program executabil.numit fişier sursă. orice program sursă trebuie eventual translatat (tradus) în limbaj cod maşină sau cod obiect pe care îl poate în elege microprocesorul. converteşte instruc iunile limbajului de programare în şirurile 1 şi 0 – cunoscute sub numele de cod maşină.3. În urma acestui proces. Pentru a programa primele calculatoare (în anii 1940-1950). • fişiere ce con in imagini (extensiile JPEG. GIF. Cum programele deveneau din ce în ce mai mari. Programatorii scriu programe într-o formă numită cod sursă. 18 . OVL). acest mod de lucru a devenit foarte incomod pentru programatori. Construirea fişierului 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. MIDI. Acest cod obiect va fi transformat în faza de editare de legături în cod maşină executabil de microprocesorul sistemului de calcul.3.

un compilator şi un editor de legături folosite pentru translatarea codului sursă în cod maşină. numit parser. de către microprocesor. În urma acestei prelucrări se ob ine codul maşină. În mod tipic. salvat într-un fişier cu extensia . C sau MATLAB folosind un editor. efective. după ce compilatorul a produs codul obiect. instruc iune cu instruc iune. Când este lansat în execu ie compilatorul acesta.exe. fiecare instruc iune a microprocesorului fiind codificată de către constructor. tuturor adreselor simbolice existente în codul obiect. Se creează astfel un fişier numit fişier cod sursă ce con ine o colec ie de instruc iuni şi declara ii scrise în limbajul respectiv. în ordinea în care au fost introduse. Acesta parcurge şi analizează sintactic. Codurile binare ale instruc iunilor microprocesorului împreună cu reprezentările interne ale datelor manipulate formează codul obiect. într-o primă etapă. Pasul final în producerea programului executabil. un programator scrie declara ii într-un limbaj precum Pascal. care translatează instruc iunile de nivel înalt într-o serie de instruc iuni cod obiect. Deci în unul sau mai multe faze (parserul este una dintre faze) din codul sursă de intrare se produce un cod de ieşire. numit în mod tradi ional cod obiect. toate instruc iunile scrise în limbajul de nivel înalt. un program executabil (executable program aflat pe disc cu extensia . lansează un analizor sintactic. gramatical. Aceste instruc iuni ale microprocesorului sunt înlocuite cu codurile lor binare. Primul pas este prelucrarea codului sursă de către compilator. secven ial.exe) se ob ine prin salvarea pe disc a codului maşină ob inut prin prelucrarea succesivă a fişierului cod sursă de către compilator (compiler) şi apoi de către link-editor (linker). Acest linker combină diferitele module (le leagă) şi dă valori reale.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. Este foarte important ca referiri la alte module de cod să fie corect reprezentate în acest cod obiect. Acest cod maşină poate fi executat secven ial. este prelucrarea codului obiect de către un editor de legături (link-editor sau linker). Cu alte cuvinte. O instruc iune de nivel înalt se translatează într-una sau mai multe instruc iuni specifice microprocesorului pentru care a fost conceput compilatorul. 19 .

Fig.exe la care adresele nu mai sunt simbolice ci absolute relativ la adresa de început a programului. Numai anumite limbaje 20 . Dacă folosim limbajul de programare C spre exemplu.obj în urma lansării în execu ie a editorului de legături. salvat pe disc sub numele nume.obj şi se leagă cu toate modulele necesare (inclusiv func ii de bibliotecă sau alte module externe). 1. ob inându-se un program executabil (cod maşină) cu numele nume. compilatorul (compiler) şi editorul de legături (linker). Blocurile dreptunghiulare reprezintă fişierele rezultate în urma aplicării celor trei utilitare de sistem: în urma utilizării editorului de texte ob inem fişierul text sursă cod cu numele generic “nume”. semnalizându-se toate erorile fatale pentru program sau avertismente utile programatorului în procesul de depanare.c care se va salva pe disc. toate salturile de adresă fiind făcute corespunzător. se ob ine un fişier cod obiect. În cazul în care compilarea se efectuează cu succes. acesta preia fişierul sursă şi îl prelucrează corespunzător. Interpretorul (interpreter) este un program care execută instruc iuni scrise într-un limbaj de nivel înalt.5 Procesul de elaborare a unui program executabil Procesul de ob inere a unui executabil este prezentat în figura de mai jos. în urma lansării în execu ie a compilatorului. La lansarea în execu ie a programului fluxul de informa ie este complet controlat de către microprocesor. se preia fişierul cod obiect nume. se ob ine fişierul nume. Blocurile tridimensionale reprezintă entită ile principale ale mediului de programare: editorul de texte.

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. un compilator translatează instruc iunile de nivel înalt direct în limbaj maşină (cod maşină). Avantajul unui interpretor este acela al evitării procesului de compilare consumator de timp în cazul în care avem programe de mari dimensiuni.de nivel înalt. În mediul de programare MATLAB. LISP sau MATLAB. Interpretorul poate executa imediat programele sursă. Prin contrast. Spre exemplu. interpretoarele permit o programare interactivă fiind des folosite în procesul de instruc ie. Un program interpretat se execută sub mediul în care a fost creat. De asemenea. Cum limbajul de asamblare con ine instruc iuni mai pu in 21 . mediu interpretor. Există două modalită i de a executa un program scris în limbaj de nivel înalt. Pentru acest motiv interpretoarele se folosesc mai ales în procesul de dezvoltare al programelor. când programatorul doreşte adăugarea unor mici por iuni de program pe care să le testeze rapid. 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. Cel mai comun mod este acela de a compila programul. Un interpretor translatează instruc iunile de nivel înalt într-o formă intermediară care este apoi executată. Programele compilate rulează în general mai rapid decât cele interpretate. apoi se deschide fişierul sursă-BASIC corespunzător şi se lansează interpretorul de BASIC pentru execu ia sa. corespunzător microprocesorului sistemului de calcul) în limbaj cod maşină. Cealaltă modalitate este “pasarea” programului unui interpretor. Putem spune că asamblorul reprezintă pentru limbajul de asamblare ceea ce reprezintă compilatorul pentru limbajele de nivel înalt. sunt prevăzute cu un interpretor. orice comandă utilizator se execută imediat. Fiecare imprimantă PostScript are incorporat un interpretor care execută instruc iuni PostScript. pentru a rula un program scris în limbajul BASIC se lansează în execu ie mediul BASIC. spre exemplu BASIC. Se pot edita şi fişiere script. Asamblorul (assembler) este un program care face transla ia unui program scris în limbaj de asamblare (limbaj de nivel scăzut.

asamblorul face practic o convertire biunivocă între mnemonicele limbajului de asamblare şi codurile binare corespunzătoare acestor mnemonice (instruc iuni). 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 .c ( lim b a j C ) n u m e .p a s ( lim b a j P a s c a l) n u m e .complexe decât cele de nivel înalt.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 . 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 .e x e Fig. 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 ã .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 .6 Detalierea procesului de generare a unui executabil 22 .b a s ( lim b a j B A S I C ) . e t c . 1.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 .o b j L in k .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 .c p p ( lim b a j C + + ) n u m e .

mai bine zis este legat de faptul că semnalul fizic purtător de informa ie este o tensiune continuă cu două valori: una înaltă (High) şi una joasă (Low). T e nsiune H igh= ’1 ’ L o w = ’0 ’ tim p Ca urmare a acestei asocieri spunem. Reprezentarea naturală a numerelor la nivelul percep iei umane este cea zecimală. Ca şi un număr zecimal. întocmai ca sistemul de numera ie zecimal. un număr binar are mai multe cifre binare. transport şi stocare a datelor interne. 23 . prin abuz de limbaj. pe când reprezentarea proprie maşinilor de calcul este cea binară. Acest lucru ine de suportul fizic de manipulare. o primă diferen ă este aceea că sistemul zecimal foloseşte ca ponderi puterile întregi (pozitive sau negative) ale lui 10 (zece) iar sistemul binar va folosi puterile întregi (pozitive sau negative) ale lui 2. adevărat) şi F (false. De aici rezultă necesitatea compatibilizării sau interfa ării între aceste două moduri de reprezentare a numerelor. Sistemul de numera ie binar folosit pentru reprezentarea informa iei în calculatoare este un sistem de numera ie ponderal. Acestor două valori li se asociază natural două valori logice: T (true.Capitolul II REPREZENTAREA DATELOR ÎN CALCULATOR Se ştie că un calculator numeric prelucrează numere binare. Cum cele două sisteme de numera ie sunt ponderale. că un calculator numeric prelucrează numere binare. fals) sau cele două cifre binare1 şi 0.

Există un standard IEEE care reglementează modul de reprezentare internă a datelor. li se atribuie semnifica ii. date de tip întreg (integer) şi date de tip real (float). În acest format se prelucrează numerele pentru implementarea diverselor opera ii aritmetice. prin conven ie. 2. De asemenea. La nivelul calculatorului informa ia nu poate fi decât binară. semnele + sau -. Formatele de reprezentare internă/externă vor fi prezentate în cele ce urmează. 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. În cele ce urmează ne vom pune următoarele probleme: . al percep iei umane. deci în principiu se poate folosi orice bază de numera ie pentru reprezentarea numerelor.În altă ordine de idei. acestea vor fi colec ii sau şiruri de cifre binare cărora.cum se reprezintă extern un număr natural 24 . Reprezentarea externă este reprezentarea numerelor la nivelul utilizatorului uman. deci în bazele 2. Cel mai simplu de reprezentat sunt numerele naturale. octal sau hexazecimal. dacă pentru reprezentarea externă sunt semnificative simbolurile de reprezentare (cifre. Într-o primă instan ă. Se face apoi trecerea la numerele întregi negative şi apoi la numerele reale care au o parte întreagă şi una frac ionară. pentru reprezentarea internă sunt necesare conven ii de reprezentare: indiferent de tipul datelor. punct zecimal sau binar. În această reprezentare putem scrie numere întregi pozitive sau negative sau numere reale. mantisă sau exponent). Din punctul de vedere al tipurilor de date care sunt implementate în limbajul C putem spune că distingem două mari categorii.1. numerele întregi interpretate fără semn se pot afişa şi în format binar. atât la nivel intern (în memoria calculatorului) cât şi la nivel extern. 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 regiştrii microprocesorului. La nivel de reprezentare externă se foloseşte semnul “-” în fa a unui număr în cazul în care acesta este negativ sau punctul care separă partea întreagă de cea frac ionară. 8 sau 16.

37x10 0.375x10-2 2 0.375 0.- cum se reprezintă intern un număr natural cum se reprezintă extern un număr întreg negativ cum se reprezintă intern un număr î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. nu sunt nici un fel de dificultă i deoarece fiecare este familiarizat cu reprezentarea zecimală a numerelor naturale sau reale.00375 -0.375 -12. Aceasta 25 . Totuşi. suntem familiariza i şi cu nota ia ştiin ifică în care intervine mantisa şi exponentul (în virgulă mobilă).375x10 -0.37x10 2 -0.12375x10 2 În general dorim să ob inem rezultatele numerice ale programelor pe care le concepem într-o formă de reprezentare accesibilă. O caracteristică a reprezentărilor externe este folosirea unor conven ii de format unanim acceptate şi de altfel foarte naturale pentru un utilizator uman. calculatorul trebuie informat asupra formatului de reprezentare în care dorim să se afişeze datele necesare.00375 12. Reprezentarea zecimală este cea mai naturală pentru utilizatorul uman. Vom oferi în continuare câteva exemple de reprezentări zecimale externe: Număr Reprezentare Reprezentare normală ştiin ifică 37 -37 0. Reprezentarea externă a numerelor În ceea ce priveşte reprezentarea externă.2.12375x10 2 -0.375 -12. 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 număr nu este întâmplătoare ci conformă cu o pondere corespunzătoare unei puteri a bazei de numera ie.” pentru delimitarea păr ii întregi de cea frac ionară.00375 12.375 0.375x100 0 -0.375 0.00375 -0.375x10 -2 0.375 -0. De asemenea.375 -0. pentru a exprima numere negative se foloseşte semnul “-” iar pentru reprezentarea numerelor reale se foloseşte punctul “. Spre exemplu.375 37 -37 0.

2.1.. De altfel şi operatorul uman face aceleaşi conven ii de reprezentare. În general. atunci vom scrie 1 ≅ 0.4.1.7.2..9} Noi suntem obişnui i să folosim mul imea cifrelor zecimale.1. Prin conven ie..6} b = 10 ⇒ B = {0. Dacă formatul ale se limitează la 4 cifre zecimale.1. fie în orice altă bază. Dacă totuşi se foloseşte o bază de reprezentare mai mare decât 10.8.. Spre exemplu să considerăm baza b = 16 care va folosi 16 cifre hexazecimale (sau mai simplu hexa). b − 1} . 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 . Reprezentarea externă a numerelor întregi Numerele naturale se pot reprezenta fie în baza de numera ie 10.. Spre exemplu ştim că numărul 1 nu poate fi exact 3 reprezentat ca un număr zecimal.5. 2. b − 2. deci fixăm un format de reprezentare.. atunci mul imea cifrelor zecimale nu mai este suficientă pentru reprezentarea numerelor în acea bază.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.3. un număr întreg în baza b se poate reprezenta cu un număr predeterminat de cifre ci ∈ B = {0.6.2. Spre exemplu: b = 2 ⇒ B = {0.1} b = 7 ⇒ B = {0.5. Mul imea B reprezintă mul imea cifrelor sau simbolurilor de reprezentare.înseamnă că va trebui să specificăm câte cifre se vor folosi la partea întreagă şi câte la partea frac ionară sau dacă dorim reprezentare ştiin ifică sau nu.2.4.3.

. + c1 ⋅ b1 + c 0 ⋅ b 0 ) = ± ∑ c k ⋅ b k k =0 n −1 În continuare vom studia următoarele probleme: ..2..... 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.c 2 c1c 0  c k ∈ B = {0.. 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 număr natural din baza 10 în baza 2. după regula: 0+0=0 27 ....Forma generală de reprezentare externă a numerelor întregi este de forma:  N b = ±c n −1c n − 2 .1.... b − 1} Valoarea numerică zecimală a numărului N b va fi: N b = ± (c n −1 ⋅ b n −1 + c n − 2 ⋅ b n − 2 + . b − 2. a) Conversia din baza 10 în baza 2 şi invers Fie de exemplu numărul zecimal 37.cum se face conversia unui număr din baza b = 10 în baza b=2 cum se face conversia inversă. Adunarea numerelor naturale binare se face întocmai ca la cele în reprezentare în baza 10. se împarte succesiv numărul la 2 şi se utilizează resturile la aceste împăr iri în ordinea inversă de cum au fost ob inute. 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ă.

Se observă cum un număr în baza 11 poate con ine şi cifra A=10 iar un număr în baza 13 poate con ine cifrele A=10. Vom converti mai întâi 4 A11 în baza 10 şi apoi numărul zecimal ob inut îl vom trece în baza 13. 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. 4 A11 = 10 ⋅110 + 4 ⋅111 = 44 + 10 = 5410 54 52 2 13 4 0 4 13 0 5310 = 4213 4 A11 = 4213 28 . transport 1 spre rangul următor Astfel. Spre exemplu. Pentru a realiza această conversie. să calculăm 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 . C=12. ca o adunare repetată. Înmul irea se face în mod asemănător. vom folosi baza intermediară 10. B=11. Fie spre exemplu numărul 4911 care se doreşte scris în baza 13.0+1=1 1+0=1 1+1=0.

c1c 0 • c −1c − 2 . Cifrele binare situate după punctul binar vor corespunde puterilor negative ale lui 2. Semnifica ie pentru programator şi pentru producătorii de software sau microprocesoare au bazele de reprezentare b = 10 şi b = 2 . b − 2.. Astfel. la numerele reale intervine simbolul punct “.. numărul 12.. −n =bm2m +bm−12m−1 +b 21 +b020 +b−12−1 +b−22−2 +. acelaşi lucru se va întâmpla şi în sistemul binar.2..b−1b−2.25 va avea reprezentarea binară: ( ) 12. deoarece baza 10 este naturală pentru reprezentarea externă a numerelor iar baza 2 este naturală pentru reprezentarea binară. Cu alte cuvinte.c − m +1c −m  c k ∈ B = {0. cu ajutorul numerelor reale putem reprezenta şi numere care nu sunt întregi.. Punctul binar va avea o semnifica ie asemănătoare cu punctul zecimal.” care delimitează partea întreagă de partea frac ionară.2.... a numerelor. care face separarea între partea întreagă şi cea frac ionară. Reprezentarea externă a numerelor reale Semnificativă pentru utilizatorul uman este reprezentarea zecimală (în baza b=10) a numerelor reale. În formulele de mai sus avem o reprezentare a unui număr real cu n cifre pentru partea întreagă şi m cifre pentru partea frac ionară. cu care suntem obişnui i.1b0.2.. Forma generală a unui număr real reprezentat într-o bază oarecare b este:  N b = ±c n −1c n − 2 . internă. Aşa cum în sistemul zecimal reprezentăm cu un număr finit de cifre zecimale numerele reale. b − 1} Valoarea zecimală a numărului de mai sus va fi: N10 = ± cn−1bn−1 +cn−2bn−2 +c1b1 +c0b0 +c−1b−1 + c−2 ⋅ b−2+c−m+1b−m+1 +c−mb−m = ± ( ) ∑ck ⋅bk k =−m n−1 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).2510 = 1100.01 = 2 3 + 2 2 + 2 −2 29 .... un număr real va avea reprezentarea binară: N2 =±bmbm−1. Fa ă de reprezentarea numerelor întregi.2.1..+b−n2−n b b 1 Spre exemplu. în general..

25 0.48 0. Astfel.37.01 Să mai considerăm un alt exemplu.44 Bitul înscris 0 1 0 1 1 1 1 0 1 Etc.37 0. înscris P. 0.Partea întreagă a unui număr real se reprezintă binar precum numerele întregi (cu sau fără semn). vom vedea cum se ob ine reprezentarea binară a lui 12.25. reprezentarea sa binară internă poate avea un număr infinit de cifre binare.3710 = 101. atunci se înscrie un bit 0 şi se continuă multiplicarea cu 2. dacă partea frac ionară zecimală se reprezintă binar.36 0. Ob inem: 5.74 0. Este valabilă şi reciproca: un număr real zecimal cu un număr 30 . atunci se înscrie un bit 1.68 0.84 0.84 1.F.96 0.48 0. P. se procedează în mod invers ca la partea întreagă. Pentru a determina partea frac ionară. x 2 0. atunci aceasta se înmul eşte succesiv cu 2. 0. Dacă rezultatul nu depăşeşte valoarea 1.84 0.25 Partea P.2 Cu cât mai multe cifre binare vom re ine după punctul binar.37 Partea întreagă are reprezentarea 510 =1012 Partea frac ionară P.92 1.72 Etc.96 0.72 0. Partea întreagă este 12.010111101. x 2 Noua Bitul frac ionară P.25 = 1100.5 1 0 0 1 Ob inem exact rezultatul căutat: 12. Partea frac ionară este 0.68 0. Spre exemplificare.92 0..F..F.F.72 1.74 1.48 0.44 Noua P.74 0..F.36 0. cu atât vom fi mai aproape de valoarea exactă 5. Ea se reprezintă binar prin împăr iri succesive la 2 şi considerarea resturilor.36 0. Se continuă mai departe cu dublarea valorii care depăşeşte 1. Dacă rezultatul depăşeşte valoarea 1. Să reprezentăm numărul 5. Ob inem un rezultat foarte important: Deşi un număr zecimal poate avea un număr finit de cifre zecimale după punctul zecimal..68 1.92 0.5 0 0.F.96 1. 0.

2. Acest lucru este decisiv pentru a în elege importan a lungimii reprezentării numerelor în calculator.. Cum orice reprezentare 3 binară internă este pe un număr finit de bi i. Bitul corespunzător ponderii celei mai mari. se numeşte MSB (Most Significand Bit) iar cel 31 .3. Tipul unei date reprezintă modul în care microprocesorul stochează în memorie şi prelucrează cu ajutorul regiştrilor interni o dată. cu atât precizia de reprezentare creşte. Reprezentarea internă a numerelor a impus în limbajul C definirea aşa-numitelor tipuri de date.1.3 Reprezentarea internă a numerelor Deoarece semnalul intern purtător de informa ie într-un calculator este de tip binar.10 = 0.infinit de cifre se poate reprezenta într-o altă bază pe un număr finit 1 de cifre ( ex: = 0. minus (-) sau punct (.) nu au nici o semnifica ie pentru calculator. Orice număr (orice tip de dată) este reprezentat la nivel intern de un număr prestabilit de bi i. Cu cât un număr binar se reprezintă pe un număr mai mare de bi i.3. Reprezentarea internă a numerelor întregi Un număr binar este o colec ie de cifre binare ponderate fiecare cu o putere a lui 2. În reprezentarea internă acest lucru nu mai este posibil deoarece semnele plus (+).13 )... un număr zecimal (întreg sau real) se va reprezenta intern în baza 2 cu ajutorul unui număr binar. ci cu o anumită aproxima ie. În reprezentarea externă a numerelor am văzut că se poate folosi orice bază de numera ie (cu cifrele corespunzătoare). 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 câmpuri de bi i din cadrul reprezentării. Specialiştii din industria software au ajuns la un consens de reprezentare concretizat prin standardul IEEE 754 de reprezentare a internă a numerelor reale în computere. numărul poate să nu fie reprezentat exact în calculator. De asemenea. O cifră binară se numeşte bit (Binary Digit) şi poate fi fie 0 fie 1.. situat cel mai în stânga.3333. 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ă. 2.

putem repeta procedeul de mai sus şi ob inem reprezentarea numărului pozitiv dat de modulul său. iar dacă numărul este negativ se utilizează în pozi ia MSB bitul de semn ‘1’. 1011010 + 1 = 1011011 => -3710 = 10110112 Evident. Se reprezintă modulul numărului negativ. pas3. Astfel: 10110112 = -1x26 + 1x24 + 1x23 + 1x21 + 1x20 = -64 + 27 = -37 32 . situat cel mai în dreapta. se adaugă în pozi ia MSB bitul de semn ‘0’. Pentru a vedea ce număr negativ este reprezentat. Reprezentarea numerelor întregi negative în complement fa ă de 2 Această formă de reprezentare a numerelor negative necesită parcurgerea următorilor paşi: pas1. O modalitate mai simplă este alocarea ponderii corespunzătoare bitului de semn dar pe care o considerăm că reprezintă un număr negativ. Dacă numărul este pozitiv. reprezentarea externă (cea percepută de operatorul uman) şi cea internă (cea prelucrată de procesorul calculatorului) sunt asemănătoare. Această conven ie prevede folosirea MSB pentru reprezentarea semnului numerelor întregi. evident) pas2. se numeşte LSB (Less Significand Bit). este posibil să credem că prin utilizarea complementului fa ă de 2 putem pierde semnifica ia numărului negativ.corespunzător ponderii celei mai mici. numerele negative se reprezintă în aşa numitul complement fa ă de 2. Se complementează to i bi ii numărului astfel ob inut. să reprezentăm numărul -37. Mai mult. Cum pentru operatorul uman operatorii ‘+’ sau ‘-‘ semnifică faptul că un număr este pozitiv sau negativ. pas1. |-37| = 37 3710 = 1001012 = [0] 100101 bit semn pas2. MSB este bitul de semn şi este egal cu 1. De exemplu. În cazul reprezentării binare a numerelor naturale. este necesară o conven ie pentru reprezentarea internă a numerelor întregi negative. 0100101---->1011010 pas3. Complementarea înseamnă transformarea bitului 0 în bitul 1 şi a bitului 1 în bitul 0. La o primă vedere. Numărul astfel ob inut se adună cu 1. folosind bit de semn (egal cu 0.

Deoarece am observat că bi ii unui întreg cu semn nu au to i aceeaşi semnifica ie. vom scrie reprezentările 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. este nevoie să reprezentăm numerele cu care lucrăm pe un acelaşi număr de bi i. se execută folosind în algoritmi bitul de semn ca pe un bit obişnuit. 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 . dorim să calculăm: 37-25 25-37 (-25)x37 (-25)x(-37) Pentru efectuarea acestor calcule.3. bi ii de semn se vor afla în aceeaşi pozi ie (vor avea aceeaşi pondere) şi vom ob ine astfel rezultate corecte. sau. mai bine zis. De exemplu. Pentru a avea o scriere pe un acelaşi număr de bi i.2 Adunarea. se adaugă (completează) la stânga bitul de semn de un număr corespunzător de ori. scăderea şi înmul irea numerelor întregi Aceste opera ii se execută folosind reprezentarea în complement fa ă de 2 a numerelor întregi.2. La adunări sau scăderi.

De exemplu. Aceste numere sunt cuprinse în gama [− 2 . aici problema nu mai are o rezolvare asemănătoare. 117-12=117+(-12) = 01110101+11110100 = 01101001 = 10510. De fapt. Incorectitudinea provine de la faptul că rezultatul a depăşit gama de reprezentare. ne aşteptăm să ob inem un rezultat. să considerăm opera iile (117-12) şi (117+12). să calculăm (-25)x37. re inem că pentru a ob ine rezultate corecte este necesar să precizăm dacă se lucrează sau nu cu bit de semn şi pe câ i bi i se face reprezentarea. Astfel. Să considerăm. se realizează func ia logică XOR a bi ilor de semn. Prin prima scădere. În ceea ce priveşte înmul irea numerelor întregi cu semn (cu bit de semn). rezultat corect. procesorul va primi pentru procesare următoarele două numere: 37 x(−25) = [0]100101 × [1]100111 34 . 127]. în sensul că nu putem trata bi ii de semn la fel cu cei de reprezentare ai valorii. 7 7 Dacă vom dori să adunăm două numere din acest domeniu şi să reprezentăm rezultatul tot pe un octet.În continuare vom pune în eviden ă importan a gamei de reprezentare. se ia decizia reprezentării corecte a rezultatului. iar opera iile de înmul ire se vor face numai cu numere pozitive. Numerele negative se vor lua în modul. adică a domeniului de valori ale datelor. rezultat corect. adunarea a două numere cu semn reprezentate pe un octet (8 bi i). 2 − 1] = [− 128. în aceeaşi gamă de reprezentare. procesorul studiază bi ii de semn şi ia o decizie în privin a semnului rezultatului. Pentru aceasta. La final. pentru că numai în acest context interpretarea rezultatelor este corectă. putem avea surprize. spre exemplu. rezultat evident incorect. 255] şi rezultatul va fi 117+12 = 001110101+000001100 = 010000001 = 12910. 105. gama de reprezentare devine [− 256. func ie de semnul rezultatului. 117+12 = 01110101+00001100 = 10000001 = -12710. Spre exemplu. Dacă rezultatul este interpretat pe 9 bi i de exemplu. Se observă că operanzii sunt în gama de reprezentare a numerelor cu semn pe 8 bi i. Ca o concluzie preliminară.

pentru a furniza rezultate corecte. Acest ultim număr se va complementa fa ă de 2. aşa cum am arătat mai sus.25 poate avea reprezentarea: 12. 2.10 + 0. se adaugă bitul de semn. se va reprezenta în complement fa ă de 2. Mai departe. To i întregii cu semn.01 → 10011 . Reprezentarea în complement fa ă de 2 se poate folosi şi pentru numerele reale negative. problema reprezentării numărului negativ a fost rezolvată cu ajutorul bitului de semn dar problema reprezentării punctului binar va avea altă rezolvare.01 01100 . Numerele întregi cu semn (care în reprezentare externă sunt prefixate cu ± ) au ca reprezentare internă un bit de semn.2510 Pentru înmul irea numerelor reale rămân valabile considerentele de la numere întregi. sunt reprezenta i intern în complement fa ă de 2. procesorul va trebui informat în permanen ă despre ce fel de numere prelucrează (cu sau fără semn) şi care este lungimea lor de reprezentare (toate trebuie să aibă aceeaşi lungime). ob inându-se 01110011101.112 = −2 4 + 21 + 2 0 + 2 −1 + 2 − 2 = −16 + 3 + 0.012 → 01100 . adică valoarea -1024+99 = -925. 0 pentru numere pozitive. Mai departe se va lucra cu 25. care se va înmul i cu numărul (fără semn) 100101. valoarea 1110011101. care se ob ine prin complementarea fa ă de 2 a numărului binar 1100111: 1100111 0011000+1=0011001 Se va re ine pentru procesare numai numărul (fără semn) 11001.3 Reprezentarea internă a numerelor reale Din considerentele de la reprezentarea externă a datelor putem trage alte concluzii importante din punct de vedere al reprezentării interne. ob inându-se 10001100010+1=[1]0001100011.2510 = 1100 .11 10011 .Se analizează separat bi ii de semn şi se ia decizia că rezultatul va fi negativ. dar care se tratează deosebit de ceilal i bi i ai reprezentării.3. Astfel.75 = −12. valoarea corectă. modulul numărului (-25).01 = 10011 . -12. 35 . Ca o concluzie. deci. la final. bitul de semn fiind MSB de la partea întreagă. Numerele binare întregi fără semn au aceeaşi reprezentare atât externă cât şi internă. ob inând. care au MSB=1. În cazul de mai sus.

spre exemplu. Cum primele procesoare care au condus la apari ia pe pia ă a primelor calculatoare pentru neprofesionişti (aşa numitele Home Computers) au fost procesoare capabile să prelucreze şi să transmită în paralel 8 bi i. Denumire Dimensiune Nr. reprezentarea binară a numărului 12. 1B = 8b (adică un byte reprezintă 8 bi i) Procesoarele au evoluat. în cazul nostru numărul pozitiv 4. cu o precizare: nu se face o deosebire netă între bi ii reprezentării păr ii întregi şi cei ai reprezentării păr ii frac ionare. Tipul unei date determină modul în care procesorul stochează şi prelucrează data respectivă. singura deosebire între reprezentarea numerelor reale şi a celor întregi constă în faptul că numerele reale necesită o informa ie suplimentară despre aşa numitul exponent. Cum evolu ia lor s-a făcut trecându-se succesiv prin multipli de 8 bi i. Acest lucru se întâmplă şi în realitate.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 reprezentării.Numerele reale se pot reprezenta identic cu cele întregi cu semn. Acest tratament nediferen iat provine de la reprezentarea ştiin ifică uzuală cu mantisă şi exponent. Fie. vom prezenta tipurile de bază pe care le pot avea datele în reprezentarea internă.2510 = 1100. a fost naturală gruparea a 8 bi i într-o entitate numită byte.25: 12. modul de interpretare al bi ilor ce compun reprezentarea şi gama de 36 . pe care le vom prezenta sintetic în tabelul de mai jos. ajungându-se în prezent la procesoare pe 64 de bi i. s-au impus şi alte entită i de reprezentare a informa iei. 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 cuvânt Cuvânt dublu Cuvânt cvadruplu B W DW QW TW A determina reprezentarea internă înseamnă să determinăm lungimea reprezentării (de obicei în multipli de octe i). În cele ce urmează. byte Nr. Deci.

int. double. numerele sunt reprezentate intern luându-se în considerare bitul de semn. â. octetul s-a dovedit o entitate suficientă pentru codificarea caracterelor utilizate în informatică. Ş.. Dacă se declară o dată de tip char. Codul ASCII a standardizat această codificare. unsigned. de exemplu. long.reprezentare. Pentru a fi receptat.. int. short. Prin caracter în elegem unită ile de bază care se pot tasta (intrări de la tastatură). deci implicit numerele întregi sau reale au MSB bit de semn. Se mai pot include caractere ce reprezintă numere. adică să determinăm magnitudinea (valorile minime şi maxime pozitive şi negative) ce pot fi reprezentate în formatul respectiv. Cum cu un octet putem codifica 28 = 256 caractere. fiecare având şi anumite particularizări. Î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: ş. float. Astfel.3. numit EBCIDIC). deci reprezentarea internă este de forma: 37 . fiecare caracter are asociat un cod binar (o combina ie de bi i) care îl identifică în mod unic. în română. Tastatura reprezintă. semne de punctua ie sau alte caractere de control.3. 2. double şi cu ajutorul modificatorilor de tip putem ob ine diverse particularizări. tipări la imprimantă sau afişa pe ecran. float. . În limbajul C. în ordinea char. astfel încât el este folosit în cvasitotalitatea calculatoarelor (doar mainframe-urile IBM mai folosesc un alt cod. Tipurile de bază sunt char. de exemplu).1 Tipul char Codul ASCII (American Standard Code for Information Interchange) este un cod de reprezentare a caracterelor. î. 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. nu se mai consideră (interpretează) bitul de semn. emis sau prelucrat de către calculator. Ca o generalitate. prin modificatorul unsigned. Modificatorii pot fi signed. Dacă se specifică explicit. mai vechi. există două tipuri de reprezentare pe care le putem numi principale: tipul întreg şi tipul real. ea este considerată explicit de tipul signed char (cu MSB bit de semn). dispozitivul de intrare care con ine de fapt o întreagă colec ie de caractere ce pot fi emise prin apăsarea unei taste.

pentru codificarea celor 16 cifre avem nevoie de 4 cifre binare ( 2 4 = 16 ).S 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. coloana H reprezintă aceeaşi valoare reprezentată în format hexazecimal (baza 16) iar în coloana Sym se reprezintă simbolul afişat pe monitoarele PC. în gama max = 2 8 − 1 = 255  ⇒ [0. Reprezentarea unui număr natural în format hexazecimal se realizează cu metoda împăr irii succesive la 16 sau. Trebuie men ionat faptul că reprezentarea datelor în format hexazecimal este foarte răspândită în tehnica programării calculatoarelor. Avantajul reprezentării interne a datelor în format hexazecimal constă în folosirea unui număr mai mic de cifre (de 4 ori mai mic decât numărul de cifre binare).se divide octetul în două grupe de câte 4 bi i 38 . 127]  min = −27 = −128  Dacă se declară tipul unsigned char. Restul de 128 de caractere se mai numeşte şi set de caractere extins ASCII şi poate fi vizualizat printr-un program simplu. Pentru a reprezenta un octet vom avea nevoie de 2 cifre hexazecimale şi vom proceda astfel: . Întregul alfabet al limbajului C se regăseşte în mul imea primelor 128 de caractere ASCII. 255]  min = 0  Tabelele de mai sus con in codurile ASCII ale primelor 128 de caractere. Cum mul imea cifrelor hexa con ine 16 simboluri (0…9 şi A…F). pornind de la reprezentarea binară a numărului. atunci nu se mai consideră (interpretează) bitul de semn şi data se consideră întreagă pozitivă. Coloana D semnifică valoarea zecimală (decimal) a octetului. mai simplu.

Codurile corespunzătoare simbolurilor alfanumerice din tabel sunt exact semnalele binare care se transmit în reprezentarea internă. În tabelele de mai jos se prezintă codificarea ASCII a caracterelor. La fel se întâmplă când se lucrează cu procesoare de text sau când se tipăreşte un document la imprimantă. atunci circuitele corespunzătoare transmit spre calculator semnale binare corespunzătoare codului 1010 0001. să presupunem că avem numărul 217. < = > ? 39 . 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 . 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 : .10012 = D916 = 13 ⋅161 + 9 ⋅16 0 = 208 + 9 = 217 - În acest mod. 60 61 . Cu alte cuvinte. dacă la tastatură se tastează simbolul “a”. Sistemul de calcul manevrează codurile ASCII corespunzătoare literelor şi cifrelor pe care utilizatorul le poate interpreta.se înlocuieşte fiecare grup de 4 bi i cu cifra hexazecimală pe care o codifică. se poate reprezenta simplu cu ajutorul a 2k cifre hexazecimale. adică 61H sau 97 în zecimal. De exemplu. 21710 = 110110012 = 1101. dacă un număr are o reprezentare internă pe un număr de k octe i.

2 Tipul int Acest tip se foloseşte pentru reprezentarea numerelor întregi cu sau fără semn.3.3. 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. s-a trecut la modul de reprezentare a întregilor impus de noul procesor Intel 80386 dotat şi cu coprocesorul matematic Intel 80387. Odată cu standardizarea ANSI C din 1989. 2 31 = 2 ⋅ 2 30 = 2 ⋅ 210 ≅ 2 ⋅ 10 3 ≅ 2 ⋅ 10 9  31  min = − 2  ( ) ( ) 40 .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. Reprezentarea pe 4 octe i duce la posibilitatea măririi gamei de reprezentare astfel:  max = 2 31 − 1 3 3  .

[ ] 5. 2 ⋅ 10 ] 9 9 9 unsigned int nu va mai lua în considerare bitul de semn. Conceptul fundamental este acela de nota ie ştiin ifică. sub forma max = 215 − 1  .844 ⋅ 1019 . 215 = 2 5 ⋅ 210 = 32 ⋅ 210 ⇒ [− 32768. fiind cunoscut şi ca reprezentare în virgulă mobilă (floating point). 32767] .2 Tipul float Acest tip de reprezentare este de tip real. Acest tip descrie mecanismul de bază prin care se manipulează datele reale.0101 x 2 2 = 0. 65535] long int se va reprezenta pe 8B şi va conduce la o gamă imensă de reprezentare a numerelor întregi. prin care orice număr se poate exprima ca un număr zecimal (deci.3.  min = −215  unsigned short int va schimba gama de reprezentare în [0.3. 2. 1.  3 3  max = 2 32 − 1 . lucru dovedit de 6 ± 2 63 = ±2 3 ⋅ 210 ≅ ±8 ⋅1018 = ±9.10101 x 2 3 41 .2234 ⋅1018 ( ) unsigned long int va considera numai numere întregi pozitive în gama 0. MSB S b 14 poate schimba cu ajutorul b0 LSB short int se va reprezenta pe 2B. astfel încât reprezentarea internă este de forma din figura de mai jos.1475 ⋅ 10 ] ≅ [− 2 ⋅ 10 .01 = 1.Rezultă că putem reprezenta numere întregi în gama: [± 2.2510 = 101. cu punct zecimal) multiplicat cu o putere a lui zece sau ca un număr real binar (cu punct binar) multiplicat cu o putere a lui 2. Evident. 2 32 = 4 ⋅ 2 30 = 4 ⋅ 210 ≅ 4 ⋅ 10 3 ≅ 4 ⋅ 10 9   min = 0  ( ) ( ) Gama de reprezentare se modificatorilor short sau long.

mantisa. cu reprezentare pe 4 octe i (32 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. cu reprezentare pe 8 octe i (64 bi i. cu reprezentare pe 10 octe i (80 bi i.bitul de semn (sign) . frac ia (significand) . quad word) .double. double word) .exponent (exponent) Folosind formatul specific I80386.long double.float . în limbajul C se disting trei tipuri de date reale: . Când un astfel de număr este încărcat de procesor în stiva pentru numere reale (flotante) pentru prelucrare sau 42 .Se observă cum stocarea în calculator a unei date floating-point necesită trei păr i: .

Deoarece punctul binar este mobil. cu bias) pentru a reprezenta numai numere pozitive (deci exponentul este interpretat ca număr natural fără semn). adică după bitul 1 implicit (phantom). să considerăm un format float în care se stochează: Sign = 0 43 . Ceea ce le deosebeşte este numărul de bi i aloca i pentru exponent şi pentru mantisă. deci bi ii semnificativi atât ai păr ii întregi cât şi ai păr ii frac ionare cu singura restric ie ca aceşti bi i să fie consecutivi. El con ine to ii bi ii semnificativi ai reprezentării. Se decide astfel foarte rapid care număr este cel mai mare.0000101 = 0. el este automat convertit la formatul long double (sau extended). De exemplu. Semnul are alocat în toate formatele un singur bit: 0 pentru numere pozitive şi 1 pentru numere negative. Avantajul exponentului decalat constă. Pentru a uşura operarea cu aceste numere. Mărimea câmpului exponent variază cu formatul şi valoarea sa determină câ i bi i se mută la dreapta sau la stânga punctului binar. Pentru a salva şi mai mult spa iu. el se converteşte la tipul float sau double. precum şi interpretarea bi ilor mantisei (significand).ca rezultat al prelucrării. cel mai mare fiind cel care are 1 la primul bit diferit. Ca exemplu.101x 2 −4 câmpul significand va stoca numărul 101. Cu cât formatul este mai larg. care va fi prezentat în continuare. nu şi cele 4 zerouri nesemnificative ale păr ii frac ionare. pe lângă faptul că nu mai are nevoie de bit de semn. cu atât vor fi mai pu ini pentru partea frac ionară şi invers. Câmpul significand este analogul mantisei în nota ia ştiin ifică. cu atât se vor reprezenta mai precis numerele. În cazul long double. Câştigând acest bit (numit bit phantom). În cazul în care acest număr se stochează în memorie. se aplică după primul bit 1. pentru formatele float şi double câmpul significand nu va con ine primul bit semnificativ care obligatoriu este 1. nici unul dintre cele trei formate float nu stochează zerouri nesemnificative. pentru numărul 0. câmpul exponent nu este stocat ca un număr întreg cu semn. ci este decalat (normalizat. se dublează gama de reprezentare. Formatul long double va con ine totuşi bitul de semn 1 cel mai semnificativ. cu cât sunt mai mul i bi i aloca i păr ii întregi. Biasul adăugat se scade pentru a afla exponentul exact. Punctul binar se pune exact înaintea primului bit din câmpul significand. în faptul că pentru a compara două numere reale putem începe prin compararea bi ilor pornind de la MSB către LSB. Pentru a salva un spa iu pre ios de stocare. Toate cele trei subtipuri reale au un format comun.

.bitul de semn va fi 0 44 . deci pe 8 octe i.5..5 Reprezentarea internă a numărului 12. 5 10 = 41480000 16 În cazul în care dorim să reprezentăm numărul negativ –12. pe 4 octe i (float)..00 x 24 = 1100. singurul bit care se va modifica va fi bitul de semn. care devine 1.1 =12. putem spune că reprezentarea internă a numărului real 12.110010.127 = 3 Bi ii câmpului significand se ob in adăugând MSB phantom.Exponent = 10000010 = 13010 Significand = 1001000…00 Valoarea reală a exponentului va fi 130 .00 Numărul real care s-a stocat este: 0.5 este (în format hexazecimal): 12 . deci aceştia vor fi 11001000.. reprezentarea internă în format float a numărului negativ real –12. Astfel.5. este următoarea: 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.5 se reprezintă în formatul double. atunci reprezentarea sa internă se va realiza astfel: .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ă numărul 12.

mantisa şi exponentul se reprezintă ca numere naturale fără bit de semn. adăugăm numărul 128. deci se va schimba şi bias. Atunci: exponent −1 3 + 1023 = 1026 = 1024 + 2 = 1000000001 0 bias - significand va fi acelaşi ca la tipul float. .exponentul cu semn este reprezentat pe 8 bi i. .5. + 127] . . Se poate pune o întrebare legitimă: de ce bias-ul în cazul float spre exemplu este 127? Pentru a răspunde la această întrebare. care va fi 1023.Exponent pozitiv = exponent +128 – 1 = exponent + bias de unde rezultă evident faptul că bias = 127 în cazul tipului float. Formatele prezentate mai sus respectă standardul IEEE 754 de reprezentare a internă a numerelor reale în computere.25 x 1. Pentru aceasta. putem face următorul ra ionament: . vom scrie cei doi factori ai produsului în forma: 45 . ci pe 11 bi i.pentru a ob ine un exponent pozitiv. deci este în gama de reprezentare [− 128.deoarece bitul phantom nu este reprezentat. În final să analizăm un exemplu de procesare a produsului a două numere reale.510 = 4029000000 000000 16 Re inem că la numere reale numai bitul de semn indică dacă numărul este pozitiv sau negativ. Vrem să calculăm valoarea 5. 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 . exponentul trebuie micşorat cu o unitate pentru a indica unde anume se pozi ionează exact punctul binar.- exponentul nu va mai fi pe 8 bi i ca la tipul float.

11 × 2  3+1 5.111 = 7.0111111× 2 = 111.56 ⋅ 1038 Valoarea maximă exactă.6 ⋅ 10307 ( ) Valoarea maximă exactă este n max = 1.5 = .7 ⋅10 308 46 . avem exponent _ biasmax = 255 ⇒ exponent _ realmax = 255 − 127 = 128 nmax = 2128 = 28 ⋅ 210 = 256 ⋅ (1024)12 ≅ 256 ⋅ 1036 = 2.. La tipul float.25 × 1.4028 ⋅ 10 38 exponent_ biasmin = 0 ⇒ exponent_ realmin = 0 − 127 = −127 −12 −12 −13 nmin = 2−127 = 2−7 ⋅ 210 = 23 ⋅ 2−10 ⋅ 210 = 8 ⋅ 210 ≅ 8 ⋅ 10−39 ( )12 ( ) ( ) ( ) Valoarea pozitivă minimă exactă este n min = 5.10101 × 5.510 = 1.01 = .875 Se observă cum câmpurile exponent şi significand sunt procesate separat.25 × 1.11)]× 2  4 .10101) × (.12 = . calculată fără a aproxima ca mai sus: 210 = 1024 ≅ 1000 = 103 este n max = 3. în final corelându-se forma de reprezentare internă. Game de reprezentare pentru numerele reale Gama de reprezentare pentru fiecare din tipurile reale prezentate mai sus se calculează luând în considerare cel mai mare număr şi cel mai mic număr posibil a fi scris în respectiva reprezentare. 1.25 = 101.0111111 ⇒ 5.10101× 23 10 2   1 .5 = [(. Astfel.11 −−−−− 10101 10101 −−−−− . exponentul este decisiv pentru gama de reprezentare.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.

3. Neajunsul acestei reprezentări este faptul că numerele BCD ocupă spa iu de stocare mai mare decât numerele binare. cifra zecimală 5 va fi reprezentată intern sub forma 00001001. numărul 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. Reprezentarea numerelor în cod BCD este folosită pentru a face numerele binare mai accesibile operatorului uman. 4932 n  max = 1. Toate formatele reale prezentate se conformează standardului IEEE 754 pentru reprezentarea numerelor în virgulă mobilă în format binar. Ambele codificări folosesc reprezentarea pe 4 bi i a cifrelor zecimale. binary-coded decimal) în locul formatelor binare standard. Spre exemplu. crescând capacitatea de stocare internă precum şi gama de reprezentare pe un acelaşi număr de octe i.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.125 ⋅ 10−306 ( ) Valoarea pozitivă minimă exactă este n min = 1. Spre exemplu.1125 ⋅ 10 −308 Efectuând aceleaşi considera ii şi calcule pentru tipul long double. 47 . Procesorul 80386 poate manevra două tipuri de formate BCD: împachetat şi neîmpachetat (packed BCD şi unpacked BCD). Formatul packed BCD stochează două cifre zecimale pe un octet. În formatul unpacked BCD.4 ⋅ 10 −4932  2. 99 pentru packed BCD şi 255 pentru codificarea binară fără semn standard. pentru computer neavând nici un fel de relevan ă. o cifră zecimală se stochează pe un octet. Ele sunt mai uşor de interpretat de către programatorul uman.1 ⋅ 10 vom ob ine  nmin = 3.exponent _ biasmin = 0 ⇒ exponent _ realmin = 0 − 1023 = −1023 −102 nmin = 2−1023 = 2−3 ⋅ 210 ≅ .

cu cel mai semnificativ bit (MSB) bit de semn: 1 pentru numere întregi negative şi 0 pentru numere întregi pozitive. toate numerele se pot defini fără semn (unsigned). Dacă se specifică explicit. 8 sau 10 octe i şi con ine 3 câmpuri de bi i distincte: bit de semn. caz în care calculatorul nu mai interpretează bitul de semn (MSB) diferit ci îl include în câmpul de reprezentare al mărimii. Acest schimb de date are dublu sens: de la operatorul uman către calculator şi invers. decisiv pentru în elegerea dezvoltărilor ulterioare. Tipul de dată întreg (integer) se reprezintă intern pe 2. putem sintetiza următoarele: Reprezentarea externă a numerelor se referă la modul în care operatorul uman acceptă schimbul de date cu calculatorul. Reprezentarea externă este de obicei zecimală şi are un format aproape identic cu formatul matematic uzual: simbol de semn prefixat. câmp mantisă şi câmp exponent. punct zecimal. Tipul de dată real (float) se reprezintă intern pe 4.Ca o concluzie la acest capitol. În format extern se introduc datele de la tastatură pentru prelucrare şi se ob in pe monitor sau la imprimantă rezultatele oferite de calculator. 4 sau 8 octe i în complement fa ă de 2. Reprezentarea internă a numerelor se referă la modul în care se stochează datele în memoria RAM a calculatorului şi respectiv în regiştrii interni ai microprocesorului. mantisă sau exponent. 48 . crescând gama de reprezentare. Un caz particular de dată de tip întreg este tipul character. de lungimi corespunzătoare. Această reprezentare internă este legată de no iunea de tip de dată. Numerele naturale se mai pot reprezenta şi în format octal sau hexazecimal. interpretat ca întreg pe un octet.

• se editează programul sursă folosind editorul mediului BorlandC.N – pentru a deschide un fişier nou. } • se ac ionează tasta F2 şi se indică numele fişierului (cu extensia . • pentru a vizualiza rezultatele execu iei programului se tastează <Alt>-F5. • se revine în fereastra de editare a mediului ac ionând o tastă oarecare. Crearea şi lansarea în execu ie a unui program C Prezentăm câteva comenzi simple pentru a lansa în execu ie un program C folosind compilatorul BORLANDC v3. • se realizează compilarea.bc – pentru a intra în mediul BorlandC. • pentru a închide un fişier sursă se tastează <Alt>-F3 iar pentru a ieşi din program în mediul de operare se tastează <Alt>-X. .cpp) pentru salvarea programului sursă pe disc – de exemplu mesaj.c sau .c . 49 .<Alt>-F – pentru a selecta meniul File.Capitolul III ELEMENTELE DE BAZĂ ALE LIMABJULUI C 3. . link-editarea (realizarea legăturilor) şi lansarea în execu ie a programului executabil mesaj.h> void main (void) { printf("Primul program in C!").exe tastând <CTRL>-F9.1 (versiunea pentru sistemul de operare DOS): • după setarea pe directorul corespunzător se tastează .1. Exemplu: #include <stdio.(se recomandă salvarea pe disc după efectuarea oricărei modificări în programul sursă pentru evitarea pierderii accidentale a acesteia).

• din meniul Compile se selectează op iunea Build All ce va afişa caseta de dialog Compiling (compilare). • din meniul File se selectează op iunea Save As… sau Save iar în căsu a de dialog care apare se va salva fişierul program cu extensia . • dacă opera ia de compilare se încheie cu succes (nu există erori de sintaxă în program) compilatorul va afişa mesajul Press any key.CPP 5: Statement missing . • lansarea în execu ie a fişierului executabil se poate realiza folosind op iunea Run din meniul Run sau combina ia de taste <CTRL>-F9.cpp.La fel de simplu poate fi utilizată şi varianta pentru sistemul de operare WINDOWS a compilatorului BORLANDC v3. • se selectează din meniul File op iunea New. Lipsa caracterului punct şi virgulă provoacă o serie de erori în cascadă. precum şi o scurtă descriere a erorii. • în fereastra noname00. Pentru a corecta erorile sintactice se merge cu ajutorul cursorului în linia indicată de către mesajul de eroare şi se corectează instruc iunea respectivă. caz în care compilatorul va crea fişierul executabil. reguli ce trebuie respectate la editarea unui cod sursă. denumite reguli sintactice.h> void main (void) { printf("Primul program in C!") } La compilare pe ecran vor apare următoarele mesaje de eroare: Compiling NONAME00.CPP 5: Compound statement missing } Cu toate că în codul sursă există doar o eroare. În acest caz. creându-se fişierul noname00. 50 .cpp se introduce codul programului. Fiecare limbaj de programare are un set de reguli.1: • se lansează în execu ie programul bcw. Error NONAME00. compilatorul de C va afişa două mesaje de eroare. Dacă este încălcată o regulă sintactică.cpp. pe ecran va fi afişat un mesaj de eroare ce specifică linia ce con ine eroarea. programul nu va fi compilat cu succes. În exemplul următor programului îi lipseşte caracterul punct şi virgulă după utilizarea func ie printf: #include <stdio.CPP: Error NONAME00.exe.

.param_n) − − − − − − − −  − − − − − − − − − − − − − − − −  Instruc iuni declarare tip parametri { − − − − − − −  − − − − − − − − − − − − − −  Corp func ie=secven ă de instruc iuni sau apel de func ii } unde: .. În limbajul C există două categorii de func ii. . Cealaltă categorie con ine func iile ce nu returnează nici o valoare la revenirea din ele ... . El semnifică lipsa unei valori returnate la revenirea din func ie.nume_func ie reprezintă numele sub care func ia este cunoscută în program.. Structura unui program C Conceptul de bază folosit în structurarea programelor scrise în limbajul C este func ia. 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. sau prin instruc iuni separate plasate imediat după lista parametrilor. un program în C este compus din cel pu in o func ie şi anume func ia main() sau func ia principală. Structura generală a unei func ii C este de forma: tip nume_func ie (param_1.. Exemple: 1) void f(void) { ………… } 51 .2..parametrii cu care func ia este apelată şi al căror tip poate fi declarat direct în această listă.param_n . 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.tip reprezintă tipul de dată returnat de func ie (în mod implicit o func ie returnează tipul int). pentru aceste func ii fiind utilizat cuvântul cheie void în calitate de tip.3. .param_1. tipul acestei valori fiind definit de de tipul func iei. O primă categorie este formată de func iile ce returnează o valoare la revenirea din ele în punctul de apel. Astfel. param_2. La rândul ei.

". la rândul lor. Din punct de vedere sintactic. Aceasta func ie este folosită în general fără parametri. sau realizate în alte limbaje de programare: asamblare. Fortran. / * apelul -------} functiei       f() * /      Functia principala ___________ f( ) {     − − − − − − − − − − − −   }  Functia f( ) Programul începe cu execu ia func iei main(). Func iile C sunt în general unită i independente. Cu aceste precizări generale. Pascal etc. orice instruc iune trebuie terminată cu caracterul ". atunci structura acestuia va fi de forma: tip main( ) { -------f( ). Func iile apelate vor primi valori pentru argumentele (parametrii) lor şi pot returna către func ia apelantă valori de un anumit tip. La rândul lor.2) double g(int x) { ………… } Func ia f nu are parametri şi nu returnează nici o valoare. şi anume func ia principală şi o func ie apelată f(). func iile apelate pot fi scrise în limbaj C. iar grupurile de instruc iuni pot fi delimitate prin caracterele { şi } pentru a forma unită i sintactice noi de tip bloc. dacă avem un program compus din două func ii. sau opera ii ce trebuie executate prin program. Func ia g are un parametru x de tipul int şi returnează la revenirea în programul principal o valoare flotantă în dublă precizie. 52 . Instruc iunile. pot defini tipul unor date folosite în program. compilabile separat.

2.3. aliniere la o nouă coloană. Programele C sunt formate din combina ii ale caracterelor din mul imea de caractere C constituite în instruc iuni semnificative. Fiecare caracter din mul imea caracter din mul imea caracterelor C are un în eles explicit pentru compilatorul C.3. carriage return (revenire la capătul rândului). Compilatorul dă mesaje de eroare când întâlneşte caractere întrebuin ate greşit sau caractere care nu apar in mul imii caracterelor C. Mul imea caracterelor C este o submul ime a mul imii caracterelor C reprezentabile. Dimensiunea mul imii de caractere reprezentabile depinde de tipul de terminal. În continuare sunt descrise caracterele şi simbolurile din mul imea caracterelor C şi utilizarea acestora. 3. form feed.3. Aceste litere şi cifre pot fi folosite pentru a forma constante. consolă etc. cifre. linefeed (linie nouă). Literele mari şi mici ale alfabetului englez sunt următoarele: ABCDEFGHIJKLMNOPRSTUVWXYZ abcdefghijklmnoprstuvwxyz iar cifrele zecimale: 0 1 2 3 4 5 6 7 8 9. tab-ul vertical şi newline sunt numite caractere whitespace deoarece servesc pentru spa iere între cuvinte. salt la linie nouă. Compilatorul C prelucrează litere mari şi mici în mod distinct.1. Caractere whitespace Spa iul. Mul imea caracterelor În programele C pot fi utilizate două mul imi de caractere: mul imea caracterelor C şi mul imea caracterelor C reprezentabile. constante şi identificatori. Litere şi numere Mul imea caracterelor C include literele mari şi mici ale alfabetului englez şi cifrele zecimale din sistemul de numere arabe. tab-ul. 3. Mul imea caracterelor C se compune din litere. cifrelor şi simbolurilor grafice. identificatori şi cuvinte cheie. de celelalte instruc iuni dintr-un program. Mul imea caracterelor reprezentabile este formată din totalitatea literelor. Compilatorul C ignoră caracterele whitespace dacă nu sunt folosite ca separatori sau 53 . Aceste caractere separă instruc iuni definite de utilizator.3. semne de punctua ie care au o semnifica ie specifică pentru compilatorul C.

Tabelul următor prezintă aceste caractere. Secven e escape Secven ele escape sunt combina ii speciale de caractere formate din whitespace şi caractere negrafice constituite în şiruri şi constante caracter. Caracterele whitespace sunt utilizate pentru a face programele mai lizibile.4. Comentariile sunt de asemenea tratate ca whitespace.drept componente de constante.3. Caracterele de punctua ie din setul de caractere reprezentabile C care nu apar în acest tabel pot fi utilizate numai în şiruri. Aceste caractere au o semnifica ie specială pentru compilatorul de C. 3. Caracter Nume Caracter Nume .3. . : ? ’ ” ( ) [ ] { } > < Virgulă Punct Punct şi virgulă Două puncte Semnul întrebării Apostrof Ghilimele Paranteză stânga Paranteză dreapta Paranteză dreaptă stânga Paranteză dreaptă dreapta Acoladă stânga Acoladă dreapta Mai mare Mai mic ! | / \ ~ _ # % & ^ * = + Semnul exclamării Bară verticală Slash Backslash Tilda Underscore Diez Procent Ampersand Săgeată sus Asterisc Minus Egal Plus 3. sau ca şiruri de caractere. Caractere speciale şi de punctua ie Caracterele speciale şi de punctua ie din mul imea caracterelor C sunt folosite pentru mai multe scopuri. Ele sunt în mod tipic utilizate pentru a specifica 54 .3. . constante caracter şi comentarii.

caracterul care urmează după secven a escape ar putea fi interpretat ca o parte a secven ei. 55 . forma „\c” reprezintă caracterul c într-un literal sau într-o constantă caracter. caracterul backspace poate fi scris ca „\10” în loc de „\010”.activare sunet \b caracterul BS (backspace) .ghilimele \' caracterul ' (single qoute) . Totuşi.constantă hexazecimală Backslash-ul care precede un caracter neinclus în lista de mai sus este ignorat şi acest caracter este reprezentat ca un literal.tab vertical \\ caracterul \ (backslash) \" caracterul " (double qoute) .rând nou \r caracterul CR (carriage return) .revenire la coloana 1 \t caracterul HT (horizontal tab) .constantă octală \xhh . dacă se întâmplă să fie o cifră octală sau hexagesială. De exemplu. când se utilizează secven e escape în şiruri.tab orizontal \v caracterul VT (vertical tab) . cum ar fi ghilimelele (”). este indicat să se scrie toate cele trei cifre ale secven ei. Similar.revenire cu un spa iu \f caracterul FF (form feed) . o secven ă hexagesimală poate să con ină cel pu in o cifră. secven a \0331 este interpretată drept ESC şi 1. Secven ele \ooo şi \xdd permit scrierea oricărui caracter din setul ASCII ca un număr octal format din trei cifre sau ca un număr hexagesimal format din două cifre.salt de pagină la imprimantă \n caracterul LF (line feed) . De exemplu. iar a doua cifră poate fi omisă.ac iuni precum carriage return şi tab pe terminale şi imprimante şi pentru a furniza reprezentarea caracterelor care normal au în eles special. atunci am avea o interpretare greşită. 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.apostrof \0 caracterul NULL \ooo . Altfel. Setul complet de secven e escape cuprinde: \a caracterul BEL . Dacă am scrie \331. omi ând primul zero. O secven ă escape constă dintr-un backslash urmat de o literă sau combina ii de cifre.

Plasând necorespunzător un caracter negrafic în programe C. Zero poate fi folosit ca o constantă pentru tipurile pointer. a1b2c3. să se specifice 56 . constante-şir sau enumerări. b1. Numărul de caractere care intră în componen a unui nume nu este limitat.Secven ele escape permit caractere de control negrafice pentru a fi transmise către display. Constantele pot fi: întregi. _hG. 3. Cuvintele cheie ale limbajului C În limbajul C există un număr de cuvinte care au o utilizare predefinită. Tabelul următor 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. În mod implicit. precum şi caracterul subliniere (_).5. În calitate de litere se pot utiliza literele mici şi mari ale alfabetului englez. numite cuvinte cheie.6. Fs. constantele se referă la valori fixe pe care programul nu le poate modifica. func iilor şi etichetelor utilizate în program. 3. Un nume este o succesiune de litere şi eventual cifre. adică ele să sugereze pe cât posibil scopul alegerii lor sau a datei pe care o reprezintă. … Se recomandă ca numele să fie sugestive. iar şirurile de caractere sunt de fapt constante de tip char[]. adică două nume sunt diferite dacă ele diferă în primele 32 de caractere ale lor. în virgulă mobilă sau reale. Utilizatorul nu poate să utilizeze aceste cuvinte pentru a denumi variabile sau func ii într-un program. Caracterele negrafice trebuie totdeauna reprezentate ca secven e escape. de asemenea. numai primele 32 de caractere dintr-un nume sunt luate în considerare. primul caracter fiind literă. Constante În C. Nume. nUME. Exemple de nume: a. constante-caracter. Numele sunt utilizate pentru a defini diferite variabile sau func ii într-un program C. Identificatori Identificatorii sunt nume ce sunt date variabilelor. Este posibil.4. el are rezultat imprevizibil.

ca şi în cazul constantelor întregi.23 .2e10 1. 4910 sau 3116.f sau literele mari corespunzătoare. Exemple de constante în virgulă mobilă: 123.constante simbolice. 2. orice valoare de orice tip poate fi folosită ca şi constantă prin adaugarea cuvântului cheie const la definirea sa. hexazecimale şi constante caracter. Constante întregi Constantele întregi se reprezintă în 4 forme: zecimale.. octale.23 0. 3. Compilatorul. un set de constante întregi definite ca o enumerare. Caracterele pot fi reprezentate şi prin secven e escape (de exemplu. atunci constanta '1' are valoarea 0618. 'A' şi '%' sunt constante caracter.6. iar o constantă care începe cu zero este un număr octal. 3. De exemplu.256-15 57 . Valoarea unei constante caracter este chiar valoarea numerică corespunzătoare caracterului dat în setul de caractere al maşinii.15 se folosesc literele a.. 1.23 1.6. dacă pe un calculator caracterele se reprezintă în cod ASCII.. Constante în virgulă mobilă O constantă în virgulă mobilă are tipul float..0 1. O constantă care începe cu zero urmat de x (0x) este un număr hexazecimal. 3... 'a'. Pentru a reprezenta cifrele hexazecimale 10. În C există trei feluri de constante simbolice: 1.. double sau long double.6. Constantele zecimale sunt cel mai frecvent folosite şi se reprezintă ca şiruri de cifre zecimale. orice nume de vector sau func ie. /* numărul 255 în zecimal */ /* numărul 9 în zecimal */ 3.2.3.1. oct = 011 . Nota iile octale şi hexazecimale sunt utile în exprimarea succesiunilor de bi i. prin constanta '\n' se introduce caracterul newline). De pildă. Exemplu: int int hex = 0xFF. Constante caracter O constantă caracter este un caracter inclus între apostrofuri. trebuie să semnaleze eroare în cazul în care constantele sunt prea mari pentru a putea fi reprezentate. Constantele caracter pot fi folosite în opera ii de calcul exact ca şi întregii.. O constantă simbolică este un nume a cărui valoare nu poate fi modificată în domeniul său.

marcând astfel sfârşitul său. Constante şir Constantele şir constau din caractere cuprinse între ghilimele. Dacă se doreşte o constantă de tip float.6. adică valoarea adresei loca iei în care se stochează primul caracter din şir. De exemplu.62. world\n“. este Adresa 58 .62 e . dacă avem constanta şir “ABC“ atunci. În cazul setului de caractere ASCII. În plus. iar valoarea numerică a constantei este adresa acestei memorii. 3. 17 şi se va genera o eroare de sintaxă. 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. 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). ci sunt de fapt 4 atomi lexicali: 56. spre deosebire de altele. Caracterele dintr-un şir sunt stocate în memorie.17 nu este o constantă în virgulă mobilă. -. spre exemplu. aceasta se poate defini astfel: const float pi8 = 3.Observa ie. Constantele şir. 56. constanta caracter ‘A’. au o loca ie în memoria calculatorului. vom face precizarea că din punctul de vedere al reprezentării. În interiorul constantelor întregi sau reale nu pot apare spa ii albe. compilatorul stochează caracterul null ‘\0’ la sfârşitul şirului. e. Cea mai scurtă constantă şir este şirul null scris drept “ “ şi este stocat în memorie ca un singur caracter null ‘\0’.4. pe când valoarea caracterului 0 este 48 sau 30H.14159265. Ca o ultimă remarcă. De exemplu. ca în faimosul “Hello.

3. "asaf" are tipul char[5]. determină scrierea unui mesaj. strlen(s) == sizeof(s) . 4}. instruc iunea: printf ("beep at end of message\007\n"). Atunci când se include o constantă numerică într-un şir de caractere utilizând nota ia octală sau hexazecimală este recomandat să se folosească 3 cifre pentru număr. Obiecte constante Cuvântul 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: char v1[] = "a\x0fah\0129". Constanta zero Zero poate fi utilizat ca o constantă pentru tipurile întregi. De exemplu. Cel mai frecvent caracter folosit este caracterul '\n'=newline (NL).1. deoarece func ia strlen() nu numără şi terminatorul \0.6. O secven ă de forma \n într-un şir nu determină introducerea unui caracter NL în şir. Tipul lui zero va fi determinat de context. /* 'a' 'xfa' 'd' '\127' */ 3. Nu se recomandă alocarea unui obiect la adresa zero. Aceasta face posibilă reprezentarea caracterului ghilimele (") şi \ în interiorul unui şir. sizeof("asaf") va fi 5. pentru fiecare şir s.5. În interiorul unui şir se poate folosi conven ia de nota ie cu \. ci este o simplă nota ie (\n este caracter neafişabil). v[ ] = {1. care se stochează în memorie la o anumită adresă şi se termină cu caracterul null. De exemplu. De notat că. Tipul unui şir este vector de un număr de caractere a. 59 .//'a' 'x0f' 'a' 'h' '\012' '9' char v2[] = "a\xfah\ 129".i. deci are aloca i doi octe i. Nu este permisă continuarea şirurilor de caractere de pe o linie pe alta. Şirul vid se notează prin " " şi are tipul char[1]. 2. Fiecare constantă şir con ine cu un caracter mai mult decât numărul de caractere din şir deoarece aceasta se termină totdeauna cu caracterul \0 care are valoarea 0. 3. /* 'a' 'xfa' 'h' '\12' '9' */ char v3[] = "a\xfad\127". a caracterului BEL şi a caracterului NL.6. în virgulă mobilă sau pointer.diferită de consta şir “A“.6. Exemplu : const const int int model = 145.

. defineşte 3 constante întregi denumite enumeratori şi le atribuie valori. Key de tip enum Keyword switch (Key) { case ASM: . Declarând ceva ca fiind constant. BREAK }. cât şi compilatorului.7.. defineşte o enumerare cu numele Keyword.. De exemplu.. De exemplu. BREAK }.. 3. 60 . AUTO . Declararea unei variabile Keyword în loc de int poate oferi atât utilizatorului. Dacă este aşa. break. Astfel instruc iunile: model = 165.. enum Keyword Key. ne asigurăm că valoarea sa nu se modifică în domeniul său. o constantă poate fi doar ini ializată.. ini ializatorul pentru o expresie constantă este.. De exemplu. BREAK = 2.. Numele enumerării devine sinonim cu int şi nu cu un nou tip. } determină compilatorul să ini ieze un avertisment deoarece sunt folosite numai două din cele trei valori ale lui Key.. începând cu 0.. Deoarece valorile enumeratorilor sunt atribuite implicit.. aceasta este echivalentă cu: const const const ASM = 0. aceasta poate fi evaluată în timpul compilării.6. AUTO = 1. De notat că const modifică un tip ceea ce înseamnă că restric ionează felul în care se poate utiliza un obiect.. AUTO .Deoarece nu i se poate atribui o valoare. break. Pentru o constantă.. case BREAK: . Enumerări Folosirea cuvântului cheie enum este o metodă alternativă pentru definirea constantelor întregi.... /* eroare */ /* eroare */ vor determina apari ia unor mesaje de eroare corespunzătoare. compilatorul nu rezervă memorie deoarece i se cunoaşte valoarea (precizată la ini ializare). de obicei (dar nu întotdeauna). Mai mult. //declara var.. enum Keyword {ASM . şi nu modul de alocare. o sugestie asupra modului de utilizare. ceea ce este uneori mult mai util decât utilizarea lui const. model++. o expresie constantă.. enum {ASM .. O enumerare poate avea nume.

elementele unei structuri. 6353 – este o constantă întreagă zecimală de tip int şi reprezintă un operand constant de tip int.2. produs(a.numele unei structuri. Un operator unar se aplică unui singur operand. Dacă tipul operandului este bine precizat la compilare. . float x2 – reprezintă declara ia variabilei x2.o constantă.1. Un operand poate fi: . 2. 4. iar un operator binar se aplică la doi operanzi. Unui operand îi corespunde un tip şi o valoare. . fie la execu ie.numele unei func ii. Operatorul 61 .b) – este un apel al func iei produs. în limbajul C.o expresie inclusă între paranteze rotunde. 3. 4.numele unui tip. valoarea operandului se determină fie la compilare. .Capitolul IV OPERANZI ŞI OPERATORI ÎN C 4. . 0xa13d – este o constantă întreagă hexazecimală de tip unsigned şi reprezintă un operand de tipul unsigned. este formată dintr-un operand sau mai mul i lega i prin operatori. Operanzi O expresie.numele unei variabile.elementele unui tablou. Exemple: 1. . Această func ie reprezintă un operand al cărui tip coincide cu tipul valori returnate de func ia produs. .o constantă simbolică. .numele unui tablou. . Operatori Operatorii pot fi unari sau binari în func ie de numărul de operanzi cărora li se aplică. . iar numele x2 reprezintă un operand de tipul float.

La scrierea unei expresii se pot utiliza operatori din toate clasele. C are mai multe clase generale de operatori: aritmetici. Un operand întreg presupune o promovare a întregilor. Folosirea parantezelor în expresii poate schimba preceden a între operatori în timpul evaluării acestora. iar % oferă restul împăr irii primului operand la al doilea. c. iar rezultatul este valoarea operandului. iar ai lui % trebuie să fie de tip întreg. Un operand întreg presupune promovarea întregilor.1. iar rezultatul este numărul negativ corespunzător. Preceden a ultimului grup este mai mică decât cea a operatorilor unari + şi -. în func ie de context reprezintă operatorul de înmul ire (binar) reprezintă operatorul de împăr ire (binar) reprezintă operatorul modulo (binar) Operandul operatorului unar plus trebuie să fie de tip aritmetic sau pointer.expresia -a / d este echivalentă cu (-a) / d. de asociativitatea operatorilor de aceeaşi prioritate şi de regula conversiilor implicite. rela ionali şi logici. 4. care însă este mai mică decât a grupului *.2. Exemplu: Dacă a. / şi %. . Operatorii limbajului C nu pot avea ca operanzi constante şir (şiruri de caractere). Operanzii operatorilor * şi / trebuie să fie de tip aritmetic. Operandul operatorului unar minus trebuie să fie de tip aritmetic. La evaluarea unei astfel de expresii este necesar să se ină seama de priorită ile operatorilor care apar in diferitelor clase de operatori. Pentru operanzi de tip întreg este adevărată egalitatea: (a / b) * b + a % b = a În expresii operatorii binari + şi . rezultatul este nedefinit.expresia d * b % a este echivalentă cu (d * b) % a. d sunt variabile de tip int. precum şi câ iva operatori speciali pentru sarcini particulare. în func ie de context reprezintă operatorul minus unar sau binar. Dacă al doilea operand al operatorului / sau % este zero.binar se aplică la operandul care îl precede imediat şi la care îl urmează imediat. Operatorul binar / reprezintă câtul. operatori pentru prelucrare bi i. b. atunci: .au aceeaşi preceden ă. + * / % 62 . Operatori aritmetici Lista operatorilor aritmetici este următoarea: reprezintă operatorul plus unar sau binar.

Operatori rela ionali Operatorii rela ionali permit compararea a două valori şi luarea unei decizii după cum rezultatul comparării este adevărat sau fals. este echivalentă cu a=(b=(c=(d . Operatori de incrementare şi decrementare În C. Dacă scriem: x = 10.. atunci valoarea returnată este zero. y = x++. vom găsi y=10 (mai întâi se face atribuirea lui x la y şi apoi incrementarea lui x). --j) sau ca sufix (i++. Între aceste moduri de utilizare există diferen e.2. 4. variabila i este incrementată înainte de a-i folosi valoarea. Aceşti operatori pot fi folosi i atât ca prefix pentru variabile (de exemplu.pentru decrementare cu 1. atunci vom găsi y = 11 deoarece mai întâi se incrementează x şi apoi se atribuie valoarea lui y. Operatorii rela ionali folosi i în C sunt: 63 . Exemplu: Considerăm secven a: x = 10. în expresia ++i.2. y = ++x. iar dacă este adevărat. 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 -. Dacă se afişează y. Astfel.expresia a%-b*c este echivalentă cu (a%(-b))*c. Dacă rezultatul opera iei este fals.3.expresia a=b=c=d-15 -15))).(unari) * / % + . ++i.2. 4. variabila i este incrementată după întrebuin area valorii acesteia. j--). Preceden a tuturor operatorilor aritmetici este: Înaltă Scăzută ++ -+ .(binari) Operatorii de aceeaşi preceden ă sunt evalua i de al stânga la dreapta. în timp ce în expresia i++. valoarea returnată este 1.

OR) precum şi operatorul logic unar de negare “!“ (NOT).2. astfel o expresie de forma a < b + c este interpretată ca a<(b+c).h> main () 64 . AND) şi || (SAU. Operatori logici Operatorii logici binari && (ŞI.== != < <= > >= egal diferit mai mic strict mai mic sau egal mai mare strict mai mare sau egal Operatorii rela ionali au o preceden ă mai mică decât operatorii aritmetici. 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 stânga la dreapta. Preceden a operatorilor logici şi rela ionali este următoarea: Înaltă ! > >= < <= == != && || Scăzută Astfel. conduc la valori întregi 0 şi 1. expresia: 10>5 && !(10<9) || 3<4 este adevarată. Semantica acestor operatori se deduce din tabelul următor. atunci când sunt aplica i unor expresii. cu semnifica ia fals şi adevărat. expresia: 1 && !0 || 1 este adevarată.4. 4. 1 && ! (0 ||1) este falsă. Programul următor tipăreşte numerele pare cuprinse între 0 şi 100. # include <stdio. expresia.

În scriere. i <= 100.5. §I). considerăm opera ia bit-not. SAU) şi ~ (NOT) precum şi la un al patrulea operator. dar ei tratează întregul operator ca pe o singură valoare. care folosesc nota ii dublate: &&. i). ||. } Operatorii logici şi rela ionali sunt utiliza i în formarea instruc iunilor repetitive precum şi a instruc iunii if.2. aceşti operatori se aplică în paralel bi ilor corespunzători afla i în orice pozi ie.{ int i. for (i = 0. i++) if (! (i%2)) printf ("%d" . 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. bit-or. Trebuie făcută distinc ia fa ă de operatorii logici. bit-negate sau exclusive-or. denumit SAU-EXCLUSIV ^ (EXCLUSIVE-OR). 4. | (OR. Operatori logici la nivel de bit Ne reîntoarcem la cei trei operatori de tip booleean & (AND. Ca exemplu. se mai foloseşte şi denumirea bit-and. Fie numărul 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. Din această cauză ei se mai numesc şi operatori logici pe bit. sau !. Exemplul următor 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 . Aceşti operatori se aplică la nivel de bit sau grupuri de bi i. adevărată sau falsă. Operatorii logici au aceleaşi denumiri.

acesta trebuie să aibă capacitatea să suporte to i (sau cel pu in mul i) operatorii utiliza i în asamblare. Exemplu: x = 7. Opera iile de deplasare pot fi utile când se decodifică perifericele de intrare cum ar fi convertoarele D/A (digital/analogice) şi când se citesc informa ii de stare. Forma generală a opera iilor de deplasare este: variabilă >> număr_de_pozi ii_bit . ||. Operatorii de deplasare se pot utiliza şi pentru realizarea cu rapiditate a opera iilor de înmul ire şi împăr ire.pentru deplasare stânga. opera ia AND poate fi folosită pentru ştergerea unor bi i dintr-un byte sau dintr-un cuvânt... în concordan ă cu opera ia specifică. iar o deplasare dreapta cu 1 bit realizează o împăr ire cu 2. respectiv la stânga. după deplasare.. pe când operatorii similari destina i prelucrării bi ilor pot produce orice valoare arbitrară. Aceşti operatori nu se aplică tipurilor float. iar XOR pentru complementarea unor bi i şi testarea egalită ii a 2 bytes. fie 1. variabilă << număr_de_pozi ii_bit . se introduc zerouri. void sau altor tipuri mai complexe. long double. În pozi iile rămase libere. Operatorii pentru prelucrarea bi ilor se aplică bi ilor dintr-un byte sau cuvânt.) produc totdeauna un rezultat care este fie 0. De exemplu. OR poate fi folosită pentru setarea unor bi i.Deoarece limbajul C a fost gândit să înlocuiască limbajul de asamblare în majoritatea opera iilor de programare. double. ambele variabile de tip char şi short int.pentru deplasare dreapta. Se ştie că o deplasare stânga cu 1 bit realizează înmul irea cu 2. !. Observa ie: Operatorii rela ionali şi logici (&&. pentru testarea şi mascarea anumitor bi i. Operatorii pentru prelucrarea bi ilor utiliza i în C sunt: & | ^ ~ >> << AND OR exclusive OR (XOR) complement fa ă de unu (NOT) deplasare dreapta deplasare stânga Opera iile pe bi i sunt utilizate de obicei în drivere. Operatorii >> şi << deplasează to i bi ii dintr-o variabilă la dreapta. 0 0 0 0 0 1 1 1 7 66 .

.t>0.x x x x x << << << >> >> 1. 0 0 0 . else printf("0"). De exemplu rotirea la stânga cu o pozi ie a numărului 10101010 ne conduce la numărul 01010101 şi se realizează după schema: 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 67 . printf("\n").}} void disp_binary(int i) /* se defineste functia disp_binary() */ /* care afiseaza bitii dintr-un byte */ {register int t. 0 0 0 . .t<8.t++) { i=i>>1. /* prototipul functiei disp_binary() */ void main() { int i = 1. 2. 0 0 0 . . i=i<<1. . 0 0 0 .t=t/2) if (i&t) printf("1"). for (t=128. 1 1 0 .} printf (" \n"). 1. . t. 3. 0 0 1 .h> void disp_binary(). 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 Următorul exemplu eviden iază efectul operatorilor de deplasare: # include <stdio. 0 1 0 .} Programul produce următoarea ieşire: 0 0 . 2. . disp_binary(i). . 0 0 0 . 0 0 0 . for (t=0. se poate realiza o func ie care să efectueze această opera ie. .t<8. 0 0 0 . 0 0 0 . 0 0 1 . 1 Deşi limbajul C nu con ine un operator de rotire. 0 0 0 . 0 0 0 . for (t=0.t++) { disp_binary(i). 0 0 0 .

rot->i=rot->i<<1. for (t=0. rot. Dacă este 1.i). for (t=128. if (rot->ch[1]) rot->i=rot->i|1.t++) { disp_binary(rot. Următoarea func ie realizează o rotire cu 1 bit.} /* se defineste functia disp_binary() */ void disp_binary(int i) {register int t. void rotate_bit(). printf("\n"). unsigned int i. Numărul de rotit se introduce (pe 8 bi i) în ch[0]. void disp_binary(). void main() { register int t.h> union rotate { char ch[1]. Se roteşte apoi întregul i (deci se rotesc to i cei 4 octe i care îi corespund). void rotate_bit(union rotate *rot) {rot->ch[1]=0. } rot.ch[0]=147. else printf("0").t>0. Un exemplu de program care să utilizeaze această func ie: # include <stdio.} Atât întregul i cât şi cele două caractere ch[0] şi ch[1] partajează primii doi octe i din cei 4 rezerva i de uniune. unsigned int i.t=t/2) if (i&t) printf("1"). rot->i=rot->i<<1. if (rot->ch[1]) rot->i=rot->i|1. De exemplu utilizând uniunea: union rotate { char ch[1].O posibilitate de realizare a opera iei de rotire necesită utilizarea unei uniuni cu două tipuri de date diferite. rotate_bit(&rot).t<7.}} /* se defineste functia rotate_bit() */ void rotate_bit(union rotate *rot) {rot->ch[1]=0. } rot.} 68 . realizându-se astfel opera ia de rota ie. Se testează MSB al lui ch[0] care se găseşte în urma rotirii în pozi ia LSB din ch[1]. atunci se setează la 1 LSB din ch[0].

} rot. void main() { register int t.t++) { disp_binary(rot. do {ch = getch().} 69 . if (rot->ch[2]) rot->i=rot->i|1. else printf("0").} Operatorul " ~ " realizează complementul fa ă de 1. unsigned int i.t>0.h> union rotate { char ch[3]. atunci se poate modifica programul de mai sus după cum urmează: # include <stdio. O utilizare interesantă a complementului fa ă de 1 este aceea că ne permite să vedem setul caracterelor extinse implementate în calculator: # include <stdio. for (t=0.} while (ch != 'q').Acest program realizează rotirea numărului 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). rot->i=rot->i<<1. Dacă dorim să facem o rotire pe doi octe i.i). rot. printf ("%c %c\n".t<7.h> void main() {char ch. void disp_binary().i=17843. for (t=32768. ch. ~ch). rotate_bit(&rot).} /* se defineste functia disp_binary() */ void disp_binary(int i) {register int t.}} /* se defineste functia rotate_bit() */ void rotate_bit(union rotate *rot) {rot->ch[2]=0.t=t/2) if (i&t) printf("1").h> # include <conio. printf("\n"). void rotate_bit().

Operatorul de atribuire În C.h> 70 . rezultatul este numărul total de octe i din tablou. -. atunci evaluarea se face de la dreapta la stânga: x = y = z = 0 este echivalentă cu (x=(y=(z=0))).2. *= . Dacă într-o expresie se fac mai multe atribuiri. O expresie de atribuire de forma x = x + 5 în care variabila din stânga apare imediat după operatorul = se poate scrie într-o formă compactă de tipul x += 5. ^ . forma: suma = a + b + c. &= . &. *. /= . >>= . Dacă sizeof operează asupra unui tip de date.h> # include <stddef. fără semn. Exemplul din programul următor prezintă dimensiunea principalelor tipuri de date: # include <stdio. De exemplu. unde operatorul += este tot un operator de atribuire. Majorită ii operatorilor binari le corespund operatori de atribuire de forma "op = " unde op poate fi : +. -= . += .7.h defineşte tipul size_t al rezultatului oferit de operatorul sizeof.6. operatorul de atribuire (asignare) este semnul egal (=). Operatorii de atribuire (asignare) sunt: = .. ^= . o expresie de asignare de forma : var = (var) op (expr) unde var este o variabilă şi expr este o expresie. Dacă sizeof se aplică unui tablou. În C. %= . trebuie privită ca o nouă expresie. numită expresie de asignare. >>. ca mai sus.2.4. deci rezultatul este un număr întreg. <<. sizeof(int) va fi 4 etc. |= Deci. %. sizeof(char) va fi 1. Operatorul sizeof Operatorul sizeof returnează numărul de octe i necesar memorării variabilei sau tipului datei care este operandul său. <<= . var este evaluată o singură dată. Fişierul standard stddef. 4. atunci tipul trebuie să apară între paranteze. Valoarea expresiei din dreapta se atribuie variabilei din stânga operatorului "=". Valoarea ei este chiar valoarea expresiei din dreapta operatorului de atribuire. admite o reprezentare compactă de forma: var op= expresie Într-o formă compactă.

void main(){ printf("\nTip printf("\nTip int)).8. Dacă este adevărată.sizeof(char)). se evaluează Expr2. float pe %d octeti". double pe %d octeti". Operatorul ternar ? Operatorul " ? " poate fi utilizat pentru a înlocui instruc iunea if / else având forma: if (conditie) expresie1 else expresie2 Operatorul ternar " ? " necesită trei operanzi şi are forma generală: Expr1 ? Expr2 : Expr3 unde Expr1.sizeof(long int)). if (x > 9) y = 100. 71 . se va afişa (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.sizeof(double)). care devine valoarea întregii expresii. se evaluează Expr3.sizeof(int)). long int pe %d octeti". valoarea lui y va fi 100. long double pe %d octeti\n". iar valoarea acesteia devine valoarea întregii expresii: Exemplu: x = 10.2. Se evaluează expresia Expr1. Cum 10 > 9. Expr2 şi Expr3 sunt expresii. short int pe %d octeti". printf("\nTip printf("\nTip printf("\nTip printf("\nTip printf("\nTip double)).} caracter pe %d octet". Dacă Expr1 este falsă. else y = 200. y = x > 9 ? 100 : 200.sizeof(short int pe %d octeti". Dacă x ar fi mai mic decât 9. Acelaşi program scris cu if /else va fi: x = 10. sizeof(long În urma execu iei acestui program.sizeof(float)). y va primi valoarea 200.

atunci va fi apelată printf() şi va afişa " S-a introdus zero". y+1).5. } f2(int n) {printf ("%d\n". pentru ca atribuirile să se facă corect. f2(). Variabila x va căpăta valoarea 6. printf (": "). În construc ia de mai sus (tip) se consideră că este un operator unar. // prototipurile functiilor f1() si f2() void main() { int t. atunci programul va executa atât func ia f1(). cât şi func ia f2(). Dacă se introduce alt număr. // se introduce numarul intreg t t?f1()+f2(t): printf(" S-a introdus zero\n").&t).2. valoarea atribuită variabilei din stânga operatorului de atribuire este valoarea ultimei expresii din dreapta.} f1() {printf ("S-a introdus "). Acest lucru este posibil folosind o construc ie de forma: (tip) operand Printr-o astfel de construc ie valoarea operandului se converteşte spre tipul indicat în paranteze. Exemplu: y = 10. Observa ie Deoarece operatorul virgulă are o preceden ă mai mică decât operatorul de atribuire. Operatorul de for are a tipului sau de conversie explicită (expresie cast) Adesea se doreşte specificarea conversiei valorii unui operand spre un tip dat.10. trebuie utilizate paranteze. după evaluare. are că efect atribuirea valorii 4 variabilei x. Acest operator este 72 . Dacă se utilizează un operator de atribuire. x = (y = y .h> f1().} Dacă se introduce zero.2. Deci expresiile separate prin virgulă sunt evaluate de la stânga la dreapta. 4.În alcătuirea expresiilor din declara ia operatorului ternar " ? " pot fi folosite şi func ii: Exemplu: # include <stdio. scanf("%d". Operatorul virgulă Operatorul virgulă se utilizează într-un şir în care se introduc mai multe expresii. prima expresie evaluată căpătând valoarea void. Astfel. instruc iunea: x = (y = 3. n). 4.9. 30 / y).

În acest caz se obişnuieşte să se spună că parantezele rotunde sunt operatori de apel de func ie. Parantezele pătrate include expresii care reprezintă indici. O expresie inclusă în paranteze rotunde formează un operand. Pentru ca această func ie să poată fi apelată cu un parametru int n (n este un parametru de tip întreg) acesta trebuie mai întâi convertit la tipul double. 4.2. Operatorii paranteză Parantezele rotunde se utilizează fie pentru a include o expresie. De exemplu. 4. Ele se numesc operatori de indexare. lista parametrilor efectivi se include între paranteze rotunde.2.11. Operatorul adresă Operatorul adresă este unar şi se notează prin caracterul &. La apelul unei func ii. În acest mod se poate impune o altă ordine în efectuarea opera iilor.cunoscut sub numele de operator de for are a tipului sau de conversie explicită. Astfel construc iile: (a-5+b)++ --(a+b) &(a*b) sunt eronate. El se aplică pentru a determina adresa de început a zonei de 73 . la un astfel de operand nu se pot aplica operanzii de incrementare şi decrementare sau operatorul adresă.12. fie la apelul func iilor. Exemplu: Presupunem că o func ie oarecare f are un parametru de tip double. Operanzii ob inu i prin includerea unei expresii între paranteze impun anumite limite asupra operatorilor. are aceeaşi prioritate ca şi ceilal i operatori unari ai limbajului C. De cele mai multe ori însă este utilizată denumirea engleză a operatorului şi anume expresie cast. Operatorii unari au prioritatea imediat mai mică decât parantezele. 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. Parantezele sunt operatori de prioritate maximă. decât cea care rezultă din prioritatea şi asociativitatea operatorilor.

În cazul în care nume este numele unui tablou. iar tipul rezultatului coincide cu tipul comun al operanzilor. Operatorii „ .13. În acest caz. Al i operatori ai limbajului C În limbajul C se mai utilizeză şi operatorii: „ * ” . Dacă operatorul curent se aplică la operanzi de acelaşi tip.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. iar operatorul „ * ” este operator de dereferen iere. Ei au prioritate maximă.memorie alocată unei date. atunci rezultatul este eronat (are loc o „depăşire”).14. având aceeaşi prioritate cu parantezele. Se obişnuieşte să se spună că operatorul de adresă & este operator de referen iere. Exemplu: Rezultatul împăr iirii 7/3 este 2 şi nu 2. Dacă operatorul binar se aplică la operanzi de tipuri diferite. nu se mai utilizează operatorul adresă &. operandul de tip inferior se converteşte spre tipul superior al celuilalt operand şi rezultatul este de tip superior. ” şi „ -> ” se utilizează pentru a se accesa componentele unei structuri. Ea ac ionează atunci când un operator binar se aplică la doi operanzi de tipuri diferite. Regula conversiilor implicite şi preceden a operatorilor Regula conversiilor implicite se aplică la evaluarea expresiilor. Î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. 74 . 4. ” ş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. atunci celălalt operand se converteşte spre tipul long double iar tipul rezultatului aplicării operatorului este de asemenea de tip long double. conform algoritmului umător: 1. Înainte de toate se convertesc operanzii de tip char şi enum în tipul int. atunci se execută operatorul respectiv. Dacă rezultatul aplicării operatorului reprezintă o valoare în afara limitelor tipului respectiv. atunci se face o conversie înainte de execu ia operatorului. în acest caz.2. Dacă unul din operanzi este de tip long double.2. „ . atunci acesta are ca valoare chiar adresa de început a zonei de memorie alocată tabloului respectiv şi. 4.

3. Altfel. Ei se asociază de la stânga la dreapta. Preceden a Înaltă Operatorul () [ ] -> . Altfel. Scăzută 75 . 6. Preceden ele operatorilor C sunt prezentate în tabelul următor.. dacă unul din operanzi este de tip unsigned long atunci celălalt operand se converteşte spre tipul unsigned long iar tipul rezultatului aplicării operatorului este de asemenea de tip unsigned long. condi ionali şi de atribuire. iar tipul rezultatului aplicării operatorului este de tip unsigned. Altfel. celălalt de tip int şi acesta se converteşte spre tipul unsigned. dacă unul din operanzi este de tip long atunci celălalt operand se converteşte spre tipul long iar tipul rezultatului aplicării operatorului este de asemenea de tip long. 5.(type) * & sizeof * / % + << >> < <= > >= == != & ^ | && || ?: = += -= *= /= . dacă unul din operanzi este de tip double atunci celălalt operand se converteşte spre tipul double iar tipul rezultatului aplicării operatorului este de asemenea de tip double. exceptând operatorii unari. ! ~ ++ -. Operatorii afla i pe aceeaşi linie au aceeaşi prioritate. Altfel. care se asociază de la dreapta la stânga.2. Altfel. 4. unul din operanzi trebuie sa fie de tip unsigned. dacă unul din operanzi este de tip float atunci celălalt operand se converteşte spre tipul float iar tipul rezultatului aplicării operatorului este de asemenea de tip float.

set care îi permite să realizeze principalele compuneri de opera ii: secven ierea. Etichetele sunt locale în corpul func iei în care sunt definite. se realizează un salt la instruc iunea prefixată de eticheta aflată după instruc iunea goto. Etichetele nu pot fi redeclarate. singura utilizare a sa fiind ca destina ie a unei instruc iuni goto. ieşirea prematură dintr-un ciclu. instruc iuni de salt. repeti ia cu test ini ial. saltul necondi ionat. decizia şi selec ia. instruc iuni expresie. 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.Capitolul V INSTRUC IUNI Limbajul C posedă un set variat de instruc iuni. instruc iuni repetitive. deci. Deoarece o etichetă este locală în corpul unei func ii rezultă că ea este nedefinită în afara corpului func iei respective. Instruc iunile pot fi clasificate în: instruc iuni etichetate. 5. repeti ia cu test final. o instruc iune goto nu poate face salt la o instruc iune din afara corpului func iei în care este definită. repeti ia cu număr cunoscut de paşi. Instruc iuni etichetate (instruc iunea goto) Instruc iunile etichetate posedă etichete ca prefixe şi au forma: etichetă: instruc iune Eticheta formată dintr-un identificator defineşte identificatorul ca destina ie pentru o instruc iune de salt.1. Instruc iunea goto are următorul format: goto etichetă La întâlnirea instruc iunii goto. Instruc iunea goto se utilizează în special pentru ieşirea din mai multe cicluri imbricate. instruc iuni de selec ie. instruc iuni compuse. Exemplu: Următorul program utilizează instruc iunea goto pentru a afişa numerele de la 1 la 100: 76 .

unde expresie este op ională. x2. DE AUTOMATICA"). Exemplu: a = b * c + 3. Identificatorii utiliza i într-un bloc pot fi ascunşi prin declara ii de acelaşi nume în blocurile interioare blocului ini ial. eticheta: printf(”%d”. } 5. printf ("FAC. } O instruc iune compusă se numeşte bloc. nr++).h> int x = 34. Exemplu: # include <stdio. 5.h> void main(void) { int nr=1.3. Exemplu: { a = b + 2. dar este utilizată pentru a înlocui un corp vid al unei itera ii sau pentru a plasa o etichetă. x). b++. construc ia “. care au forma: [ expresie ]. Un bloc ne permite să tratăm mai multe instruc iuni ca pe una singură. Dacă expresia expresie de mai sus lipseşte. o instruc iune expresie constă dintr-o expresie urmată de caracterul ". Corpul unei func ii este o instruc iune compusă.#include <stdio. Domeniul de vizibilitate al unui identificator declarat într-un bloc se întinde din punctul declara iei până la sfârşitul blocului.". Majoritatea instruc iunilor expresie sunt atribuiri sau apeluri de func ii. if (nr<=100) goto eticheta. printf("x = %d\n". Deci. Instruc iuni compuse O instruc iune compusă este o posibilă listă de declara ii şi/sau instruc iuni închise între acolade.“ se numeşte instruc iune vidă. /* x este global */ void main(void) { int *p = &x. 77 . /*p preia adresa variabilei globale*/ int x1.2. Aceasta nu are nici un efect. Instruc iuni expresie Cele mai multe instruc iuni sunt instruc iunile expresie.

Instruc iunea if O instruc iune if cu care în C se implementează o structură de control de selec ie sau o structură alternativă. } 5. prin instructiune1 sau instructiune2 se în elege o instruc iune simplă. &x. Dacă valoarea expresiei este diferită de zero (condi ie adevărată).x). y). în acest fel putânduse ob ine o structură de selec ie cu o ramură vidă de forma: if (conditie) instructiune. /*x este local si il ascunde pe cel global */ x = 1. printf("x = %d\n". scanf ("%d %d". } printf("x = %d %d %d \n". dacă valoarea expresiei este zero (condi ie falsă). else instructiune2. se execută instructiune2.4. /* atribuirea se face la cel local */ x1 = x. este op ională. după executarea lui instructiune1 sau instructiune2.x2).1. Instruc iuni de selec ie 5.} { int x. unde conditie este orice expresie care prin evaluare conduce la o valoare întreagă. atunci se execută instructiune1. printf("Introduceti doua numere intregi: \n"). Por iunea else instructiune2.x2). &y). if (x > y) printf ("Cel mai mare este : %d\n". altfel. În ambele cazuri. /* se atribuie valoarea 2 acestui x */ x2 = x.x1. apare o ambiguitate atunci când else este omis dintr-un if inclus (încuibat). /* se ascunde prima variabila locala */ x = 2. o instruc iune compusă (un bloc) sau o instruc iune vidă. x1). } Deoarece partea else dintr-o instruc iune if este op ională. printf("x = %d\n". # include <stdio. y. else printf("Cel mai mare este : %d\n".h> void main (void) { int x. 78 .4.x.{int x. Aici. controlul este transferat la instruc iunea ce urmează după if. Exemplu: Următorul program citeşte două numere şi afişează pe cel mai mare dintre ele. are următorul format general: if (conditie) instructiune1.

. astfel: if (x) { if (y) printf ("1"). structura putându-se încheia după secven a notată cu instructiuneN. O asemenea construc ie este de forma: if (conditie1) instructiune1. Instruc iuni de selec ie multiplă: if . .2. . după care controlul este transferat la instruc iunea următoare din program. . . 79 . . folosindu-se perechi else if. . . Dacă dorim ca else să fie asociat cu if(x) trebuie să utilizăm acolade. else if (conditie2) instructiune2. else este asociat cu instruc iunea if(y). else . else if (conditie3) instructiune3.4. . În acest caz. . . 5. printf ("2"). pe o ramură. atunci se execută secven a corespunzătoare ultimei alternative introdusă prin else. De exemplu. Dacă una din ele este adevărată. else if (conditieN) instructiuneN.} else printf ("2"). . . . else instructiuneN+1. o altă instruc iune if.În C acest lucru se rezolvă prin asocierea lui else cu cel mai apropiat if. atunci este executată instruc iunea corespunzătoare. condi iile sunt testate în ordine. . . } else printf ("2"). Secven a anterioară este echivalentă cu: if (x) { if (y) printf ("1"). Dacă nici una dintre expresii nu este adevărată. Această ultimă alternativă nu este obligatorie. în secven a: if (x) if (y) else printf ("1").else if Într-o instruc iune if se poate include. În acest fel se creează posibilitatea de a codifica structuri de selec ie multiplă. Codul pentru fiecare alternativă poate fi format dintr-o instruc iune simplă (inclusiv instruc iunea vidă) sau dintr-un bloc delimitat prin { şi }.

pe rând. float x.} else if(ch == 'c') { in = x/fact. .h> void main(void) { const float fact = 2.3. Forma generală a instruc iunii switch este: switch (variabila) { case constanta1 : secventa_instructiuni_1 break. .4.cm). . . .2f cm \n".h> # include <conio. . . } else in = cm = 0. /* se introduce un caracter de la tastatura care se afiseaza pe ecran */ if (ch == 'i') { in = x. . . . .in. ch = getche().cm. .2f in = %5.&x). cm = x. . printf ("\nIntroduceti numarul: \n").in. case constanta2 : secventa_instructiuni_2 break. case constantaN : secventa_instructiuni_N break. } 5. Presupunem că indicăm unitatea intrării cu i pentru inch şi c pentru centimetru: # include <stdio. o valoare cu constantele dintr-o mul ime şi în momentul găsirii unei coinciden e se execută instruc iunea sau blocul de instruc iuni asociate acelei constante. .Exemplu: Considerăm un program care realizează conversiile inch-cm şi cm-inch. se compară. printf("\n%5. . default : secventa_instructiuni_N+1 } 80 . . . char ch = 0. . Instruc iunea switch Într-o instruc iune de selec ie switch. printf("\nIntroduceti unitatea: \n"). scanf("%f".54. cm = x * fact. . case constanta3 : secventa_instructiuni_3 break.

break. case 'c': in = x/fact. case 3 : 81 . cm. ch = getche().h> void main(void) { const float fact = 2. } printf("\n%5.2f in = %5.h> # include <conio. break. iar dacă default lipseşte. deoarece prezen a acesteia este op ională. char ch = 0. switch(ch) { case 'i': in = x. Pentru a ieşi din instruc iunea switch se foloseşte instruc iunea break. Dacă nu se găseşte nici o coinciden ă. printf ("\nIntroduceti numarul: \n"). se trece la instruc iunea următoare. break. scanf("%f".2f cm \n".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. după care se trece la instruc iunea imediat următoare după switch. t < 10. default: in = cm = 0. Secven a de instruc iuni se execută pâna se întâlneşte break. se execută secven a de instruc iuni de după default. break. &x). for (t = 0.h> void main (void) { int t. printf("\nIntroduceti unitatea: \n"). break. in. cm = x * fact.54. Exemplu: # include <stdio. t++) switch (t) { case 1 : printf ("Now"). Exemplu: Decizia din exemplul anterior poate fi realizată şi astfel: # include <stdio. float x. cm = x.cm). case 2 : printf (" is ").in. } Observa ie: Constantele case trebuie sa fie distincte.

} unde: 82 . ciclul se continuă. x <= 100. for (x = 1.5. time for all good men \n"). # include <stdio. to "). 5. Instruc iunea for Forma generală a instruc iunii for este: for (initializare.case 4 : printf (" printf (" break.h> void main (void) { int x.1. } the ").initializare este o instruc iune de atribuire utilizată pentru ini ializarea variabilei de control a ciclului. . } Rulând acest program.5. dacă condi ia este falsă (egală cu 0). x++) printf("%d ". Nu există nici o restric ie privitoare la tipul său. 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 afişează pe ecran o listă de op iuni (un meniu) din care utilizatorul alege câte una şi o execută. x). Instruc iunile switch pot fi şi incluse (încuibate) una în alta. incrementare) instructiune. case 5 : case 6 : printf (" break. Exemplu: Următorul program afişează pe ecran numerele de la 1 la 100. conditie.condi ie este o expresie rela ională care se testează înaintea fiecărei itera ii: dacă condi ia este adevărată (diferită de 0). case 7 : case 8 : case 9 : printf (" break.incrementare se evaluează după fiecare itera ie specificând astfel reini ializarea ciclului. . instruc iunea for se încheie. . . . Instruc iuni repetitive 5.

afişând subşirurile ce au ca bază primul caracter. i++) { printf (" Acesta este i : %3d". } 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.h> void main (void) 83 . Putem crea cicluri for în care variabila de control se decrementează. x <= 100. i <= n. Exemplu: Următorul program afişează pe ecran numerele de la 0 la 100 din 5 în 5: # include <stdio. # include <stdio.h> void main (void) { int x. i*i).Nu întotdeauna ciclul for trebuie să se desfăşoare în sensul creşterii variabilei de control. factorial = 1. printf ("Introduceti n : "). for (i = 0. for (i = 1.n # include <stdio.h> # include <string.h> void main (void) { int x.. x--) printf("%d". Exemplu: Programul următor afişează numerele de la 100 la 1. precum şi pătratul acestora: # include <stdio. printf (" %d ! = %ld \n". x > 0. i). printf (" si i patrat : %5d \n".h> void main (void) { int n. n. i < 100. long int factorial.. scanf ("%d". # include <stdio. i. factorial). x = x + 5) printf ("%d". i++) factorial *= i.h> void main (void) { int i. for (x =100. x). Exemplu: Programul următor parcurge un şir de caractere de la stânga la dreapta. Exemplu: Programul următor afişează pe ecran numerele de la 0 la 99. } } Exemplu: Calculul factorialului unui număr: n! = 123. for (x = 0. &n). } Instruc iunile for pot fi incluse una în alta. } Nu există restric ii în incrementarea sau decrementarea variabilei de control a ciclului. x).

n. terminare = getchar(). ci condi ia de terminare a ciclului poate fi orice expresie C corectă. for (x = 0. char sir[81].&raspuns). if (raspuns != i+j) printf("\nGresit !").h> # include <conio. x + y). for (n = 0. Exemplu: Considerăm un program pentru antrenarea unui copil în exerci iile de adunare. În exemplul următor. x++. atât x cât şi y sunt variabile de control a ciclului: # include <stdio. Una din cele mai utilizate variante constă în folosirea a mai multe variabile de control a ciclului. gets (sir). x + y < l00.h> void main (void) { int x. Dacă copilul vrea să se oprească se apesa tasta T. i<100. putchar ('\n'). l = strlen (sir). raspuns. i < n. atunci când calculatorul îl întreabă dacă să continue.i. y = 0.h> void main (void) { int i. char terminare = ' '. i++) { for (j=1. j. y++) printf ("%d ". scanf("%d".{ int l. Terminarea ciclului presupune testarea nu numai a variabilei de control cu anumite valori prestabilite. j<100 && terminare !='t'. # include <stdio. y.j). printf(" Continuam ? "). puts ("Tastati un sir terminat cu <CR> : ").j++) { printf ("\nCit este %d + %d ? ". } /* Pentru terminare se apasa tasta t */ } } 84 . n <= l. i. ++n) { for (i = 0. Se observă că ini ializările şi incrementările celor două variabile sunt separate prin virgulă. } Acest program tipăreşte numerele de la 0 la 98 din 2 în 2. } } Variante ale ciclului for: Limbajul C permite mai multe variante ale ciclului for care determină creşterea flexibilită ii acestuia în diferite situa ii. else printf("\nCorect !"). for (i=1. ++i) putchar (sir[i]).

Deoarece instruc iunea de incrementare a lui x lipseşte.h> void main (void) { for (.O altă caracteristică interesantă a ciclului for este aceea că nu se impune definirea tuturor celor trei parametri ai ciclului for. factorial = 1. programul testează ca x să fie egal cu 123. long int factorial. } printf (" Ai apasat tasta A "). x != 123. Ieşirea dintr-o bucla for: Pentru terminarea unei bucle for. i <= n. .) scanf("%d". . Exemplu: O variantă de calcul a lui n! ar fi următoarea: # include <stdio. for (i = 1. dar nu modifică pe x în nici un fel. &x). de fiecare dată când ciclul se repetă. n. se ob ine o buclă fără sfârşit. printf (" %d ! = %ld \n".) printf ("Aceasta bucla va rula la nesfirsit.) se foloseşte instruc iunea break care se plasează oriunde în corpul ciclului şi determină încheierea imediată a ciclului (la întâlnirea acesteia). ca în exemplul următor în care se consideră că elementul condi ie are valoarea adevărat: for (.) { ch = getche (). Dacă de la tastatură se introduce 123. Exemplu: Acest program va rula până când de la tastatura se apasă tasta A: # include <stdio. &n).h> void main (void) { int n. \n "). De exemplu. chiar şi a buclei for(. i. if (ch == 'a') break. } } Bucle infinite: Una din cele mai interesante utilizări ale ciclului for constă în crearea de bucle infinite. factorial). condi ia buclei devine falsă şi ciclul se termină. oricare dintre ei putând fi op ionali.) { factorial *= i ++. ciclul următor se va executa până când de la tastatura se introduce numărul 123: for (x = 0. scanf ("%d". } 85 . printf ("Introduceti n : "). programul continuându-se cu instruc iunea ce urmează după instruc iunea for. Dacă nici una din cele trei expresii care formează ciclul for nu sunt precizate..h> # include <ctype.

86 .5. x). t++).d. pentru o pereche x. o instruc iune simplă sau un bloc de instruc iuni ce vor fi executate repetitiv.c. y-x). printf (" Introduceti doua numere pozitive: \n").d.m. y de numere întregi pozitive. xi. yi. y = yi. De exemplu.m. atunci cmmdc (x.h> void main (void) { int xi. printf (" C. x = xi. y. } Metoda de calcul se bazează pe faptul că: ♦ daca x > y. Exemplu: Programul următor calculează c. # include <stdio. atunci instructiune se execută. atunci controlul este transferat la instruc iunea ce urmează după while. t < O_ANUMITA_VALOARE. y) = cmmdc (x-y.m. Instruc iunea while Forma generală a instruc iunii repetitive while este: while (conditie) instructiune. scanf ("%d %d". ♦ daca x = y. cmmdc (14. evident dacă condi ia nu este adevărată. %d) = %d". după o evaluare (inclusiv prima) rezultă o valoare 0 (condi ie falsă).m. x). x. cât timp valoarea asociată condi iei este diferită de 0 sau condi ia este adevărată. instruc iunea for este legată de parcurgerea unor structuri de date de tip tablou. Astfel. 21) = 7. y) = cmmdc (x. Deoarece instruc iunea while realizează testarea condi iei la începutul instruc iunii.c. Observa ie: De obicei. yi. 5. În timpul execu iei se evaluează mai întâi condi ia buclei a cărei valoare trebuie să fie întreagă. Dacă. atunci cmmdc (x. &xi. aceasta instruc iune este bună de utilizat în situa iile în care nu se doreşte execu ia buclei.2. else y -= x. Dacă valoarea calculată este diferită de 0 (condi ie adevărată). ♦ daca x < y. unde instructiune poate fi o instruc iune vidă. &yi). atunci cmmdc (x. while (x != y) if (x > y) x -= y. y) = x =y . instruc iunea asociată cu while se execută repetat.Utilizarea ciclurilor for fără corp (instruc iune) : Pentru crearea unor întârzieri de timp se pot folosi cicluri for cu corp vid de forma: for (t = 0. (%d.

while (lung > 0) { printf (" "). printf(" Introduceti un sir de caractere: \n"). Reamintim că instruc iunea for se foloseşte după următorul format general: for (initializare. condi ia se evaluează după execu ia secven ei 87 . Corpul ciclului while poate con ine şi numai instruc iunea vidă. while (conditie) { instructiune. centreaza (strlen (sir)). conditie. lung--. } } centrarea Dacă dorim să programăm un ciclu infinit. incrementare. { lung = (80 .lung)/2. se asigură prin mecanisme de tip break. De exemplu.5. în cazul folosisii mecanismului do-while. } 5. goto sau return.h> # include <ctype. care este echivalentă semantic cu secven a: initializare. incrementare) instructiune.Exemplu: Programul următor realizează centrarea unui text pe ecran: # include <stdio. în acest caz.3. } /* Se calculează numărul de spa ii pentru unui şir de caractere cu lungimea lung */ centreaza (lung) int lung. atunci se poate găsi o expresie care ramâne tot timpul adevărată. gets (sir). printf (sir). Un exemplu uzual este următorul: while (1) { Corpul ciclului } Ieşirea din ciclu. Observa ie: Instruc iunea while reprezintă mecanismul sintactic de bază pentru a programa cicluri în C. Instruc iunea do-while Spre deosebire de ciclurile programate cu while sau for. while ((ch = getche ()) != 'A').h> void main (void) { char sir[255]. este o buclă simplă care se execută până când de la tastatură se va introduce caracterul "A". unde condi ia de ciclare este verificată la început.

Afisarea erorilor de ortografie \n "). printf ("2. } while (conditie). ch=getche(). num). în caz contrar se execută corpul ciclului şi se reevaluează condi ia. un intreg între 1 si 10: "). while (conditie) instructiune. } 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 afişat pe ecranul terminalului. atunci controlul se transferă la următoarea instruc iune din program. do-while este echivalentă cu secven a: instructiune. Deşi acoladele nu sunt necesare când instructiune este o instruc iune simplă. Forma generală a buclei do-while este: do { instructiune. do { printf ("\n Introduceti optiunea dumneavoastra: ").h> void main (void) { char ch. o instruc iune simplă. Corectarea erorilor de ortografie \n"). Semantic. Celelalte execu ii sunt condi ionate de valoarea întreagă rezultată din evaluarea condi iei. de obicei se utilizează pentru a evita confuzia cu while. printf (" Numarul introdus este : %d ". do { printf("\n\nIntrod. &num). Dacă această valoare este 0 (condi ie falsă). printf ("1.h> # include <ctype. printf ("3.h> void main (void) { int num. Verificarea ortografiei \n "). Exemplu: Următoarea secven ă asigură preluarea corectă a unei valori întregi între 1 şi 10: # include <stdio. scanf ("%d". Exemplu: Următorul program implementează o versiune a unui meniu de verificare a corectitudinii ortografice într-un text: # include <stdio.de instruc iuni ce reprezintă corpul ciclului. } while (num < 1 || num > 10). Se remarcă faptul că instructiune ce reprezintă corpul ciclului (adică. // Se citeste optiunea de la tastatura switch (ch) { case '1': 88 . o instruc iune compusă sau o instruc iune vidă) este executată cel pu in odată.

Bucle încuibate Când o buclă este introdusă în altă buclă. programul va bucla până când se va selecta o op iune validă. . for (i = 1.h> void main (void) { int i. . . . k. printf (" i i^2 i^3 i^4 \n "). break. . . . 5. . } } Când se execută acest program se ob in următoarele rezultate: i i^2 i^3 i^4 1 1 1 1 2 4 8 16 3 9 27 81 .4. i++. . j. } printf (" \n "). break. . . bucla interioară se spune a fi inclusă (nested. } while (i < 10). . p. . } } while (ch != '1' && ch != '2' && ch != '3'). i++) { for (j = 1. . i < j. Exemplu: Programul următor afişează primele 4 puteri ale numerelor cuprinse între 1 şi 9: # include <stdio. 9 81 729 6561 89 . printf (" %9d ". p). break. încuibată) în bucla exterioară. j++) { p = 1. i < 5. do { c[i] = a[i] + b[i]. . . c[10]. case '2': corecteaza_erorile(). . . k++) p = p * i. } while (i < 10). } După afişarea op iunilor. i < 10. case '3': afiseaza_erorile().verifica_ortografia(). . . sau i = 0. i = 0. . . for (k = 1.5. . b[10]. . Exemplu: Adunarea elementelor a doi vectori: int a[10]. i = i + 1. do { c[i] = a[i] + b[i].

%d) = ". s.i. /* Introducem pe rand dimensiunile fiecarei matrici. j. a[i][j] = elem.cb=ca. printf("Program de inmultire a doua matrici\nSe declara dimensiunile fiecarei matrici\n\n"). cb. k. lb. scanf("%d". linii matrice B = \n").&lb).%d) = ". lc.&ca). ca.} } /* Se introduc matricile */ for(i=0. coloane matrice A = \n"). i<=la-1. 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"). scanf("%d". // Program de inmultire a doua matrici # include <stdio. linii matrice A = \n"). i++) for(j=0.j++) { printf("b(%d. j). } while ((lb>=101)||(cb>=101)) { printf("Intoduceti dimens. Evident. cb=ca.j). pu in mai complex. i.Alinierea rezultatelor se datoreşte utilizării în printf() a unui format de afişare corespunzător (%9d) care precizează dimensiunea minimă a câmpului specificat. j<=ca-1. ca=101. 90 . printf("Nr.ca=101. cc.h> float a[100][100]. scanf("%f". int la. scanf("%d".&la).i<=lb-1. printf("\nNr.c[100][100]. lb=ca+1. coloane matrice B = \n"). } for(i=0. while ((la>=100)||(ca>=100)) { printf("Intoduceti dimensiunile primei matrice"). j++) { printf("a(%d. celei de-a doua matrice"). este un program de înmul ire a două matrice. scanf("%d".b[100][100].&cb).j<=cb-1. void main(void) { la=101.&elem). Un alt exemplu. } if(ca!=lb) { la=101. în acest caz vom avea 3 bucle for incluse una în cealaltă. i. scanf("%f". printf("\nNr. float elem.i++) for(j=0.&elem). lb=ca+1. printf("Nr.

În cazul buclelor incluse.3f ". Dacă într-o buclă se întâlneşte o instruc iune break.k++) s = s+a[i][k]*b[k][j]. for (t = 0.i++) for(j=0.j++) { s=0. De exemplu. t). } printf("\n\nC = A*B\n"). t++) { printf (" %3d ". }} tipăreşte numerele până la 10 şi atunci se opreşte deoarece break determină ieşirea imediată din ciclu. for(i=0.i<=la-1. A doua utilizare constă în terminarea imediată a unui ciclu scurtcircuitând testul condi ional normal al buclei.c[i][j]).3f ".3f ". Instruc iunea break Instruc iunea break are două utilizări.b[i][j]=elem.i++) { printf("\n"). calculatorul termină (părăseşte) imediat bucla şi controlul programului se transferă la instruc iunea ce urmează instruc iunii de buclare.j<=cb-1.j++) printf("%6.h> void main (void) { int t. for(i=0.i<=la-1.b[i][j]). programul: # include <stdio. for(i=0.i<=lb-1. } // Se afisaza matricile printf("\n\nA = \n"). programul: # include <stdio. } printf("\n\nB = \n").i++) { printf("\n").j<=cb-1.i++) { printf("\n"). for(k=0.5.j<=cb-1. if (t == 10) break.j++) printf("%6. De exemplu. for(j=0. for(j=0. } // Se calculeaza fiecare element al matricei produs for(i=0. for(j=0.i<=la-1. este important de notat ca break determină ieşirea imediată numai din bucla interioară (din bucla în care este introdus). c[i][j] = s.j++) printf("%6.5. }} 5.h> void main (void) 91 . t < 100.a[i][j]).j<=ca-1.k<=ca-1. Prima utilizare constă în terminarea unui case în cadrul instruc iunii switch.

5. # include <stdio. } } va afişa pe ecran numerele de la 1 la 10 de 100 de ori.{ int t....6. Instruc iunea continue Instruc iunea continue. count). x). De exemplu.. condi ia din if este îndeplinită.. t < 100.. apoi testarea condi iei de continuare a buclei.. } Dacă la una din itera ii. count++. continuarea procesului de buclare. 92 . Instruc iunea break se poate utiliza şi în cadrul ciclurilor programate cu while sau do-while..... se realizează mai întâi opera ia de incrementare a variabilei de control a ciclului. Dacă instruc iunea break se execută în cadrul unei instruc iuni switch. for sau do-while.... t < 100.. o instruc iune continue determină trecerea direct la testul condi ional şi prin urmare.... ++t) { count = 1..... programul următor va afişa pe ecran numai numerele pare... În cazul unui for..h> void main (void) { int x.. În cazul instruc iunilor while şi do-while. executată într-un ciclu. determină oprirea itera iei curente şi asigură trecerea imediată la itera ia următoare. schema generală de utilizare fiind următoarea: while (expresie) { . 5. if (conditie) break. if (count == 10) break. } } Se observă că atunci când se generează un număr impar se execută instruc iunea continue ce va determina trecerea la itera ia următoare by-pasând instruc iunea printf().. altfel el poate continua până când expresia din while are valoarea fals. nu şi ieşirea din ciclu. for (x = 0. atunci ciclul se termină automat. atunci ea determină terminarea numai a instruc iunii switch. care la rândul ei este inclusă într-un ciclu programat cu while. printf (" %d ".. x++) { if (x % 2) continue. .....) { printf (" %d ". for (. for (t = 0..

. tip. = 0.1. iar size este numărul elementelor pe care le va con ine tabloul. 6. Accesul la un element specific al tabloului se face utilizând un index. var_nume este numele tabloului. Şirurile de caractere pot fi definite prin conceptele: vector de caractere şi pointer-caracter. Adresa cea mai mică corespunde primului element. // se rezerva 10 elemente intregi t. Tipul de bază determină tipul de dată al fiecărui element al tabloului. un tablou constă din loca ii de memorie contigue..Capitolul VI TIPURI DE DATE STRUCTURATE În C există două categorii de tipuri de date structurate: tablourile şi structurile. Aici. Elementele tabloului a[10] sunt a[0]. iar adresa cea mai mare corespunde ultimului element.. // vectorul a contine 10 intregi float v[3].a[9]. În C. t++) x[t] = t. t < 10. Un tablou este o colec ie omogenă de valori de acelaşi tip identificate printr-un indice. Exemple: int a[10]. // vectorul v contine 3 reali În C toate tablourile folosesc pe zero ca index al primului lor element. declară tipul de bază al tabloului. Declararea unui tablou cu o singură dimensiune are următoarea formă generală: tip var_nume[size]. Tablouri unidimensionale Un tablou este o colec ie de variabile de acelaşi tip care sunt referite printr-un nume comun. Un tablou poate avea de la una la mai multe dimensiuni. Cel mai utilizat tablou este tabloul de caractere. } 93 .. Exemplu: Programul următor încarcă un tablou de întregi cu numerele de la 0 la 9: void int int for (t main (void) { x[10]. denumite selectori. iar o structură este o colec ie neomogenă de valori identificate prin nume simbolice.

a acestuia va fi: Total bytes = sizeof (tip) * lungimea_tabloului Observa ie: Limbajul C nu realizează verificarea dimensiunilor unui tablou: astfel. prin rularea programului: char ch[7]. Aceste verificări rămân în sarcina exclusivă a programatorului. compilatorul C nu semnalează nici o eroare: void main (void) { int crash[10]. Exemplu: "acesta este un sir". De exemplu.h" întoarce numărul caracterelor din şir fără terminatorul 0. void main (void) { int i. De notat că. de fapt. adică: strlen(s) = sizeof(s) . } 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. Tablourile unidimensionale sunt. i < 100. deşi vectorul crash con ine numai 10 elemente. Fiecare constantă şir con ine cu un caracter mai mult decât numărul de caractere din şir. pentru fiecare şir s. Dacă se trece peste sfârşitul unui tablou într-o opera ie de atribuire. dimensiunea totală.1. nu există nimic care să ne oprească să nu trecem peste sfârşitul tabloului. i++) ch[i] = 'A' + i. astfel "asaf" are tipul char[5]. în bytes. sizeof ("asaf") = 5. } Se observă că bucla se iterează de 100 de ori. i++) crash[i] = i. atunci se vor atribui valori unor alte variabile sau chiar se vor distruge păr i din program. deoarece aceasta se termină totdeauna cu caracterul NULL '\0' care are valoarea 0. for (i = 0. liste de informa ii de acelaşi tip. i. Exemplu: Deşi următorul program este incorect. for (i = 0.Pentru un tablou unidimensional. i < 7. Aceasta face posibilă reprezentarea caracterelor " şi \ în interiorul unui 94 . §irul vid este descris prin " " şi are tipul char[1]. De exemplu.1.1. Tipul unui şir este "vector de un număr de caractere". În interiorul unui şir se poate folosi conven ia de nota ie cu \. Constante şir În C o constantă şir este o secven ă de caractere închisă între ghilimele. func ia strlen(s) din fişierul antet "string.

printf ("\n Adresa: "). dar majoritatea programelor nu testează dacă mai sunt caractere după el. printf ("%s\n%s\n". adresa[41]. sau ajunge la sfârşitul acestuia. Func ia scanf() citeşte un şir de caractere din bufferul de intrare până când întâlneşte un spa iu. instruc iunea: printf("beep at end of message \007 \n "). nume. ci este o simplă nota ie. atunci în variabila nume se va memora doar valoarea "ENE". determină scrierea unui mesaj. nume). valorile variabilelor nume şi adresa). un caracter TAB. dacă se tastează. o Utilizarea func iei scanf(). Cel mai frecvent caracter folosit este caracterul '\n' = new line (NL). o Cea mai bună cale de a introduce un şir de la tastatură constă în utilizarea func iei gets() din fişierul antet "stdio. valori de tip şir de caractere. Pentru a ob ine şirul în întregime este recomandat să se transmită numele sub forma: "ENE_ALEXANDRU". a caracterului BEL şi a caracterului NL. } S-au definit variabilele nume şi adresa ca tip şir de caractere de maximum 20 şi 40 de caractere. În printf() descriptorul "%s" are rolul de a preciza cum trebuie convertite valorile datelor de afişat (în cazul de fa ă. 6. scanf ("%s". Ini ializarea vectorilor de caractere • Citirea unui şir de la tastatură utilizând func iile scanf() şi gets(). 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.h". respectiv adresa. adresa).h> void main (void) { char nume[21].2.1. Şirul "%s" care apare în apelul func iei scanf() precizează că se vor citi în variabilele nume. adresa). De exemplu. "ENE ALEXANDRU". scanf ("%s". Exemplu: # include <stdio. Nu este permisă continuarea şirurilor de caractere de pe o linie pe alta.şir. Forma generală a func iei gets() este: gets (nume_vector) 95 . Este posibil să folosim caracterul null într-un şir. Astfel. printf ("\n Nume: ").

# include <stdio. A. } Se observă că func ia printf() poate primi ca argument un şir de caractere. Exemplu: # include <stdio. gets (sir). Dacă se introduce un şir mai lung decât dimensiunea tabloului. 23 + 1 = 24 loca ii (bytes). De multe ori. Func ia gets() returnează vectorul ce va păstra şirul de caractere introdus de la tastatură.13". iar cel de-al doilea vector va ocupa începând de la adresa adresa. I. char e3[18] = "cannot open file\n". vectorii de caractere se ini ializează sub forma: char nume_vector[size] = "sir_de_caractere" unde size = numărul caracterelor din şir plus 1. compilatorul C va crea un vector suficient de lung încât să permită ini ializarea dorită. vectorul va fi suprascris. puts (adresa). precum şi caracterul "\n". Exemplu: În loc să scriem : char e1[12] = "read error\n". char e2[ ] = "write error\n".h> void main (void) { char nume[14] = "ENE ALEXANDRU". Cuza. char e2[13] = "write error\n". 96 . puts (nume).h> void main (void) { char sir[80]. nr. sir). gets() va continua să citească caractere până la introducerea caracterului CR. în C se realizează ini ializarea unor vectori de caractere a căror dimensiune nu este precizată. Func ia puts() scrie pe stdout şirul memorat în vectorul al cărui nume apare ca parametru al func iei puts(). /* citeste un sir de la tastatura */ printf ("%s". 13 + 1 = 14 octe i. Dacă dimensiunea vectorului nu este precizată.Pentru a citi un şir se apelează gets() având ca argument numele vectorului. putem scrie: char e1[ ] = "read error\n". } Vectorul nume va ocupa începând de la adresa nume. char adresa[24] = "Str. • Ini ializarea vectorilor de caractere utilizând constantele şir Folosind constantele şir. Exemplu: Programul următor afişează şirul de caractere introdus de la tastatură. fără nici un index.

printf("%s". sir).h) Func ia strcpy() Apelul func iei strcpy() are următoarea formă generală: strcpy (nume_sir. 8.h> void main(void) { char sir[80]. Dacă vectorul are o lungime necunoscută. e2.. 'o'. . 'e'. 4. } Acest program va copia "hello" în şirul sir. 3. constanta_sir). va tipari: write error are lungimea 13 o Ini ializarea unui vector (tablou unidimensional) se poate face şi cu o listă de ini ializatori scrişi între acolade. strcpy (sir. 3}. În cazul în care sunt mai pu ini ini ializatori. Func ia strcpy() copiază con inutul constantei_sir (inclusiv caracterul terminator '\n') în nume_sir. numărul de ini ializatori determină mărimea tabloului.3.Instruc iunea următoare declară şi ini ializează vectorul x ca un tablou unidimensional cu 3 membri: int x[] = {1. i[9] = 10. 6. numărul de ini ializatori nu poate depăşi numărul de membri din tablou. este echivalentă cu: char sir[6] = "hello". 6. 9.Instruc iunea următoare: char sir[6] = { 'h'. Func ii pentru prelucrarea şirurilor (fişierul antet string.char e3[ ] = "cannot open file\n". .1. Rezultă că: i[0] = 1. 2.Instruc iunea următoare ini ializează un vector de 10 întregi cu numerele de la 1 la 10: int i[10] = {1. sizeof (e2)). iar tipul devine complet. Dacă tabloul are lungime fixă. Exemplu: • # include <string. membrii în plus sunt ini ializa i cu zero. 5. Cu această ultimă ini ializare. 7. . "hello"). 10}. • Func ia strcat() Apelul func iei strcat() are forma: 97 .. 2. . 'l'. Exemple: . 'l'. instruc iunea printf ("%s are lungimea %d\n". '\0' }.

h> # include <string. strcpy (second.h> # include <string. Exemplu: # include <stdio. iar rezultatul va avea de asemenea caracterul terminator NULL. dacă s1 = s2 şi un număr pozitiv. 98 . } Func ia strlen() Func ia strlen() se apelează sub forma: strlen (s) unde s este un şir.h> void main(void) { char first[20]. printf ("Introduceti parola: "). strcpy (first. Func ia strcat() concatenează şirul s2 la sfârşitul şirului s1 şi întoarce şirul s1. s2). second[10]. Exemplu: Programul următor returnează lungimea unui şir introdus de la tastatură. Func ia strcmp() Se apelează sub forma: strcmp (s1. • # incude <stdio. gets (s). Func ia strlen() returnează lungimea şirului s. Ambele şiruri trebuie să aibă caracterul terminator NULL. Şirul s2 nu se modifică. "there"). if (strcmp (s. return 0. strcat (first.h> void main (void) { char s[80]. 0. second). } Acest program va afişa "hellothere" pe ecran.strcat (s1. Exemplu: Această func ie poate fi folosită ca o subrutină de verificare a parolei: • # include <stdio. dacă s1 < s2.h> void main (void) { char sir[80]. "pasword")) { printf (" Invalid pasword \n ").h> # incude <string. printf ("%s". "hello").} return 1. dacă s1 > s2. s2). first). Această func ie compară şirurile s1 şi s2 şi returnează valori negative.

} 99 . sir. sir[i]. gets (sir). strcat (s1.h> # include <string. } Dacă se rulează acest program şi se introduc şirurile s1 = "AUTOMATICA" şi s2 = "AUTOMATICA". func ia strcmp() returnează fals (0) şi din această cauză în instruc iunea if s-a folosit !strcmp(). s1).h> # include <string.strlen(s2)). se va afişa: Lungimi 10 10 Sirurile sunt egale AUTOMATICAAUTOMATICA Dacă şirurile sunt egale. unde se converteşte un şir de caractere scris cu litere mici la litere mari. gets(s2).i>=0. printf("Lungimi: %d %d \n". printf ("%s\n".sir[i]).h> void main (void) { char s1[80]. gets(sir). Observa ie: Func ia strlen() nu numără şi caracterul NULL. } caractere ". sir). s2[80]. } Exemplu: Programul următor realizează introducerea unor şiruri. concatenarea lor şi afişarea rezultatului. gets(s1). int i. for(i=strlen(sir)-1.strlen(s1). s2). Observa ie: Caracterul NULL de terminare a vectorului de caractere poate fi utilizat în buclele for ca în exemplul următor. # include <stdio.printf ("Introduceti un sir: "). printf("%s". # include <stdio. strcpy (sir. "acesta este un test").h> void main (void) { char sir[80]. if (!strcmp (s1. s2)) printf ("Sirurile sunt egale\n"). int i. for(i = 0. Exemplu: Programul următor afişează inversul unui şir de caractere introduse de la tastatură. compararea lor. i++) sir[i] = toupper (sir[i]).i--) printf("%c".h> void main (void) { char sir[80]. # include <stdio.h> # include <string. printf ("Sirul %s contine %d strlen(sir)).

'2'. l < 3. '0'. declara ia: int v[2. declară un vector cu 2 elemente.. '0'. Ini ializarea matricelor Declara ia : char v[2][5] = { 'a'. c < 4. 6. '1'.h> void main (void) { char v[2][5] = { 'a'. for (l = 0.1. 100 . num[0][0] = 1. conduce la ini ializarea primului vector cu primele 5 litere. 'd'. i < 2. 'e'. Exemplu: Secven a de instruc iuni: # include <stdio. fiecare element fiind un vector de tip int[5]. 'd'.Conversia caracterelor se face cu func ia toupper() care returnează litera mare corespunzătoare argumentului (literei mici).2. num[3][4]. . Astfel. } conduce la încărcarea tabloului num[3][4]cu numerele de la 1 la 12.. conduce la eroare. } } 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. 6. for (i = 0. j++) printf ("v[%d][%d] = %c". Tablouri cu două dimensiuni (matrice) Tablourile bidimensionale (matricele) sunt reprezentate ca vectori de vectori. 'b'. int i. Astfel. i. printf ("\n"). c. '4' }. 'c'. j. '3'.2. iar a celui de-al doilea cu primele 5 cifre.. 'b'. Ciclul func ionează până când sir[i] devine caracterul null. Se observă că fiecare dimensiune a tabloului este separată (închisă) între paranteze. ++l) for (c = 0. i++){ for(j = 0. 'c'. num[2][3] = 12. j. j < 5. '3'. iar dimensiunile nu sunt separate prin virgulă. De exemplu. declara ia: int v[2][5]. 5]. '2'. '4' }. '1'. ++c) num[l][c] = (l * 4) + c + 1. 'e'.h> void main (void) { int l. Exemplu: Programul: # include <stdio. v[i][j]).

4. 6. Întrucât ini ializatorul se termină cu virgulă.3. {3. unde primul indice se referă la linie şi al doilea indice se referă la coloană.5. Tablouri bidimensionale de şiruri Pentru crearea unui tablou de şiruri se foloseşte un tablou de caractere. Func ional. 6.[sizeN].5}.3. Accesul la un singur şir este foarte uşor: se specifică numai primul indice. în care mărimea indicelui din stânga determină numărul de şiruri. fiecare şir având maximum 80 de caractere.4. 101 .7}. 5. 5.6}. {2. 7. De exemplu: gets (sir_tablou[2]) întoarce al treilea şir din tabloul sir_tablou. declara ia : char sir_tablou[30][80]. 2. Secven a: float y[4][3] = { {1}. 3 şi 0.}. 3. Acelaşi efect ar fi putut fi realizat de: float y[4][3]={1. restul tabloului fiind ini ializat cu 0. elementele lui y[3] vor fi ini ializate cu 0. {2}. y[0][1].Se observă că limbajul C memoreză tablourile bidimensionale într-o matrice linii-coloane.numerele 2. De exemplu. este o ini ializare cu paranteze complete şi are următorul efect: . Tablouri multidimensionale Forma generală a declara iei unui tablou multidimensional este: tip int nume[size1][size2]. defineşte un tablou de 30 de şiruri. {3}. 3. bidimensional.2. 2. }.. privit ca un tablou bidimensional. 6 ini ializează pe y[1]. 5 ini ializează prima linie a tabloului: y[0][0].numerele 1. exprimată în bytes..numerele 3. instruc iunea anterioară este echivalentă cu: gets (&sir_tablou[2][0]). este: nr_linii * nr_coloane * sizeof(tipul_datei) Declara ia: float y[4][3] = { {1. cu 1. . ini ializează prima coloană a lui y. 6. 3. 4. iar indicele din drepta specifică lungimea maximă a fiecărui şir. {0}. . declara ia: trei[4][10][3]. De exemplu. }.2. y[0][2] sau y[0]. 7 ini ializează pe y[2]. 5. Cantitatea de memorie alocată permanent pentru un tablou.

4. 16. 16. tag) ce poate fi folosit la crearea variabilelor tip structură. } addr. Structuri O structură este o colec ie de variabile (de tipuri diferite) referite sub un singur nume. char *city. fragmentul următor defineşte un şablon pentru o structură numită addr care defineşte la rândul său numele şi adresa unei persoane necesare în cazul transmiterii unei scrisori: struct addr { char name[30]. char *state. Variabilele care formează structuri se numesc elementele structurii. Pentru definirea unui şablon al unei structuri se foloseşte cuvântul cheie struct. unsigned int zip.” (este unul din pu inele cazuri de utilizare a caracterului punct şi virgulă ”. Trebuie men ionat că pentru aceasta este necesară precizarea indicelui celui mai din dreapta. 6. Forma generală de ini ializare a tablourilor este următoarea: specificator_tip nume_tablou[size1][size2]. char city[20]. 5. 1.4. char *street. declara ia: int sqrs[5][2] = {1. 9. 1. 4. compatibile cu tipul de bază al tabloului. deci trebuie făcută distinc ie dintre structuraşablon şi variabila-structură. char street[40]. unsigned int zip. 4. De exemplu. 9. 25}. }. Defini ia unei structuri formează un aşa numit şablon (template. 25}.” după acoladă). 2. 2. 5. Programul anterior defineşte numai forma (tipul) datelor structurii. 3. este echivalentă cu declara ia: int sqrs[ ][2] = {1. struct { char *name.creează un tablou de 4*10*3 întregi. unde lista_valori este o listă de constante separate prin virgulă. dar nu defineşte variabilele structură. Astfel. 102 . char state[3]. 4.[sizeN]={lista_valori}.. Precizăm că numele addr identifică structura particulară definită anterior (şablonul) şi este specificatorul său de tip.. O variabilă de tip structură se declară cu ajutorul şablonului structurii. Observa ie: Limbajul C permite şi ini ializarea tablourilor multidimensionale fără dimensiune. Terminarea defini iei se face cu ”. 3.

} addr_info. se pot declara una sau mai multe variabile-structuri. Pentru declararea unei singure structuri numite addr_info. binfo. char state[3]. char street[40]. De exemplu. Limbajul C alocă suficientă memorie pentru a păstra toate variabilele ce alcătuiesc o structură. . } addr_info.Pentru a declara o variabilă actuală cu această structură vom scrie struct addr addr_info. char city[20]. tip nume_variabile. 103 . . Această linie declară variabila addr_info ca variabilă structură de tip addr. char street[40]. binfo. nu mai este necesară includerea numelui addr al structurii. . char city[20]. . astfel : struct addr { char name[30]. . . În cazul de mai sus se defineşte variabila-structură addr_info cu şablonul definit. cinfo de acelaşi tip. Secven a anterioară defineşte o structură şablon numită addr şi declară variabilele addr_info. unsigned int zip. . . unsigned int zip. } variabile_structura. . . cinfo. . astfel: struct { char name[30]. tip nume_variabile. memoria alocată pentru structura addr_info va fi : Name 30 bytes Street 40 bytes City 20 bytes State 3 bytes Zip 4 bytes Când se defineşte o structură şablon. . char state[3]. dar fără nume Forma generală de definire a unei structuri este : struc nume_tip_structura { tip nume_variabile. . .

zip).".4. După cum se observă. De exemplu: printf ("%d". În aceeaşi formă. 104 . De exemplu apelul: gets (addr_info.1. dar nu ambele. tablourile de structuri încep cu indexul 0. pentru afişarea variabilei zip se va scrie: printf ("%d". caracter cu caracter. addr_info. addr_info. are ca efect trecerea la un pointer-caracter la primul caracter al elementului nume. Pentru a avea acces la o structură oarecare din cele 100 se va indexa numele structurii (în cazul acesta addr_info). instruc iunea următoare va atribui elementului zip al variabilei structură addr_info valoarea 12345: addr_info.name). for (t = 0. Tablouri de structuri Cel mai uzual mod de folosire a structurilor este în tablouri de structuri. Se observă că. De exemplu. ++t) putchar (addr_info. De exemplu. Pentru declararea unui tablou de structuri.name[t]. De exemplu. are ca efect afişarea codului zip din a treia structură. fie variabile_structura. se foloseşte programul: register int t.zip). la fel orice variabilă tablou. 6.name[t]).nume_element De exemplu. se pot referi celelalte elemente ale structurii addr_info. addr_info[2]. pentru declararea unui tablou cu 100 de structuri addr definite anterior.zip = 12345. se va scrie: struct addr addr_info[100]. se poate indexa name. Pentru a avea acces la fiecare caracter al elementului addr_info.unde pot fi omise fie numele tipului structurii nume_tip_struct.name. Pentru accesarea elementelor unei structuri se foloseşte forma generală : nume_structura. pentru afişarea con inutului lui addr_info. nume_tip_structura după cuvântul cheie struct se referă la şablonul structurii (tipul său) 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 ". mai întâi se defineşte o structură şi apoi se declară un tablou de variabile de acel tip.name.

name = '\0'. case 'l' : load(). street. }}} Func ia init_list() pregăteşte tabloul structură pentru utilizare prin punerea unui caracter null în primul byte al câmpului "nume". Structura func iei de initializare init_list() ar putea fi următoarea: /* Functia init_list() */ void init_list() { register int t. Pentru definirea structurii de bază addr care va con ine aceste informa ii vom scrie: struct addr { char name[20]. } addr_info[SIZE]. zip. Prima func ie necesară în program este main(). unsigned int zip. Tabloul addr_info contine SIZE structuri de tip addr. a cărei structură este următoarea: void main() { char choice. break. state. case 'q' : exit(). Această ini ializare are loc în memoria internă a calculatorului (nu în fişierul maillist de pe disc).. city. break. char city[15]. case 's' : save(). break. for (t = 0.maillist Pentru a ilustra modul de utilizare a structurilor şi tablourilor de structuri prezentăm un exemplu de program pentru actualizarea unei liste de coresponden ă. case 'd' : display(). char state[10]. Informa iile ce vor fi memorate se referă la name. for (. Programul impune ca o variabilă structură să nu fie utilizată dacă câmpul nume este vid. switch (choice) { case 'e' : enter().Exemplu de program pentru actualizarea unei liste de coresponden a . break.) { choice = menu(). } 105 . unde SIZE se defineşte după necesită i. t++) *addr_info[t]. char street[30]. init_list(). t < SIZE.

/* Functia menu() */ char menu() { char s[5].state). gets (addr_info[i].} Rutinele save() şi load() se utilizează pentru actualizarea bazei de date. } while (!strrchr("edlsq". printf ("State: ").ch)). i++) if (!*addr_info[i].zip). printf ("Zip: "). Func ia enter() are ca efect introducerea unor noi informa ii în următoarea structură liberă a listei addr_info[SIZE]. printf ("(Q)uit\n"). do { printf ("(E)nter\n"). printf ("(D)isplay\n").name) break.h> întoarce un pointer la ultima apari ie a lui c în cs sau NULL dacă nu apare. printf ("(L)oad\n").Func ia de selectare a meniului menu() va afişa mesajele op iunilor şi va returna varianta aleasă. i < SIZE. Când se apelează load().name). printf ("Street: "). if (i == SIZE) { printf ("addr_info full \n"). printf ("(S)ave\n"). Prin tastarea literei din paranteze. Aceste rutine lucrează cu fişierul disc maillist. atunci se copiază în tabloul structură din memorie 106 .&addr_info[i]. ch=s[0].c) din <string. printf ("City: "). } Observa ie: Func ia strrchr(cs.ch. gets (addr_info[i].} printf ("Name: ").street). gets (addr_info[i]. for (i=0. /* Lista plina */ return. Această introducere se efectuează prin determinarea primei structuri libere din memorie (cu addr_info. /* Functia enter() */ void enter() { register int i. printf (" Alegeti optiunea: "). se va lansa în execu ie o anumită procedură. gets (addr_info[i].city). scanf ("%d". gets(s). return tolower(ch).name setată la 0) şi prin completarea sa cu informa ii culese de la tastatură.

} for (i = 0. return. save(). exit(). } Atât save() cât şi load() confirmă un succes a unei opera ii cu fişiere prin verificarea valorilor întoarse de func iile fread() sau fwrite().} else printf ("File read error\n").city). Func ia display() afişează pe ecran întregul tablou structură din memorie care con ine date valide: /* Functia display() */ void display() { register int t. return. printf("%s\n".sizeof(struct addr). fp) == 1). else if (feof(fp)) { fclose (fp).name) if(fwrite(&addr_info[i]. if ((fp = fopen("maillist". de supraînscriere a fişierului disc cu datele din memorie.name!='\0') { printf("%s\n". Spre exemplu. i <= SIZE. for (t=0. 1.t<SIZE. dacă dorim să adăugăm date la fişierul maillist care con ine deja date introduse anterior.fp) !=1) printf (" File write error \n "). Structura acestor rutine este următoarea: /* Functia save() */ void save() { register int i.street). return.t++) { if (*addr_info[t].datele stocate în fişierul maillist.addr_info[t].addr_info[t]."rb")) == NULL) { printf("Cannot open file\n ").name).addr_info[t]. fclose (fp). i++) if(*addr_info[i]. i++) if(fread(&addr_info[i]. În plus. 107 . printf("%s\n".sizeof(struct addr). Func ia load() realizează opera iunea inversă.1. enter().} /* Functia load() */ void load() { register int i. "wb")) == NULL) { printf (" Cannot open file\n "). load().} for (i = 0. if ((fp = fopen("maillist". load() trebuie să caute şi indicatorul de sfârşit de fişier utilizând func ia feof() deoarece fread() întoarce aceeaşi valoare dacă se întâlneşte indicatorul de sfârşit de fişier sau dacă apare o eroare. i < SIZE. ordinea de lansare a comenzilor va fi: init_list().

addr_info[t].state). printf ("(S)ave\n"). case 's' : save().) { choice = menu(). printf("%d\n\n". for (t = 0. init_list(). break. char state[10]. FILE *fp. for (.}}} Listingul complet al programului va fi: # include <stdio. printf ("(D)isplay\n"). char menu().ch.h> # include <ctype.name = '\0'.addr_info[t].. unsigned int zip. printf ("(Q)uit\n"). } addr_info[SIZE]. case 'l' : load(). char street[30]. 108 .zip).exit().printf("%s\n". void init_list(). case 'q' : exit(). t++) *addr_info[t].save(). t < SIZE. case 'd' : display(). void main() { char choice.load(). }}} /* Functia init_list() */ void init_list() { register int t. char city[15]. printf (" Alegeti optiunea: "). getchar(). break. switch (choice) { case 'e' : enter(). } /* Functia menu() */ char menu() { char s[5]. break.enter(). break. gets(s). do { printf ("(E)nter\n").h> # define SIZE 100 struct addr { char name[20]. printf ("(L)oad\n"). void display().h> # include <string.

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

addr_info[t]. } struct2.}}} 6. printf("%5d".x).addr_info[t].addr_info[t]. Pentru a transmite func iei func() adresele elementelor din structura struct2.4. getchar().printf("\n%20s".t<SIZE. float z.name!='\0') { printf("%20s". printf("%5s\n".s).s[2]). /* se paseaza adresa caracterului s[2] */ 110 . /* se paseaza adresa caracterului x */ func (&struct2. Introducerea structurilor în func ii a) Introducerea elementelor unei structuri în func ii Când un element al unei variabile tip structură este transmis (pasat) unei func ii. printf("%30s".z). char s[10]. /* se paseaza valoarea intregului y */ func3 (struct2. //se utilizeaza valoarea caracterului lui s[2] unde func(). func4() sunt numele unor func ii.addr_info[t].x).street). for (t=0. printf("%30s"."City"). /* se paseaza valoarea reala a lui z */ func4 (struct2.s[2]).y)."State"). printf("%10s".t++) { if (*addr_info[t].addr_info[t]."Zip"). printf("%15s"."Name"). /* se utilizeaza adresa sirului s */ func (struct2.2."Street"). se scrie astfel: func (&struct2. func3(). /* se paseaza valoarea caracterului x */ func2 (struct2. printf("%15s".city). printf("%10s".zip). int y. func2().state). Exemplu: struct struct1{ char x.name). Modalitatea de a introduce fiecare element într-o func ie este următoarea: func (struct2. de fapt este transmisă valoarea acelui element.

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ă. } Acest program declară atât argumentul arg al lui f1. Ceea ce trebuie avut neapărat în vedere atunci când. cât şi parametrul param ca având acelaşi tip de structură.}. Aceasta înseamnă că orice modificare asupra con inutului structurii în interiorul func iei în care este apelată structura. /* Se defineste un tip structura */ struct struct_tip { int a. limbajul C transmite func iei întreaga structură utilizând metoda standard a apelului prin valoare.h> void f1().} 111 . /* se declara structura arg */ arg. arg. char ch.b) Transmiterea unei întregi structuri unei func ii Când o structură este utilizată ca argument al unei func ii.b. Programul va afişa pe ecran valoarea 1000. f1(arg). void main() { struct struct_tip arg. ca parametru. nu afectează structura folosită ca argument.x). param. f1(arg). char ch. Exemplu: # include <stdio. y. {printf ("%d\n". } arg.h> void f1().a = 1000. Exemplu: # include <stdio.a = 1000. } param. b. void main() { struct { int a. /* se apeleaza functia f1() */ } void f1(param) /* se declara functia f1 */ struct { int x. se utilizează o structură este ca tipul argumentului să fie identic cu tipul parametrului. char ch.

sir_ord=cit_sir().h> // definim prototipurile functiilor utilizate struct sir_lung cit_sir(). struct sir_lung ord_sir().sir_par. sir_init=cit_sir(). propunem următorul program: .sir_ord). void tip_sir(). 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ă. sir_impar=ord_sir(sir_impar). 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. scrierea lor pe display. /* se defineste structura sir+lungime si variabila globala sir */ struct sir_lung { int sir[80]. struct sir_lung concat_sir(). având în vedere ca primele pozi ii să fie ocupate de numerele pare din şir sortate crescător după care să urmeze numerele impare din şir sortate tot crescător.a). struct sir_lung par_sir().sir_impar).param. Programul în C este prezentat în continuare: # include <stdio. sir_ord=concat_sir(sir_par. getchar().se concatenează cele două şiruri .void f1(struct struct_tip functia f1() */ {printf ("%d\n". Se vor construi func ii care să realizeze citirea şirurilor de la tastatură. int lung. // programul principal void main(){ struct sir_lung sir_init. sir_init=concat_sir(sir_init.şirul rezultat se sortează în ordine crescătoare.se preia de la tastatura un al doilea şir de numere întregi . struct sir_lung impar_sir().sir_impar. sir_par=par_sir(sir_init). 112 . sir_par=ord_sir(sir_par).} sir.se preia de la tastatura un prim şir de numere întregi . } param) /* se declara Pentru exemplificare. sir_impar=impar_sir(sir_init).sir_ord. Pentru a realiza acest program.

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

Din func ia cit_sir() se poate ieşi prin tastarea oricarui caracter nenumeric.} y. Tablouri şi structuri în structuri Elementele unei structuri pot fi simple (int. referirea întregului a[3][7] se face prin: y. al doilea element este salariul săptămânal al acestuia. Aici "addr" este structura definită anterior. Instruc iunea următoare va atribui codul 90178 variabilei "zip" din adresa muncitorului muncitor.5.4. char etc. 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. -9 -2 31 2 -7. 6. Definitia uniunilor este similară celei a structurilor.3.) sau complexe (tablouri de elemente de diferite tipuri. structuri). Uniuni În C o uniune este o zonă de memorie utilizată de mai multe variabile ce pot fi diferite ca tip. /* tablou de 10x10 intregi */ float b. Se observă că elementele variabilei-structură "adresa" sunt incluse în structura "sal". Se observă că elementele fiecărei structuri sunt referite de la exterior către interior. respectiv de la stânga la dreapta. incluse). } muncitor. Exemplu: struct sal { struct addr adresa. Rulând programul vom ob ine rezultatele: 1 2 17 -3 6 -4. 114 . De exemplu.adresa. Exemplu: Considerăm structura : struct x { int a[10][10].zip = 90178. float salariu. Exemplul anterior defineşte structura "sal" cu 2 elemente: primul este o structură de tip "addr" care con ine adresa salariatului.a[3][7] Când o structură este un element al altei structuri. 1 2 17 -3 6 -4 -9 -2 31 2 -7 -4 -2 2 2 6 -9 -7 -3 1 17 31 6. structurile se numesc încuibate (nested.

Când se declară o variabilă union compilatorul C rezervă o zonă de memorie suficient de lungă capabilă să preia variabila cu lungimea cea mai mare. uniunile sunt des utilizate când sunt necesare conversii de tip. În exemplul următor. mai întâi se crează o uniune ce cuprinde un întreg şi un vector de două caractere. Când un element al unei uniuni se adresează direct. poate fi scrisă folosind o uniune. fp) union pw word. se utilizează operatorul ". // se scrie primul caracter putc(word->ch[1]). fie utilizând o instruc iune de declarare separată. se foloseşte operatorul "-> ". pentru a atribui elementul întreg i al uniunii anterioare "exuniune" valoarea 10. utilizând aceasta uniune este : putw(word.Exemplu: union tip_u { int i. } /* se atribuie valoarea 10 intregului i al uniunii exuniune */ In C.}. De exemplu. iar când elementul se adresează printr-un pointer.".}. Pentru aceasta. De exemplu. declară variabila-uniune "exuniune" în care atât întregul i. Atunci. cât şi caracterul ch ocupă aceeaşi zonă de memorie (cu toate ca int ocupa 4 octe i. O variabilă de acest tip poate fi declarată fie prin plasarea numelui său la sfîrşitul acestei defini ii.i = 10. char ch. iar char numai un octet). instruc iunea : union tip_u exuniune. programul transferă un pointer la "exuniune" unei func ii : func1(union tip_u *un) { un -> i = 10. se va scrie: exuniune. /* se declara uniunea word */ FILE *fp { putc(word ->ch[0]). char ch[2]. // se scrie al doilea caracter } 115 . astfel: union pw { int i. Pentru a accesa elementele unei uniuni se utilizează aceeaşi sintaxă folosită la structuri (operatorii punct şi " -> "). structura lui putw(). De exemplu. func ia standard putw() ce realizează scrierea binară a unui întreg într-un fişier de pe disc.

Dându-se această defini ie şi declara ie. enum bancnota bani. mie=1000. De aceea. cincisute.douasute. 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ă. printf ("%s".zecemii}.cincisute. deoarece "bani" este un întreg şi nu un şir : bani = cincimii. mie). s. Enumerările se definesc în acelaşi mod ca şi structurile. if (5*bani == cincimii) printf("Sunt 5000 lei.d. Fără nici o altă ini ializare. sunt valabile urmatoarele instructiuni: bani = mie. cincimii. bani). va afisa pe ecran: 0 3 Se pot specifica valorile unuia sau mai multor simboluri folosind ini ializatori. Exemplu: Următorul fragment defineşte o enumerare numită "bancnota" cu care apoi se declară o enumerare numită "bani" având acest tip: enum bancnota {suta.m. s). zecemii}. valoarea primului enumerator este 0.6. cât şi lista_variabile sunt op ionale. Trebuie precizat că fiecare enumerator este caracterizat printr-o valoare întreaga.cincimii. strcpy (bani. instruc iunea: printf ("%d %d. a celui de-al doilea este 1.6. Forma generală de definire a unei enumerări este: enum nume_tip_enum { lista_enumeratori} lista_variabile.a. Enumerări O enumerare este o mul ime de constante întregi ce pot lua toate valorile unei variabile de un anumit tip. unde atât nume_tip_enum.mie. De exemplu: enum bancnota {suta. douasute. utilizând cuvântul cheie enum ce semnalează începutul unui tip enumerare. Nici acest program nu functionează: gets (s). suta.\n"). 116 .

.sutamii. se va scrie: switch (bani) { case suta: printf (" suta "). double f."douamii"."mie".cincisutemii}.cincizecimii. case douasute: printf (" douasute "). . . . Fragmentul anterior va func iona numai dacă nu se realizează ini ializarea simbolurilor. . . . se poate declara un tablou de şiruri şi utiliza valoarea enumeratorului ca index. douasute. name[bani]). } y. "douasute". următorul fragment va afişa şirul corespunzător: char name[ ][20] = { "suta".h> enum bancnota { suta. .mie. "mie".zecemii. case zecemii: printf (" zecemii "). cincisute. . void main() {printf("%d\n". . . . . break. . "cincisute". . ."cincizecimii" "sutamii". ."cincimii" . char name[][20]= {"suta"."douasute". . bani <= cincisutemii. .} Dacă variabilei uniune y din exemplul următor i se aplică operatorul sizeof() vom găsi sizeof(y) = 8. name[bani])."cincisutemii"}. void main() { enum bancnota bani. cincimii. . .h> union { char ch. . bani ++) printf ("%s\n". Următorul program afişează numele bancnotelor: # include <stdio. printf ("%s". 117 . "cincimii" "zecemii" }. .Pentru a afişa tipurile bancnotelor con inute în enumerarea "bani". . deoarece indexarea şirurilor începe cu zero. . # include <stdio.sizeof(y)). . . break.} Deci compilatorul va re ine valoarea celei mai largi tipuri de date din uniune. } Uneori pentru a translata valoarea unui enumerator în şirul corespunzător. for (bani = suta. . int i."zecemii". ."cincisute". . . . De exemplu.

Exemplu: • # include <stdio. iar nume_variabila este numele variabilei pointer. aparte de tipurile char. Forma generală pentru declararea unei variabile pointer este: tip * nume_variabila. • Operatorul * este complementarul lui &. deci depinde în mare măsură 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 tipărirea con inutului unei variabile pointer (o adresă) utilizând printf() cu formatul %p. int. Operatori pointer Există doi operatori pointer speciali * şi &: Operatorul & este un operator unar care oferă (returnează) adresa unei variabile (adresa operandului său). /* pointer la caracter */ int *temps. /* int count are valoarea 100 */ 118 . /* pointer constant la caracter */ 7. Tipul de bază al pointerului defineşte tipul variabilelor spre care indică pointerul. Formatul în care se stochează o variabilă pointer în memorie depinde de tipul de compilator care se foloseşte. count = 100. nu la formatul în care se stochează efectiv o variabilă pointer în memorie. *start.h> void main (void) { int *count_addr. Cuvântul cheie tip din declara ia unui pointer se referă la tipul de dată spre care indică pointerul. Variabila pointer este o variabilă de un tip special.1. Este un operator unar care returnează valoarea variabilei plasată la adresa care urmează după acest operator. count. float. val. unde tip poate fi oricare din tipurile de bază admise în C. /* pointeri la intregi */ char *const q. Exemplu: char *p.Capitolul VII POINTERI Un pointer este o variabilă care păstrează adresa unui obiect de tip corespunzător.

/* val preia valoarea de la adresa count_addr. val). adresa care nu are nici o legatura cu valoarea lui count */ val = count_addr. } Să presupunem că zona de stocare a celor trei variabile arată astfel: Adresa i j p i = 123. /*Se va tipari numarul 100 */ Spre exemplu. /*count_addr indica spre count. Memoria 1200 1202 1204 După primele două atribuiri zona de stocare va arăta astfel: A dresa i j p 1200 1202 1204 1200 M emoria 123 Con inutul variabilei p (de tip pointer) va fi valoarea 1200. j. p = &i.count_addr = &count. // i si j sunt ambele intregi scurti short *p // p este pointer la tip intreg scurt i = 123. Aceasta instructiune plaseaza valoarea lui count aflata la adresa count_addr în variabila val */ printf ("%d". Aceasta instructiune plaseaza in count_addr adresa din memorie a variabilei count. copiază un întreg scurt de la loca ia 1200 în j. Instruc iunea j = *p. loca iile de memorie fiind acum ca cele din figură: Adresa i j p 1200 1202 1204 Memoria 123 123 1200 119 . j = *p. adică adresa variabilei i. p = &i. să considerăm por iunea de program: short i.

int *p1. Exemplu: # include <stdio. 7. 120 .1.12.h> void main (void) { float x = 10. /* p preia adresa lui x */ y = *p. /* p1 indica spre x */ p2 = p1 /* p2 indica tot spre x */ printf ("p1 = %p p2 = %p".h> void main (void) { int x. } /* Se afiseaza valoarea hexa a adresei lui x.1. Importan a tipului de bază Considerăm declara ia: val = *count_addr. de unde ştie compilatorul câ i bytes să transfere în cazul oricărei asignări care utilizează pointeri. Sau. p2). expresiile în care intervin pointeri respectă aceleaşi reguli ca orice alte expresii din limbajul C. Răspunsul la aceste întrebări este acela că.x. /* pointer la intreg */ p = &x. mai general. y. deoarece în program se declară p ca fiind pointer la întreg scurt şi compilatorul va transfera în y numai 2 bytes (corespunzători reprezentării unui întreg scurt) şi nu 4 bytes.2. • Atribuirea pointerilor Ca orice variabilă.*p2. Exemplu: /* Acest program nu lucreaza corect */ # include <stdio. } Acest program nu va atribui valoarea lui x lui y. un pointer poate fi folosit în membrul drept al unei instruc iuni de asignare (atribuire). Expresii în care intervin pointeri În general. pentru atribuirea valorii unui pointer unui alt pointer.1. /* pointeri la intregi */ p1 = &x. nu valoarea lui x */ Se observă că în func ia printf() tipărirea se face cu formatul %p care specifică faptul că variabilele din listă vor fi afişate ca adrese pointer. p1.y). /* y preia valoarea de la adresa p */ printf ("x = %f y = %f". Se pune întrebarea: care va fi numărul de bytes ce va fi transferat variabilei val de la adresa indicată prin *count_addr. corespunzători unui număr real în virgulă mobilă.7. tipul de bază al pointerului determină tipul datei spre care indică pointerul. short int *p.

c1. /* p contine adresa lui c1 */ char c2 = *p. acesta va indica spre elementul anterior. /*c2 preia valoarea de la adresa p*/ printf ("c1 = %c c2 = %c". După fiecare decrementare a unui pointer. O l-valoare (left value) este un operand care poate fi plasat în stânga unei opera ii de atribuire. Deci variabila indicată de p este c1. Astfel. dacă p1 = 2000. aşa încât valoarea lui *p atribuită lui c2 este 'a'. Verifica i utilizarea pointerilor din programul următor: 3000 3001 3002 3003 3004 3005 3006 Memoria i i+1 i+2 i+3 121 . ch ch + 1 ch + 2 ch + 3 ch + 4 ch + 5 ch + 6 Cum valoarea indirectată de un pointer este o l-valoare. p1++. iar valoarea memorată în c1 este 'a'. acesta va indica spre următorul întreg. c2). acesta va indica spre următorul element al tipului său de bază. ea poate fi asignată şi incrementată ca orice altă variabilă. aşa cum se poate vedea în exemplul următor: char *ch = 3000. char *p = &c1. acesta va fi p1 = 2004 (va indica spre următorul întreg). După fiecare incrementare a unui pointer. • Opera ii aritmetice efectuate asupra pointerilor o Utilizarea operatorilor de incrementare şi decrementare Fie secven a: int *p1. Exemplu: char c1 = 'a'. Valoarea pointerilor va fi crescută sau micşorată în concordan ă cu lungimea tipului datelor spre care aceştia indică.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. /* pointer la intreg */ De fiecare dată când se incrementează p1. după efectuarea instruc iunii p1++. short int *i = 3000.

t=3. 122 . *pj). printf("i= %d.++pj. *pi. *pi. short i. *pj). dacă pointerul rezultat indică în afara tabloului. j=2. Dacă valoarea p1 = 3000.h> void main(void) { short *pi. p1 = p1 + 9. pj). rezultatul este nedefinit.++*pj= 1 La sau dintr-un pointer. Evident că valoarea pointerului se va modifica corespunzător lungimii tipului datei indicată prin pointer. indicând spre un alt element din tablou. considerând că elementul curent este indicat de p1. t. i=1. printf("++pj= %p. printf("*pi= %d *pj= %d\n". pj= %p\n". printf("*pi= %d *pj= %d\n". o Utilizarea operatorilor de adunare şi de scădere face ca p1 să indice spre al 9-lea element având tipul lui p1. pi=&i. j. long *pl. rezultatul este un număr întreg cu semn care reprezintă deplasamentul dintre cei doi pointeri (pointerii la obiecte vecine diferă cu 1). *pj /= *pi+2. se pot aduna sau scădea valori de tip întreg. printf("pi= %p.pj= 0065FDDC *pi= 1 *pj= 1 *pi= 1 *pj= 0 ++pj= 0065FDDE. ++*pj= %d\n". pi.++*pj).j= 2 pi= 0065FDE0. atunci p1 + 9 va avea valoarea: (valoarea lui p1)+9*sizeof(int)=3000+9*4=3036 Aceleaşi considerente sunt valabile în cazul în care un întreg este scăzut dintr-un pointer. i. j). /* Pointer la intreg */ p1 = p1 + 9. De exemplu. pe calculatoarele mai moderne ob inem rezultatul i= 1. pj=&j. Dacă doi pointeri de acelaşi tip sunt scăzu i. *pj /= *pi+1. *pj. În cazul tablourilor. j= %d\n". } În urma rulării sale.# include <stdio. double *pd. Exemplu: int *p1. Rezultatul este un pointer de acelaşi tip cu cel ini ial.

} Doi pointeri de acelaşi tip se pot compara printr-o expresie rela ională.8. Nu se pot efectua opera ii de înmul ire şi împăr ire cu pointeri.Dacă p indică spre ultimul membru dintr-un tablou. &start). dump (start).2.*px. atunci instruc iunile: if (p < q) printf (“ p indica spre o adresa mai mica decit q \n “). *p). Exemplu: Un exemplu interesant de utilizare a pointerilor constă în examinarea con inutului loca iilor de memorie ale calculatorului. float x[] = {1.j. printf (“Introduceti adresa de start: “). p++) { if (!(t%16)) printf ("/n").6. astfel: dacă p şi q sunt doi pointeri.h> dump (start).7.px). px = &x[4]+i. t++. p = (char far *) start. Observa ii : Nu se pot aduna sau scădea valori de tip float sau double la/sau dintr-un pointer. /* Se apeleaza functia dump () */ } dump (start) /* Se defineste functia dump() */ unsigned long int start. j = &x[i]-&x[i-2].5.h> void main(){ int i=4.3. Compararea pointerilor se utilizează când doi sau mai mul i pointeri indică spre acelaşi obiect comun. /* Programul afiseaza continutul locatiilor de memorie de la o adresa specificata */ # include <stdio. atunci (p+1) are valoare nedeterminată. o Compararea pointerilor sunt corecte.9}. j.h> # include <stdlib. Exemplu: Scăderea a doi pointeri este exemplificată în programul: # include <stdio. printf ("%2X ". *px. printf("%d %f %p %p\n". void main (void) { /* start = adresa de inceput */ unsigned long int start. {char far *p.&x[4]. 123 . /*Conversie la un pointer*/ for (t = 0. . scanf (“ %lu “. int t.4.

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

Programul introduce cuvântul cheie far care permite pointerului p să indice loca ii de memorie care nu sunt în acelaşi segment de memorie în care este plasat programul. Formatul %lu din scanf() înseamnă: "citeşte un unsigned long int". Formatul %X din printf() ordonă calculatorului să afişeze argumentul în hexazecimal cu litere mari (Formatul %x afişează în hexazecimal cu litere mici). Programul foloseşte explicit un şablon de tip pentru transferul valorii întregului fără 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 până acum, s-au folosit func ii C care atunci când erau apelate, parametrii acestor func ii erau (întotdeauna) actualiza i prin pasarea valorii fiecărui argument. Acest fapt ne îndreptă eşte să numim C-ul ca un limbaj de apel prin valoare. Există totuşi o excep ie de la această regulă atunci când 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 său 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 strânsă legătură î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 corepunzători 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). Considerăm 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 fără 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 fără indici reprezintă adrese, pe când numele unui tablou cu indici se referă la valorile stocate în acea pozi ie în tablou. Deoarece indicele inferior al oricărui 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, considerăm un program care tipăreşte 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 uşor de utilizat când se doreşte referirea elementelor unui tablou în ordine strict crescătoare sau strict descrescătoare. Dacă însă se doreşte 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 următoarele 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ă acelaşi 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ă acelaşi lucru cu valoarea de la adresa (pq+n). Exemplu: Programul următor realizează tipărirea 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 păstrează stiva într-un tablou, iar rutinele push() şi pop() realizează accesul la elementele stivei utilizând pointeri. Pentru memorarea vârfului stivei, utilizăm variabila tos (top of stack). Considerăm că folosim numai numere de tip întreg. În programul main(), rutinele push() şi pop() sunt utilizate astfel: push() citeşte numerele introduse de la tastatură şi le memorează în stivă dacă acestea sunt nenule. Când 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 fără 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: Prezentăm 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) rămâne adevărată până când se întâlneşte 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 următor afişează pe ecran mesajul " Acest program func ionează ":
# include <stdio.h> void main (void) { char *s; s = " Acest program functioneaza "; printf (s); }

128

7.h> void main (void) { char s[80]. p = &x[2]. De exemplu. Un domeniu în care această practică este esen iala constă în găsirea unui subşir într-un şir dat. printf (" Introduceti un sir : \n "). atunci printf() afişează mai întâi un spa iu şi apoi “friend“. se va scrie: x[2] = &var. se va scrie: y = *x[2]. Preluarea adresei unui element al unui tablou Până acum s-a văzut că un pointer poate să adreseze primul element al unui tablou. i++) p = & s[i+1].7. /* Gaseste primul spatiu sau sfarsitul sirului */ for (i = 0. De exemplu. dacă se introduce “my friend“. Este posibil să se adreseze orice element al unui tablou aplicând operatorul & unui tablou indexat. Dacă în şirul introdus nu este nici un spa iu. programul va afişa spa iul şi apoi subşirul rămas. printf (p). var. // Vector de 10 pointeri la intregi char *p[20]. Exemplu: Programul următor afişează ultima parte a unui şir introdus de la tastatură.2. int i. char *p. astfel: 129 . Exemplu: int *x[10]. //Valoarea lui var este atribuita lui y Exemplu: Tablourile de pointeri pot fi utilizate în construirea mesajelor de eroare. } Dacă p indică spre un spa iu. celui de al treilea element al tabloului de pointeri *x[10]. din punctul în care se întâlneşte primul spa iu: # include <stdio. s[i] && s[i] != ' '.3. Pentru găsirea valorii lui var. p indică spre sfârşitul şirului şi atunci nu se va afişa nimic. plasează adresa celui de-al 3-lea element al vectorului x în pointerul p. Tablouri de pointeri Putem construi tablouri de pointeri în aceeaşi manieră în care se definesc alte tipuri de date.2. // Vector de 20 pointeri la caracter Pentru atribuirea unei variabile întregi.4. gets (s).

Conceptul de tablou de pointeri este simplu. dacă se introduce 2. care indică spre variabila ce con ine valoarea dorită: Pointer Pointer Variabilă Adresă ---------> Adresă ---------> Valoare Declararea indirectărilor multiple se face sub forma: /* cpp este un pointer la pointer la caracter */ char **cpp. valoarea pointerului este adresa variabilei care con ine valoarea dorită: Pointer Variabilă Adresă ---------> Valoare În cazul unui pointer la pointer. În cazul unei indirectari simple. deoarece indexarea tabloului conduce la clarificarea semnifica iei lui. iar [] şi () sunt operatori postfixa i. trebuie folosite paranteze.2. De exemplu. " Read error \n ". /* newbalance este un pointer la pointer la float */ 130 . 7. &num). care este pasat ca argument func iei selmes(). selmes (int num) /* Selecteaza un mesaj */ { scanf("%d". /* Se introduce un numar de la tastatura */ printf("%s". Un pointer la un pointer este o formă de indirectare multiplă sau un lan de pointeri.char *err[ ] = { " Cannot open file \n ". atunci când se doreşte schimbarea priorită ii. atunci se va afişa mesajul: Write error Atentie !. " Write error \n " }. } Func ia printf() este apelată din func ia selmes(). err[num]). Pointeri la pointeri Un tablou de pointeri este ceea ce numim pointeri la pointeri. Deoarece prioritatea operatorilor postfixa i este mai mare decât cea a operatorilor prefixa i. // Pointer la un tablou de 10 intregi Pentru aceasta trebuie inut cont de faptul că * este un operator prefixat. primul pointer con ine adresa celui de-al doilea pointer. Trebuie facută distinc ia între: int *v[10]. // Tablou de 10 pointeri la intregi int (*v)[10].5. Aceasta va afişa mesajul de eroare indexat prin numărul de eroare num.

Astfel. aşa cum se vede în exemplul următor: # include <stdio. p = s. de asemenea. *p = x. *p). Programul nu va oferi niciodată o valoare pointerului p dar va tipări valoarea lui x. Exemplu: Considerăm programul: # include <stdio. Ini ializarea pointerilor Secven a: char *p. /* p indica spre s */ este echivalentă cu: char *p = "Hello world \n".6. x = 10. t > -1. **q). Pentru a avea acces la o valoare indirectată printr-un pointer la pointer este necesară.float **newbalance. utilizarea operatorului * de două ori. t--) printf("%c". **q. p din ultima declara ie poate fi utilizat ca orice alt şir. în cazul programelor de dimensiuni mari./*Se afiseaza valoarea lui x*/ } 7. poate duce la erori care.h> void main (void) { int x. /*Se tipareste sirul in ordine directa si inversa*/ printf (p). programul următor este corect: # include <stdio. } Acest program atribuie valoarea 10 anumitor loca ii de memorie necunoscute. printf ("%d\n". sunt foarte greu de depistat şi pot avea urmări catastrofale. } Observa ie: Neini ializarea pointerilor sau ini ializarea greşită a acestora. x = 10. for(t = strlen(p)-1. void main (void) { register int t. *p.h> void main(void) { int x. Într-un program. p = &x. /* q preia adresa lui p */ printf(" %d ". *p. 131 .h> char *p = " Hello world \n ".p[t]). char s[] = "Hello world \n ". /* p preia adresa lui x */ q = &p.2.

memoria ce li se alocă este fixă pe tot timpul execu iei programului. necesită cunoaşterea în avans a cantită ii de memorie necesare în fiecare situa ie. } Func ia printf() nu va afişa niciodată valoarea lui x (care este 10). de multe ori. un program alocă memorie pentru diversele informa ii în spa iul 132 . Pentru a corecta programul. *p. În cazul • A doua metodă de alocare a memoriei. care se presupune că reprezintă o adresă şi nu o valoare. Pentru variabilele locale. dar va tipări o valoare necunoscută.*p). Instruc iunea atribuie valoarea 10 pointerului p. programul alocă memorie în spa iul stivei. constă în utilizarea func iilor de alocare dinamică malloc() şi free(). p = x.7. variabilelor globale. în timpul execu iei programului. Aceasta datorită atribuirii greşite: p = x. trebuie scris: p = &x. în C. 7. 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ă foloseşte variabilele globale şi locale.h> void main (void) { int x.2. Alocarea dinamică a memoriei Există două metode principale prin care un program C poate memora informa ii în memoria principală a calculatorului. x = 10. Prin această metodă.Exemplu: Considerăm acum următorul program: # include <stdio. Deşi variabilele locale sunt eficient de implementat. utilizarea acestora. printf("%d".

Prin aceasta. în scopul stabilirii şi men inerii unei liste a variabilelor memorate. Dacă nu este suficientă memorie pentru a satisfce cererea malloc(). Parametrul actual p trebuie să fie un pointer la un spa iu alocat anterior cu malloc(). apare un eşec şi malloc() returnează NULL. se poate folosi operatorul sizeof(). după care eliberează zona. programele pot deveni portabile pe o varietate de sisteme. Func ia free() returnează sistemului memoria alocată anterior cu malloc(). Exemplu: Următorul program va aloca memorie pentru 40 de întregi. func ia malloc() alocă o parte din memoria rămasă liberă. ceea ce înseamnă că trebuie utilizat un şablon explicit de tip atunci când pointerul returnat de malloc() se atribuie unui pointer de un alt tip. Se observă că stiva creşte în jos. aceasta deoarece adresele de retur şi variabilele locale corespunzătoare acestor func ii sunt salvate în stivă. De fiecare dată când se face un apel de eliberare a memoriei.h>. După eliberarea memoriei cu free(). calloc() sau realloc(). plasată între programul util şi memoria sa permanentă şi stivă. va afişa valoarea acestora. Acestea lucrează împreună şi utilizează zona de memorie liberă plasată între codul program şi memoria sa permanentă (fixă) şi vârful stivei. malloc() va returna un pointer la primul byte al zonei de memorie din heap ce a fost alocată. 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. Declararea func iei free() se realizează sub forma: free(void *p). aceasta se poate reutiliza folosind un apel malloc(). Aceasta întoarce un pointer de tip void. func ia free() eliberează memorie sistemului. Declararea func iei malloc() se face sub forma: void *malloc (int numar_de_bytes). Dacă apelul lui malloc() se execută cu succes. iar dimensiunea acesteia depinde de program. De fiecare dată când se face o cerere de memorie. utilizând free(): 133 . Pentru determinarea exactă a numărului de bytes necesari fiecărui tip de date.memoriei libere numită heap. Func ia free() eliberează spa iul indicat de p şi nu face nimic dacă p este NULL. Func iile malloc() şi free() Aceste func ii formează sistemul de alocare dinamică a memoriei în C şi fac parte din fisierul antet <stdlib.

De exemplu. t < 40. p = (int *) malloc(40*sizeof(int)). //Verificati neaparat daca p este un pointer corect else { for (t=0. calculatorul va salva şi va reface din stivă numai adresa structurii. următoarea instruc iune declară pe addr_pointer ca pointer la o dată de acest tip : struct addr *addr_pointer. se copiază vechiul con inut într-o altă zonă de memorie). func ia returnează un pointer la acel bloc (adresa primului octet din bloc). } } Func iile calloc() şi realloc() Func ia calloc() alocă un bloc de n zone de memorie. Pointeri la structuri Limbajul C recunoaşte pointerii la structuri în acelaşi mod în care se recunoaşte pointerii la orice alt tip de variabilă. Func ia realloc() primeşte 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. ++t) *(p + t) = t. O utilizare importantă a pointerilor la structură constă în realizarea apelului prin adresă într-o func ie. *(p + t)).h> # include <stdlib. for (t=0. Declararea func iei se face cu: void *realloc(void *ptr. conducând astfel la cresterea vitezei de executare a programului. 7. Când unei func ii i se transmite ca argument un pointer la o structură. if (!p) printf("Out of memory \n"). unsigned int dim).8. t<40. *p. unsigned int dim). free(p).h> void main(void) { int t. fiecare de dim octe i şi setează la 0 zonele de memorie.# include <stdio.2. ++t) printf("%d". Declararea unui pointer la structură se face plasând operatorul * în fa a numelui unei variabile structură. Declararea func iei se face cu: void *calloc(unsigned int n. 134 . pentru structura addr definită mai înainte.

se plasează operatorul & înaintea numelui variabilei structură.}. pentru o referire corectă a elementelor unei structuri utilizând pointerii sunt necesare paranteze. plasează adresa lui person în pointerul p. Pentru a referi elementul balance. } person.secunde = 0. time. void main() {struct tm time.minute = 0.Pentru a găsi adresa unei variabile structură. modernă. int minute. int secunde. /* se declara un pointer la structura */ atunci: p = &person. for (.balance Deoarece operatorul punct are prioritate mai mare decât operatorul *. struct tm { /* se defineste structura tm */ int ore.ore = 0. există două metode: Prima metodă utilizează referirea explicită a pointerului numestructură şi este considerată o metoda învechită (obsolete). De exemplu. time. char name[80]. // Structura time de tip tm time. iar a doua metodă. minutul şi secunda utilizând un timer software. se va scrie: (*p). # include <stdio. void afiseaza(). pentru referirea unui element al unei variabile structură dându-se un pointer la acea variabilă. struct balanta *p.h> void actualizeaza(). delay().referirea explicita prin pointeri */ struct tm *t. utilizează operatorul săgeată -> (minus urmat de mai mare). dându-se următorul fragment : struct balanta { float balance. examinăm următorul program care afişează ora.) { actualizeaza (&time). }} void actualizeaza(t) /*Versiunea 1. Exemplu: Pentru a vedea cum se utilizează un pointer-struct. Actualmente. afiseaza (&time).. { 135 .

ore este echivalent cu t -> ore Atunci programul de mai sus se poate rescrie sub forma: # include <stdio. } if ((*t). { printf ("%d : ".ore = 0. Elementele unei structuri pot fi referite utilizând în locul operatorului ". De exemplu : (*t).++t). (*t). (*t).} Pentru ajustarea duratei întârzierii se poate modifica valoarea contorului t al buclei.minute = 0. void afiseaza(). printf ("%d : ". În ambele func ii. (*t). printf ("\n").secunde == 60) { (*t).}.minute ++. operatorul "->". 136 . void main() {struct tm time.(*t). In interiorul func iei main() se declară structura "time" şi se ini ializează cu 00:00:00. time. Programul transmite adresa lui time func iilor actualizeaza() şi afiseaza().secunde = 0.ore). dar nu declară variabilele. delay().secunde = 0. for (t = 1. int secunde.secunde).minute).} void afiseaza(t) // Se defineste functia afiseaza() struct tm *t. printf ("%d ".ore ++. argumentul acestora este declarat a fi un pointer-structură de tip "tm".minute == 60) { (*t). time. // Declara structura time de tip tm time.".secunde ++. delay(). Se vede ca programul defineşte o structură globală numită tm. (*t). if ((*t).} void delay() /* Se defineste functia delay() */ { long int t.h> void actualizeaza(). (*t).} if ((*t).ore == 24) (*t). struct tm { /* se defineste structura tm */ int ore. Referirea fiecărui element se face prin intermediul acestui pointer.minute = 0. int minute.ore = 0.t<140000000.

. Dacă dorim o reprezentare contiguă 137 .} void delay() /* Se defineşte func ia delay() */ { long int t. delay().} void afiseaza(t) // Se defineste functia afiseaza() struct tm *t. printf ("%d : ". Tabloul de date definit în acest fel este şi el de acest tip nou. este foarte posibil să indicăm spre un element care a fost şters. t->ore). for (t = 1. 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 legături logice între ele.++t). pe care îl mai numim şi tip structurat. { t->secunde ++. Se ajunge astfel la no iunea de tablou. { printf ("%d : ". }} void actualizeaza(t) /*Versiunea 1. Folosind pointeri la tabloul de structuri. După cum s-a remarcat. Adesea.9. pe care le putem umple numai printr-o gestiune foarte precisă a pozi iilor din tablou. prin ştergerea unor elemente structură dintr-un tablou de structuri ob inem goluri în tablou. afiseaza (&time). t->minute). în exemplele de până acum am folosit structuri de tip static. t->secunde). } if (t->minute == 60) { t->minute = 0. ale cărui elemente sunt fiecare câte o structură. printf ("%d ".} 7.} if (t->ore == 24) t->ore = 0. Termenul structuri de date statice exprimă ideea că putem modifica cu uşurin ă valorile componentelor dar este foarte dificil să mărim numărul lor peste limita maximă declarată înainte de compilare.referirea explicita prin pointeri */ struct tm *t.t<140000000.2.for (. această grupă de date se repetă de mai multe ori.) { actualizeaza (&time). t->minute ++. if (t->secunde == 60) { t->secunde = 0. t->ore ++. Static se referă la faptul că tablourile de structuri au dimensiuni predefinite. printf ("\n"). Mai mult.

char city[15]. nu există nici o posibilitate de a mări tabloul fără a modifică şi apoi recompila sursa programului. char state[10]. Dacă în această aplica ie. cu numele addr_info. char street[30]. Rezultă că am definit un tablou static cu 100 de elemente. de exemplu). numite variabile dinamice. aceaste opera ii devin foarte anevoioase. se va aloca dinamic spa iu pentru respectivul element. Structurile dinamice se construiesc prin legarea componentelor structurii. } addr_info[SIZE]. va trebui să compactăm (sau să defragmentăm) tabloul la fiecare ştergere a unui element de tip structură. unsigned int zip. Tabloul trebuie redeclarat cu o dimansiune mai mare (în cazul nostru prin #define SIZE 200. se va crea elementul prin înscrierea informa iilor corespunzătoare şi se va lega în listă. la care fiecare element este o structură cu şablonul addr. 2. recompilare şi reintroducerea datelor care fuseseră deja introduse până în momentul în care s-a constatat necesitatea măririi tabloului. Acest lucru prezentă două inconveniente (vezi [Mocanu. Când un element este scos din listă spa iul de memorie ocupat de acesta va fi eliberat şi se vor reface legăturile.în memorie. dacă dorim să schimbăm ordinea în care s-au stocat elementele din tablou sau să inserăm într-o pozi ie intermediară un element nou. apoi se recompilează programul şi abia apoi se execută cu succes. 2001]): 1. Eliminarea neajunsurilor prezentate mai sus se face prin implementarea listelor cu ajutorul unor structuri de date dinamice. constatăm că avem nevoie de mai mult de 100 de rezervări de memorie. Mai mult.Este posibil ca programul sursă să nu fie disponibil. Când apare necesitatea introducerii unui element în listă. O variabilă dinamică ce intră în componen a unor structuri de date dinamice (nod) prezintă în structura sa două păr i: 138 .Execu ia şi ob inerea rezultatelor sunt amânate şi produc întârzieri pentru modificarea programului sursă. chiar în timpul execu iei programului. Într-un exemplu anterior am folosit secven a # define SIZE 100 struct addr { char name[20].

float. Determinarea ultimului element se poate face prin parcurgerea secven ială a listei până la întâlnirea nodului cu pointerul spre nodul următor cu valoarea NULL. Partea de informa ie (info) ce poate apar ine unui tip simplu (int. La rândul lor. de tipul unui tablou unidimensional (vector). există un nod spre care nu pointează nici un alt nod. Elementele unei liste se numesc noduri. acesta fiin primul nod al listei. listele pot fi grupate în mai multe categorii. adăugare/ştergere/modificare au unui element (nod). etc. care are un prim element şi un ultim element. de la primul către ultimul.1. ce realizează înlăn uirea variabilelor dinamice în structuri de date dinamice. De asemenea. Dintre structurile de date dinamice. Principalele opera ii care se pot efectua asupra unei liste sunt: crearea listei. Există un nod pentru care pointerul spre nodul următor este NULL.) conform cu necesită ile problemei. de obicei de acelaşi tip. char. Acesta este ultimul nod al listei simplu înlăn uite (sau simplu legate). cele mai importante fiind listele simplu înlăn uite. Lista este o structură liniară. O listă simplu înlăn uită poate fi identificată în mod unic prin primul element al listei. info next info next Listă simplă înlăn 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ă către primul. cu 139 . Partea de legătură (link) ce con ine cel pu in un pointer de legătură (next) la o altă variabilă dinamică. Lista simplu înlăn uită este cel mai simplu tip de listă din punctul de vedere al legării elementelor: legătura între elemente este într-un singur sens. double. 2. Celelalte elemente au un predecesor şi un succesor. Procedurile necesare pentru crearea şi utilizarea unei liste circulare sunt extrem de asemănătoare cu cele de la liste liniare. listele circulare şi listele dublu legate. cele mai simple şi mai utilizate sunt listele. accesul la un element şi ştergerea în totalitate a listei.

info next NULL info next previous Listă dublu legată info NULL previous Pointerul next indică spre următorul nod. Particularizările se vor face pe exemplul bazei de date construite anterior. }. Pentru aceasta vom utiliza două variabile globale de tip pointer. Vom prezenta în continuare modul în care putem proiecta func iile principale care ac ionează asupra unei structuri dinamice. struct addr *next. Tipul unui nod se declară în felul următor: General struct tip_nod { declaratii struct tip_nod *next.singura deosebire că ultimul element nu are adresa de pointer vid (NULL) ci adresa primului element. iar câmpul previous indică spre câmpul anterior. 140 . una fiind imaginea în oglindă a celeilalte. Vom denumi aceste variabile first respectiv last. una care pointează spre primul nod al listei iar cealaltă spre ultimul nod al listei. Particular struct addr { char name[20]. char street[30]. char city[15]. Ele pot fi privite ca structuri dinamice ce combină două liste liniare simplu înlăn uite ce partajează acelaşi câmp comun info. 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. }. char state[10]. unsigned int zip.

Zona alocată prin intermediul func iei malloc() se poate elibera folosind func ia free(). Crearea unei liste simplu înlăn uite în memorie (pentru prima oară). Afişarea pe ecran. vom folosi atât modularizarea internă prin construirea mai multor func ii cât şi o modularizare externă prin izolarea programului principal de restul func iilor care se folosesc. Pentru o mai bună proiectare a programului.2. Vom descrie pe rând func iile care îndeplinesc sarcinile enumerate mai sus. Programul va avea următoarele facilită i: 1.1. care să folosească liste dublu înlăn uite sau circulare. Programul este prevăzut cu o intefa ă simplă prin care utilizatorul poate alege dintre op iunile pe care le are la dispozi ie. prin tastarea ini ialelor comenzilor. înregistrare cu înregistrare. Ştergerea unor înregistrări a) Ştergerea primului nod al listei b) Ştergerea unui nod intern al listei c) Ştergerea ultimului nod al listei 3. acestea se lansează în execu ie. Inserarea unor înregistrări (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 (adăugare la coada listei) 2. Interfa a este sub forma unui meniu din care. Programul poate fi extins ulterior pentru structuri dinamice mai complexe. a bazei de date con inute în lista dinamică. 2. Salvarea din memorie pe disc a unei liste simplu înlăn uite 4. Propunem conceperea unui program asemănător cu programul de exploatare a unei baze de date conceput anterior dar care să folosească în locul unui tablou static o listă simplu înlăn uită. Citirea de pe disc în memorie a bazei de date salvate anterior 5. 141 . Exploatarea unei liste simplu înlăn uite în memorie: 2.Atât la crearea unei liste cât şi la inserarea unui nod se va apela func ia malloc() pentru a rezerva spa iu de memorie pentru un nod.

break. .Bază de date cu listă simplu înlăn uită Afişare pe ecran a înregistrărilor 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 . case 'i' : insert().) { choice = menu(). case 'd' : display_list(). break.}}} Fişierul header local. 142 .h este: # include <stdio. case 'l' : loadf_list(). break. break.înregistrare intermediară Exemplu: Programul principal bd_main. case 'q' : exit(0). char street[30]. char city[15]. switch (choice) { case 'c' : create_list().c # include "local.h> typedef struct addr { char name[20].h> # include <ctype. case 'e' : erase(). for (. break. break.h> # include <stdlib.ultima înregistrare . case 's' : savef_list().prima înregistrare .h" void main() { char choice. char state[10]. break.h> # include <string.

typedef struct addr { char name[20]. struct addr *next. *last.h care con ine: # include <stdio.unsigned int zip.c se pot compila împreună. struct addr *next. savef_list().c unde sunt colectate toate func iile pe care le folosim.h> extern FILE *fp. Cu extern se definesc prototipurile unor func ii care nu se găsesc în fişierul bd_main.c şi bd_bib.h> # include <string. prin comanda typedef. printf ("(L)oad list from file\n"). extern TNOD *first. unsigned int zip. char state[10].}TNOD. char street[30]. Interfa a cu utilizatorul Aceasta constă într-un meniu principal. erase().}TNOD. TNOD *first.ch.h> # include <stdlib. se defineşte un nou tip de date numit TNOD. First şi last sunt pointeri la structuri de acest tip iar fp este pointer la fişier (file pointer). extern void display_list(). Cu ajutorul acestui fişier header se realizează definirea şablonului structurii addr şi apoi. *last. rezultând un singur fişier executabil. extern TNOD *add_nod(). şi din două submeniuri pentru opera iile de ştergere şi inserare de înregistrări. Acest fişier are la rândul său un fişier header numit local1. char city[15]. extern char menu(). /* Functia menu() principala */ char menu() { char s[5]. 143 . care permite accesarea func iilor principale. do { printf ("\n(C)reate new list\n").c ci în fişierul bd_bib.h> # include <ctype. insert(). FILE *fp. printf ("(S)ave to file\n"). Prin cele două fişiere header cele două fişiere sursă bd_main. extern int loadf_list(). extern int create_list().

printf ("(I)ntermediate delete\n"). } while (!strrchr("fliq". gets(s). } //meniu functia de inserare char menu_insert() { char s[5].ch=s[0]. } //meniu functia de stergere char menu_del() { char s[5].} else {printf ("File read error\n"). return tolower(ch). printf ("(I)nsert record\n"). printf ("(L)ast record delete\n"). ch=s[0]. Func ia loadf_nod() citeşte o înregistrare (record) din fişier. do { printf ("(E)ntire list delete\n").ch=s[0]. printf (" Option ? "). } while (!strrchr("clsdieq". returnând 1 în caz de reuşită.ch)).}} 144 .dat care se crează la prima salvare a listei dinamice din memorie. gets(s).printf ("(D)isplay list\n").fp)==1) return 1.ch. do { printf ("(F)irst record insert\n"). printf ("(E)rase\n"). return tolower(ch). printf ("(L)ast record insert\n"). else if (feof(fp)) {fclose (fp). return tolower(ch). printf (" Alegeti optiunea: "). gets(s). } while (!strrchr("efliq". printf (" Option ? "). printf ("(Q)uit\n"). /* Functia loadf_nod() from file*/ int loadf_nod(TNOD *p) {if (fread(p. printf ("(Q)uit\n"). printf ("(I)ntermediate insert\n").sizeof(TNOD). return -1. printf ("(F)irst record delete\n"). printf ("(Q)uit\n"). } Încărcarea bazei de date de pe disc Baza de date este înregistrată pe disc în fişierul maillist. -1 dacă se ajunge la sfârşitul fişierului şi 0 în cazul în care există o eroare de citire de pe disc.1.ch. return 0.ch)).ch)).

return 0. Ea returnează 0 145 . fclose (fp). do { if(fwrite(p.dat. Spre deosebire de func ia loadf_list() care apela la func ia loadf_nod().last=p. if ((fp = fopen("maillist". first=last=NULL. first->next=NULL.return 0. "wb")) == NULL) { printf (" Cannot open file\n "). lista fiind vidă.} if (p==NULL){ printf("Memorie insuficienta pentru lista\n").i. Crearea unei liste se face prin func ia create_list. fclose (fp). return 0. TNOD *p. if ((fp = fopen("maillist". 1.} p=first.} while (((p=(TNOD *)malloc(n))!=NULL)&&((i=loadf_nod(p))==1)) if (first==NULL){ /* prima creare */ first=last=p.} while (p!=NULL)./* Functia loadf_list() from file */ int loadf_list() { int n=sizeof(TNOD). Salvarea bazei de date pe disc Reprezintă opera iunea inversă celei de citire.return 0. last->next=NULL."rb")) == NULL) { printf("Cannot open file\n ").return i.return 1. Lista simplu înlăn uită din memorie se salvează în întregime pe disc în fişierul maillist. În pointerii first şi last se stochează în permanen ă adresele primei şi ultimei înregistrări (nod sau structură).} Crearea unei liste simple înlăn uite La început variabilele first şi last au valoarea NULL.} p=p->next.} Func ia loadf_list() alocă fiecare înregistrare citită de pe disc unui nod al listei dinamice construite în memorie. sizeof(TNOD).} else { last->next=p. 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.fp) !=1) {printf (" File write error \n ").} free(p).

printf("%15s".} /* Functia display_list() */ void display_list() { TNOD *p. TNOD *p.} else { last->next=p."Street"). printf("%15s". first->next=NULL. while ((p=(TNOD *)malloc(n))!=NULL) {p=add_nod(p).p->name). last=p."State").} // afisare o singura inregistrare (nod) void disp_nod(TNOD *p) { printf("%20s". if ((ch=getchar())=='x') break.}} Afişarea listei dinamice din memorie Prin această opera ie. last->next=NULL. printf("%10s"."Zip"). printf("%10s". fără a face o citire a unei baze de date create anterior. printf("%5d"."City")."Name"). /* Functia create_list() new */ void create_list() { int n=sizeof(TNOD).p->state). printf("%5s\n". 146 .p->city).} if (p==NULL){ printf("Memorie insuficienta pentru lista\n"). // functie de afisare antet void disp_antet() { printf("\n%20s". free(p). se afişează secven ial toate înregistrările con inute în nodurile listei pornind de la prima şi terminând cu ultima. printf("%30s". if (first==NULL){ /* prima creare */ first=last=p.p->street).pentru eroare şi 1 dacă opera ia reuşeşte. first=last=NULL. Aceasta este prima func ie care se apelează când construim o bază de date nouă. Prin această func ie se ini ializează o listă dinamică în memorie. printf("%30s". char ch='c'. disp_antet(). realizată de func ia display_list().p->zip).} printf ("Exit? (x): ").

} Func ia display_list() apelează la func ia disp_nod() care afişeză o singură înregistrare. /* ne pozitionam pe ultimul nod */ p=first.) { choice = menu_insert(). printf("Exit? (x): "). . switch (choice) { case 'f' : ins_first(). Inserarea unor noduri în lista dinamică Această opera ie presupune introducerea unor noduri (înregistrări) fie la începutul listei înaintea primului nod.s[2]. break. gets(s). last->next=p. fie la sfârşitul său (adăugare) după ultimul nod. else printf("Lista vida !\n").s[2]. Dacă este nevoie de afişarea unui cap de tabel (antet) se apelează la func ia disp_antet(). p->next=NULL. getchar(). p=p->next. // functia de inserare void insert() { char choice. case 'i' : ins_int().}} /* Functia insert_last() */ void ins_last() { int n=sizeof(TNOD). break. TNOD *p. case 'q' : break. char ch='c'. Func iile listate mai jos realizează aceste sarcini. p=add_nod(p). while (p->next!=NULL) p=p->next.}} /* Functia insert_first() */ void ins_first() { int n=sizeof(TNOD). char ch='c'. last=p. fie între două noduri vecine nesituate la extremită i.ch=s[0]. 147 . for (.p=first. if (p!=NULL) do { disp_nod(p).} while (p!=NULL).} break. case 'l' : ins_last(). // se creaza lista in memorie while (ch!='x') { p=(TNOD *)malloc(n). break.

// func ia de ştergere void erase() { char choice. first=p.ch=s[0]. Ştergerea unor noduri din lista dinamică Nodurile pe care dorim să le ştergem pot fi interioare sau se pot afla la extremită i. . este nevoie să modificăm pointerii globali first şi last. for (. *pi.TNOD *p. este nevoie să modificăm pointerii first şi last .} pi=(TNOD *)malloc(sizeof(TNOD)). s[2]. 148 .} La inserarea unui nod. ci numai se refac legăturile şi se eliberează cu func ia free() zona de memorie alocată în prealabil (prin inserare sau creare) cu func ia malloc(). while ((p!=NULL)&&(ch!='y')) { disp_nod(p). În toate cazurile este nevoie să refacem legăturile distruse după dispari ia prin ştergere a unor noduri. Mai mult. disp_antet(). pi->next=p->next. printf("Here ? [y]: "). este nevoie ca acesta să fie legat de cele între care se introduce cu ajutorul pointerilor next. switch (choice) { case 'e' : del_list(). printf("Exit? (x): ").ch=s[0]. gets(s). avem op iunea de a şterge întreaga listă din memorie în scopul înlocuirii în totalitate a datelor con inute în înregistrări. p->next=pi. pi=add_nod(pi). §tergerea nu este efectivă.) { choice = menu_del(). while (ch!='x') { p=(TNOD *)malloc(n). p=add_nod(p). p->next=first. char ch='n'. break. gets(s). if (ch!='y') p=p->next. p=first. În acest caz. Dacă se doreşte ca nodul inserat să fie primul sau ultimul din listă.}} // inserare dupa inregistrarea afisata void ins_int() { TNOD *p.

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

lista_parametri. Lista parametrilor este închisă între paranteze. Definirea unei func ii C se realizează după următorul format general: nume_func ie (lista_parametri) declara ii_parametri { declara ii _locale instruc iuni } tip sau. float.Capitolul VIII FUNC II 8. este o listă de nume de variabile separate prin virgulă care vor primi valorile argumentelor în momentul apelării acesteia.). int. Dacă pentru o func ie nu se specifică nici un tip. structură etc. fie direct în lista parametrilor. însă. 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 utilizând instruc iunea return.1. implicit se consideră că func ia întoarce o valoare întreagă. de multe ori valorile returnate de func ii sunt ignorate.) sau un tip derivat (pointer. Chiar dacă o func ie nu are parametri. declarată şi apelată. după apel. În C o func ie poate fi definită. În C orice func ie "întoarce" (returnează). double etc. parantezele nu trebuie să lipsească. o valoare al cărui tip trebuie cunoscut. 150 . atunci. 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 practică. Tipul acestor parametri este descris fie în paragraful declara ii_parametri. Standardul limbajului C permite chiar declararea explicită a func iilor care nu returnează valori ca fiind de tip void. Lista parametrilor. Acesta poate fi un tip de bază (char.

2 warning(s) Aceste avertismente sunt generate deoarece. la primul pas al compilării.c(13): warning C4244: 'return': conversion from 'int' to 'float'. atunci ei sunt considera i implicit de tip int. possible loss of data test.obj .. În cazul compilatoarelor moderne.h> 151 .c(11): warning C4244: 'return': conversion from 'int' to 'float'. programul următor va genera două avertismente. 4). b. { if (a > b) return (a). Exemplu: # include <stdio. la parcurgerea liniei de declarare a func iei: float max(a. Programul. } În cazul în care tipul parametrilor formali ai unei func ii nu se declară. // Prototipul functiei max() float x. } float max(a. test.. } În urma compilării va rezulta: Compiling.b) parametrii formali a şi b sunt considera i de tip întreg. possible loss of data C:\cpp_examples\test. b) int a. b). b) float a. } sau int max(int a. else return (b). b. else return (b). else return (b). se poate defini sub forma: int max(a.De exemplu. modificat ca mai jos. care returnează cel mai mare dintre numerele întregi a şi b. void main() { x = max(3. func ia max(a.x). va duce la o compilare fără probleme: Exemplu: # include <stdio. { if (a > b) return (a). printf("max= %d". int b) { if (a > b) return (a).c C:\cpp_examples\test.0 error(s).h> float max().

float max().2. 4. printf("max= %d\n".x). } float max(float a. else return (b).2. Reîntoarcerea dintr-o func ie Mai intâi precizăm că instruc iunea return are două utilizări importante: ♦ return determină ieşirea imediată din func ia în care se află instruc iunea şi reîntoarcerea în programul apelant.h> int max(). } int max(a. void main() { x = max(3. Exemplu: Aceasta func ie tipăreşte un şir în ordine inversă: # include <string.1). void main() { char s[10]. } Tot corect va rula şi programul: # include <stdio. float b) { if (a > b) return (a). int x. else return (b). ♦ return poate fi folosită pentru a întoarce o valoare. 8.x).4). void main() { x = max(-3.b) { if (a > b) return (a).h> void afis_invers(char s[]). printf("max= %d\n". Reîntoarcerea dintr-o func ie în programul apelant (func ia apelantă) se poate face în două moduri: a) După parcurgerea codului corespunzător func iei se revine în programul apelant la instruc iunea imediat următoare. float x. } Se observă că nu mai este nevoie de declararea explicită a parametrilor formali de tip întreg a şi b. 152 .

Exemplu: Func ia următoare afişează rezultatele ridicării unui număr întreg la o putere întreagă pozitivă: power (baza. cu excep ia celor daclarate a fi de tip void. Această valoare este fie explicit specificată prin return. Func ia return poate fi folosită fără nici o valoare asociată. afis_invers(s). Exemplu: sqrt() şi sin() returnează respectiv radăcina pătrată şi sinusul argumentului. printf (" Rezultatul este %d \n". dar nu returnează nici o valoare. scanf("%s". /* Functia se termina daca exp e negativ */ i = 1. Valori returnate Toate func iile. Dacă o func ie este declară ta ca fiind de tip void.3. aceasta poate fi folosită în orice expresie C. fie este zero dacă nu se utilizează instruc iunea return. &baza. printf("\n"). instruc iunea return determină terminarea func iei înainte ca sistemul să întâlnească }. Func iile care nu sunt de tip void se pot împăr 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. } caracrere de la b) Al doilea mod de întoarcere dintr-o func ie se realizează utilizând func ia return. exp){ int baza.printf("Introduceti un sir de tastatura (max 10)\n"). instruc iunea: swap(x. O func ie poate con ine mai multe instruc iuni return. i. returnează o valoare. care pot simplifica anumite algoritme. t--) printf("%c". i).y) = 100. t >= 0. for (t = strlen(s)-1. scanf("%d %d". 8. exp. if (exp < 0) return. } Dacă exponentul exp este negativ. De exemplu. &exp). exp--) i = baza * i. O func ie nu poate fi membrul stâng într-o expresie de atribuire. s[t]). este greşită. exp. for (. } void afis_invers(char s[]) { register int t. 2) A doua categorie de func ii sunt cele care manipulează informa ii şi întorc o valoare care arată reuşita sau eşecul acestei 153 .s).

Domeniul unei func ii În C.h> mul(). 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. de obicei. număr care. În linia b. Blocul de instruc iuni care descrie corpul unei func ii este separat de restul programului şi dacă acesta nu utilizează variabile globale sau date. func ia printf() întoarce numărul de caractere tipărite.y). y. (De exemplu. fwrite() întoarce numărul de octe i înscrişi (ceru i să se înscrie). nu are o utilizare ulterioară. } Linia a atribuie valoarea returnată de mul() lui z. //. y = 20.manipulări. deoarece nu se atribuie nici unei variabile ce va fi utilizată în altă parte a programului. 3) A treia categorie de func ii sunt cele care nu trebuie să întoarcă o valoare explicită.4. De exemplu. orice altă valoare indică apari ia unei erori. Exemplu: Considerăm următorul program care utilizează func ia mul(): # include <stdio. //. dar aceasta este utilizată de printf(). calculatorul va ignora valoarea returnată. deoarece cele două func ii au scopuri diferite. valoarea returnată nu este atribuită.al treilea apel al lui mul() } mul(a. y).primul apel al lui mul() printf("%d\n". Un exemplu este fwrite() folosită pentru a scrie informa ii pe disk. fiecare func ie este un bloc de instruc iuni.b) // Se defineste functia mul() { return a*b. mul(x. cu excep ia instruc iunii de apel al acelei func ii. Dacă scrierea se face cu succes.y)). z.al doilea apel al lui mul() mul(x. x = 10. 154 . Dacă pentru o func ie care returnează o valoare nu se specifică o opera ie de atribuire. In linia c valoarea returnată este pierdută. Codul şi datele care sunt definite în interiorul unei func ii nu pot interac iona cu codul şi datele definite în altă func ie. z = mul(x. 8. el nici nu poate afecta. nici nu va fi afectat de alte păr i ale programului. /. void main (void){ int x. nu putem utiliza goto pentru a realiza saltul dintr-o func ie în mijlocul unei alte func ii).

întrucât C presupune că toate variabilele neglobale sunt prin defini ie (implicit) variabile locale. De obicei. Ea poate fi declarată şi register.În cadrul unei func ii se deosebesc trei tipuri de variabile. parametri formali şi variabile globale. o dată în func1() şi o dată în func2(). Mai exact. deci au atributul auto. variabilele locale utilizate în interiorul unei func ii se declară la începutul blocului de cod al acestei func ii. x = 10. Cu toate acestea. acest cuvânt cheie nu se utilizează. valoarea sa păstrându-se şi la ieşirea din func ie. 8.4. deci se stochează în memoria stivă. } func2() { int x. astfel: variabile locale. Exemplu: func1() { int x. x = -199.1. Variabile locale Variabilele declarate în interiorul unei func ii se numesc variabile locale. caz în care se stochează în regiştrii interni ai microprocesorului sau poate fi declarată static. deoarece fiecare x este cunoscut numai în blocul în interiorul căruia a fost declarat. o variabilă locală este auto. variabilele locale există numai pe durata execu iei blocului de cod în care acestea au fost daclarate. deoarece o variabilă locală poate fi declarată 155 . blocurile de program în care se declară variabilele locale sunt func iile. deci o variabilă locală este creată la intrarea în blocul său şi distrusă la ieşire. Variabilele locale nu sunt cunoscute în afara blocului în care au fost daclarate. Acest lucru nu este neapărat necesar. caz în care se stochează în memoria de date sau statică. Limbajul C con ine cuvântul cheie auto. Implicit. domeniul lor limitându-se numai la acest bloc. De obicei. care poate fi folosit pentru declararea de variabile locale. Domeniul unei func ii determină modul în care alte păr i ale programului pot avea acces la aceste trei tipuri de variabile stabilind şi durata de via ă a acestora. } Aici variabila întreagă x este declarată de două ori. x din func1() nu are nici o legatură cu x din func2(). Variabilele locale pot fi referite numai prin instruc iuni interioare blocului în care au fost daclarate aceste variabile.

&c. dar înainte de a fi folosită. &s). Astfel. caracterul c 156 . /* Se prelucreaza numerele */ } } Aici. //Se preia optiunea de la tastatura /* Daca raspunsul este yes */ if (ch == 'y') { char s[80]. func() creează variabila locală s la intrarea în blocul de cod a lui if şi o distruge la ieşirea din acesta. Mai mult. Exemplu: Considerăm următoarea func ie: func (){ char ch.char c) { while (*s) if (*s == c) return 1. gets (s). chiar din altă parte a func iei func() care o con ine.oriunde în interiorul blocului în care se utilizează. Aceste variabile se numesc parametri formali ai func iei. c. scanf("%c %s". /* s se creeaza numai dupa intrarea in acest bloc */ printf (" Introduceti numerele: \n ").h> int func (char s[10]. variabilele locale nu pot re ine valorile lor după încheierea apelului func iei. prelucreaza_nr (s). atunci aceasta trebuie să declare variabilele care vor accepta (primi) valorile argumentelor. Parametri formali Dacă o func ie va folosi argumente. } void main() { char s[10].4. Parametrii formali ai func iei se comportă ca orice altă variabilă locală din interiorul func iei. 8.2. Exemplu: /* Func ia următoare întoarce 1 dacă apar ine şirului s altfel întoarce 0 */ # include <stdio. return 0. con inutul lor este pierdut o dată ce calculatorul părăseste blocul. Declararea parametrilor formali se face după numele func iei şi înaintea corpului propriu-zis al func iei. Deoarece calculatorul creează şi distruge variabilele locale la fiecare intrare şi ieşire din blocul în care acestea sunt daclarate. else s++. ch = getche(). printf (" Continuam (y / n) ? : "). s este cunoscută numai în interiorul blocului if şi nu poate fi referită din altă parte.

. . . şi înainte de prima lor utilizare. Aceasta func ie întoarce 1 dacă caracterul c apar ine şirului şi 0 dacă c nu apar ine şirului. . } 8. declararea parametrilor func iei func() de mai sus se poate face şi sub forma: func (char *s. c)) printf("Caracterul se afla in sir\n"). else printf("Caracterul NU se afla in sir\n"). . } func1() /* Se defineste functia func1() */ { int temp.count). variabilele globale vor păstra valorile lor pe durata execu iei complete a programului. func2(). } Func ia func() are doi parametri: s şi c. variabilele globale se plasează la începutul unui program. func1(). Un al doilea mod (recomandat de ANSI-C în 1989) de a declara parametrii unei func ii constă în declararea completă a fiecărui parametru în interiorul parantezelor asociate func iei. . deci se stochează în memoria statică.3. char c) { . . . De asemenea.if (func(s. Aceşti parametri formali pot fi utiliza i ca orice altă variabilă locală. . Variabilele globale pot fi plasate în orice parte a programului.4. // Se va afisa 100 } func2() /* se defineste functia func2() */ { int count. Variabile globale Spre deosebire de variabilele locale. Variabilele globale se creează prin declararea lor în afara oricărei func ii (inclusiv main()). /* count este local */ 157 . De obicei. /* temp preia variabila globala count */ temp = count. Exemplu: int count. variabilele globale sunt cunoscute întregului modul program şi pot fi utilizate de orice parte a programului. /* count este global */ void main (void) { count = 100. evident în afara oricărei func ii. Precizăm că argumentele cu care se va apela func ia trebuie să aibă acelaşi tip cu parametrii formali declara i în func ie. De exemplu. printf ("count is %d". mai exact înaintea func iei main(). .

vom afişa şi loca iile de memorie pentru fiecare tip de variabilă x. } Se observă că. Variabilele globale sunt memorate într-o zonă fixă de memorie destinată special acestui scop (memoria statică). count < 10. Un alt exemplu util şi pentru în elegerea lucrului cu pointeri este următorul: se declară o variabilă globală x. rămânând în această zonă pe parcursul întregii execu ii a programului. dacă o variabilă globală şi o variabilă locală au acelaşi nume. x. Func ia func2() a declarat o variabilă locală count. Pentru a vedea şi modul în care se stochează în memorie aceste variabile. func2() se va referi numai la variabila locală count şi nu la variabila globală count declarată la începutul programului. Reamintim că. Variabilele declarate explicit extern sunt tot variabile globale. // z este pointer *(&z) = z.for (count = 1. 158 . din care deducem că: &* = *& = identitate &(*z)= z. 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. ci şi tuturor modulelor în care au fost declarate de tip extern. pointer sau variabilă). dar accesibile nu numai modulului program în care au fost declarate. count ++) printf ("%2d\n" . şi apoi două variabile locale cu acelaşi nume. // z este o variabila de un anume tip (z este de alt tip în fiecare din egalită ile de mai sus. ambele o folosesc. Deci. count). care se vor “ascunde“ una pe cealaltă. Re inem că. în general. deşi nici func ia main() şi nici func ia func1() nu au declarat variabila count. precum şi pentru pointerul p corespunzător. Când se referă la count. 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. o variabilă locală ascunde o variabilă globală.

*p.. p. . x = 1. *r. /* x este global */ void main(void) { int *p = &x. *r. &p).. ... *q. ... . . .. .Adresa Memoria . &p)... &x. ..x. .. // 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". anume x. q=&p &x=adresa x &p=adresa p &q=adresa q # include <stdio. /* Acest prim x este o variabila locala ce o ascunde pe cea globala */ p = &x.. p.. .&x. q = &p. . p=&x . .. .. . . ..x. // q retine adresa pointerului p r = *q. . *p. *p. printf("x=%d &x=%p *p=%d p=%p &p=%p\n".. q. . /* p este o variabila pointer catre un intreg */ void **q. . . . variabila p este declarată variabilă pointer către o dată de tip întreg şi este ini ializată cu adresa variabilei spre punctează. { int x.. ob inem următorul 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. . ... Pointerul q este declarat ca un pointer la un alt pointer.. .. 159 ... p.. } } În urma execu iei programului. .} { int x. &p).. printf("x=%d &x=%p *p=%d p=%p &p=%p\n". . . /* Acest al doilea x ascunde prima variabila locala x */ x = 2. /* Pointerul p retine adresa variabilei x */ printf("x=%d &x=%p *p=%d p=%p &p=%p\n".h> int x = 34. .. x .&x. // Se atribuie valoarea 2 acestui x p = &x..x. .. . &q)....

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 ataşate 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

Acelaşi lucru se face pentru celelalte două variabile locale. Din interpretarea rezultatelor de mai sus putem trage următoarele 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 făcut atribuirea p = &x; Se mai observă cum pointerul p, care ini ial indica spre variabila globală x, este încărcat cu adresa de memorie 4336176 (00426A54 H), pe când în cazurile când 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, atât cât sunt aloca i pentru variabila de tip întreg. Se observă că variabila globală se află într-o altă zonă de memorie decât 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);}

Când se doreşte realizarea produsului a oricăror doi întregi x şi y se utilizează varianta generală a func iei, iar când se doreşte 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 acelaşi 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 foloseşte 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 fiecărui argument în (la) câte un parametru formal al func iei apelate. Exemplu: Se apelează o func ie ce calculeaza pătratul unui număr î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ă, schimbările survenite asupra parametrului formal x nu afectează variabila utilizată pentru apelul func iei (schimbările 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 următor 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 următor:
# 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 având ca argumente tablouri
Când se apelează o func ie având ca argument un tablou, acesteia i se va transmite un pointer la primul element al tabloului. Reamintim că în C numele unui tablou fără 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 fără 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 utilizând []. Toate cele trei metode de declarare a unui tablou ca parametru produc acelaşi 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 afişarea tuturor numerelor prime cuprinse între două limite întregi. Programul principal apelează două func ii: nr_prim() returnează 1 dacă argumentul său întreg este prim şi 0 dacă nu este prim; numerele prime sunt grupate într-un vector, care se afişează 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 când 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 următor 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 rulării programului va fi:
abcdefghijklmnoprstuvxyzw ABCDEFGHIJKLMNOPRSTUVXYZW ABCDEFGHIJKLMNOPRSTUVXYZW

Exemplu: Dacă nu dorim să se întâmple 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

{ char ch. func ia se încheie cu return. xgets() introduce terminatorul de şir. t < 80. /* terminare sir return. for (t = 0. switch (ch) { case '\n' : s[t] = '\0'. Când se apasă CR. 166 . prin defini ie. int t. deoarece programul nu-i schimbă valoarea. ++t) printf("%c". Dacă se introduce un spa iu. s[t]. } //Nu se modifica continutul sirului sir Rezultatul rulării va fi de această dată: abcbdefghijklmnoprstuvxyzw ABCBDEFGHIJKLMNOPRSTUVXYZW abcbdefghijklmnoprstuvxyzw În aceasta variantă con inutul tabloului ramâne nemodificat./* Se defineste functia afis_litmari() */ { register int t.toupper(s[t])). este un pointer la caracter. care. xgets(s) char *s. Prezentăm o variantă simplificată a acestei func ii numită xgets(). deoarece numele programului este codificat ca primul argument. 8. } } s[80] ='\0'. ++t) { ch = getchar(). Parametrul argc con ine numărul argumentelor din linia de comandă şi este un întreg. Argumentele argc şi argv ale func iei main() Singurele argumente pe care le poate avea func ia main() sunt argv şi argc.7. Întotdeauna acesta va fi cel pu in 1. prin func ia for este de 80. for (t = 0. contorul t este redus cu 1. default: s[t] = ch. case '\b': if (t > 0) t--. Numărul caracterelor introduse de la tastatură. } */ Func ia xgets() trebuie apelată având ca argument un tablou de caractere. Dacă se introduc mai mult de 80 de caractere. break. Un exemplu clasic de transmitere a tablourilor într-o func ie îl constituie func ia gets() din biblioteca C standard.

pentru a executa programul. Evita i folosirea sa fără paranteze. {if (argc != 2) { printf (" Ati uitat sa va introduceti numele \n"). Exemplu: Următorul program arată modul de utilizare al argumentelor linie_comanda şi va afişa Hello urmat de numele dumneavoastră. Fiecare element din acest tablou indică spre un argument linie_comanda. numărul introdus pe ecran. } printf ("Hello %s !". Dacă şirul "display" apare ca al doilea argument_comanda. ve i tipări ARG_LC DAN. sau. Parametrul argv[] se declară. de obicei. imediat după numele programului: # include <stdio. adică char *argv. return. argv[1] va indica spre primul argument etc. sub forma char *argv[]. argv[1]). astfel: argv[0] va indica spre primul şir. Următorul program numit "nrinvers" numără invers de la o valoare specificată prin linia de comandă şi transmite un beep când ajunge la zero. Precizăm că programul converteşte primul argument. Argumentele linie_comanda trebuie separate prin spa iu sau TAB şi nu prin virgulă. } Dacă acest program se numeşte ARG_LC. Toate argumentele linie_comanda sunt şiruri. în linia de comandă. char *argv[]) /* nrinvers */ { int disp. care con ine numărul la un întreg folosind func ia standard atoi()..h> void main (argc. atunci. şi reprezintă un tablou de lungime nedeterminată.Parametrul argv este un pointer la un tablou de pointeri la caractere. 167 . Accesul la elementele lui argv[] se realizează prin indexarea acestuia. # include <stdio. count. care este întotdeauna numele programului. argv) // Numele programului int argc.h> # include <stdlib. char *argv[]. programul va afişa.h> void main(int argc.C şi numele dumneavoastră este DAN.h> # include <string. Ieşirea programului va fi Hello DAN !. dacă vă introduce i numele. mai precis reprezintă un tablou de pointeri. de asemenea. if (argc < 2) { printf ("Trebuie introdusa lungimea numarului in linia de comanda\n").

Acest lucru este necesar deoarece compilatorul nu cunoaşte tipul datei întoarse de func ie şi acesta va genera un cod greşit pentru apelul func iei. sum(first. 8. } float sum(float a. for (count = atoi(argv[1]). count. 168 . Chiar dacă func ia are argumente. Declararea tipului este similară celei de la declararea tipului variabilei: specificatorul de tip ce precede func ia indică tipul datei întoarse de func ie. compilatorul C o va declara implicit de tip int. care nu sunt adaptate la cerin ele ANSI-C). Func ii care returnează valori neîntregi Dacă nu se declară explicit tipul func iei. programul va afişa un mesaj de eroare. înainte de utilizarea unei func ii ce întoarce tipuri neîntregi. /* Se emite un beep */ } Observa ie: Dacă în linia de comandă nu se specifică nici un argument.8. la începutul programului se plasează o formă specială de declara ie care să precizeze compilatorului ce tip de valoare va returna acea func ie.return.23. } Instructiunea de declarare a tipului func iei are forma generală: specificator_de_tip nume_func ie(). Pentru ca func ia să întoarcă un tip diferit de int trebuie. printf("%f\n".count).//Prototipul functiei (fara parametri) void main(void) { float first = 123. Exemplu: # include <stdio. Pentru a nu se genera incertitudini datorate dimensiunii de reprezentare.7). second)). în declara ia de tip acestea nu se precizează (cu excep ia compilatoarelor mai vechi de 1989. pe de o parte. O func ie C poate returna orice tip de dată din C.09. float b) // Definitie sum() //Se returnează o valoare de tip float { return a+b. să se precizeze un specificator de tip al func iei şi apoi să se identifice tipul func iei înaintea apelului acesteia. tipul acestei func ii trebuie făcut cunoscut programului. } if (argc==3 && !strcmp(argv[2]. --count) if (disp) printf("%d ".h> float sum(). Pentru a preveni această greşeală. second = 99. Această declara ie se numeşte prototipul func iei. "display")) disp = 1. printf("%c". else disp = 0.

când un pointer este incrementat sau decrementat. char *s) {int count. din tabloul pe care îl indirectează. Un scurt program ce ar utiliza func ia match() este următorul : # include <stdio. } count ++. float. Exemplu: Programul următor con ine o func ie care întoarce un pointer într-un şir în locul în care calculatorul găseşte o coinciden ă de caractere. De exemplu. char *match (char c. această prelucrare este dependentă de tipul datei indirectate: de exemplu. o func ie ce întoarce un caracter poate fi definită ca o func ie care întoarce un întreg. trebuie discutate câteva concepte importante. dacă este increment un pointer la int.9. acesta va indica către elementul următor. double. În general. returnează un pointer la o dată de tip int. noua valoare (a adresei) va fi cu 4 mai mare fa ă de valoarea anterioară. nici întregi fără semn. while (c!=s[count] && s[count] != '\0') return (&s[count]).Dacă o func ie ce a fost declarată int întoarce un caracter. compilatorul trebuie "să ştie" ce tip de dată este indirectată de pointer. Deoarece fiecare tip de date poate avea lungimi diferite.h> 169 . char. atunci func ia int *f(). 8. Deoarece conversiile caracter --> întreg-caracter sunt fără probleme. struct etc. count = 0. Returnarea pointerilor Deşi func iile care întorc pointeri se manipulează în acelaşi mod ca şi celelalte tipuri de func ii. dacă func ia int f() returnează un întreg. func ia va întoarce un pointer la terminatorul de şir (NULL). Motivul acestei distinc ii este legat de faptul că atunci când se prelucrează un pointer aritmetic. Pointerii la variabile nu sunt nici întregi. Dacă nu se găseste nici o coinciden ă. calculatorul converteşte valoarea caracter într-un întreg. respectiv anterior. Func ia match() va încerca să întoarcă un pointer la locul (elementul) din şir unde calculatorul găseşte prima coinciden ă cu caracterul c. pentru a-l face să indice corect spre următorul element. Pointerii sunt adrese de memorie a anumitor tipuri de date: int.

# include <conio. NumeLuna(i)). } Acest program citeşte mai întâi un şir şi apoi un caracter. gets (s). "Noiembrie". Rezultatul se găseşte în s1. altfel se tipăreşte "Nu există nici o coinciden ă". # include <stdio. scanf("%d". "Mai". // Prototipul functiei void main (void) { char s[80]. se declară şi se apelează o astfel de func ie. &i). return ((nr>=1) && (nr <= 12)?luna[nr]:luna[0]).h .h> char *match()."Iulie". În cazul în care caracterul este în şir. "Septembrie". deci o func ie care copiază caracterele din şirul s2 în şirul s1. "Aprilie". ch. /* Vom incepe cu definirea functiei strcpy2() si apoi vom declara programul principal main(). printf("%s \n ". "Iunie". "Februarie". "August". /* Se introduce un caracter */ p = match (ch.h> void main(void) { int i. "Martie". /* Apelul functiei */ /* p preia valoarea functiei match() */ if (p) { printf("\n Adresa caracterului ce coincide cu cel dat este: %p". atunci se tipăreste şirul din punctul unde se află caracterul. char *NumeLuna(). "Decembrie"}. p). printf("\n Subsirul de la adresa caracterului ce coincide cu cel dat este:\n %s\n". } char *NumeLuna(nr) int nr. "Octombrie". *p. /* Se introduce un sir */ ch = getche().} else printf("Nu exista nici o coincidenta"). O astfel de func ie se declară sub forma: char *f() Exemplu: Programul următor arată modul în care se defineşte. "Ianuarie". In acest fel nu mai este necesara declararea prototipului functiei strcpy2() */ 170 . } Un alt exemplu va fi reprezentat de o variantă a func iei strcpy() din string. s). { char *luna[]= {"Eroare".p). Un caz interesant este oferit de func iile care returnează pointeri către şiruri de caractere.

j). j<col. // Echivalent: char *s0.h> # define DIM_MAX 10 void cit_mat(). transpunerea sa şi respectiv afişarea rezultatului apelând la func iile cit_mat(). puts(strcpy2(sir1. gets(sir2).*sir2. i. i++) { for (j=0. void main() { int a[DIM_MAX][DIM_MAX]. int col) { int i. for (i=0. j. return s0. dim_lin. tip_mat(a. int lin. i<lin. int lin. care se copiază şi el. } void cit_mat(int p[][DIM_MAX]. dim_lin. j<col. *p. } void main() { char *sir1. dim_col. j.register char s2[]) { char *s0 = s1. for (i=0. # include <stdio.sir2)). incrementând ambii pointeri simultan. Valoarea returnată. j++) printf("%d ". s0. tip_mat(a. void tip_mat(). p = trans_mat(a. &p[i][j]). printf("Introduceti dimensiunea matricei dim_col]: ").} Se observă cum se ini ializează s0 cu s1. dim_col). &dim_lin. Bucla while atribuie valorile (caracterele) (*s2) în loca iile indicate de pointerul s1. puts(“Introduceti un sir de la tatstatura \n”).h> char *strcpy2(register char s1[]. dim_lin. cit_mat(a. dim_lin. i<lin. dim_lin). Un ultim exemplu îl constituie un program de manipulare a unor matrici. int col) { int i. trans_mat() şi tip_mat(). } } void tip_mat(int p[][DIM_MAX]. i++) for (j=0. scanf("%d".s0 = s1. dim_col. int *trans_mat(). dim_col).p[i][j]). Bucla se termină la întâlnirea caracterului null. j++) { printf("x[%d][%d] = ". dim_col). scanf("%d %d".# include <stdio. [dim_lin 171 . Acest program realizează citirea unei matrici. while ((*s1++ = *s2++) != '\0'). re ine adresa de început a şirului s1. &dim_col).

tipul void se comportă ca un tip fundamental (de bază). int lin. for (i=0.} return p. { while (*sir) printf ("%c \n". j. Exemplu: void f(void) void *pv /* functia f nu intoarce o valoare */ /* pointer la un obiect necunoscut */ Utilizând void se impiedică folosirea func iilor ce nu întorc o valoare în orice expresie. } int *trans_mat(int p[][DIM_MAX]. // Se declara prototipul void main (void) { afis_vertical ("Hello "). De exemplu. i. int col) { int t. p[i][j] = p[j][i]. Astfel. este declarată de tip void. p[j][i] = t. void se utilizează şi ca tip de bază pentru pointeri la un obiect de tip necunoscut. Func ii de tip void Din punct de vedere sintactic. compilatorul C consideră că aceasta întoarce o valoare întreagă. } void afis_vertical (sir) char *sir. j<col. func ia afis_vertical() afişează pe ecran argumentul său şir. vertical. } Înaintea utilizării acestei func ii sau oricărei alte func ii de tip void. Tipul void este utilizat pentru declararea implicită a acelor func ii care nu întorc o valoare. } 8. void afis_vertical (sir) char *sir. şi întrucât nu întoarce nici o valoare. } 172 . Nu există obiecte de tip void. *sir ++). modul de utilizare al func iei afis_vertical() este următorul: # include <stdio.10. i<lin. aceasta trebuie declarată. } printf("\n"). prevenind astfel o întrebuin are greşită a acestora. i++) for (j=i. { while (*sir) printf ("%c \n". *sir ++). Dacă nu se declară.h> void afis_vertical(). j++) {t = p[i][j].printf("\n").

8. y.. Spre exemplu.. deoarece compilatorul extrage informa ia despre func ii în momentul în care parcurge corpul defini iei lor. Func iile prototip au fost adăugate de comitetul ANSI-C standard. înaintea folosirii unei func ii care întoarce o altă valoare decât int. programul de mai sus se poate scrie şi sub forma următoare. func (x.. y) /* Parametrii functiei sunt: */ int x. aceasta trebuie definită. } void main (void) { int x.real */ { printf ("%f". y = 10.. } /* Se afiseaza o nepotrivire */ void func (x.h> void func (x.h> void func(int. func (x. y).intreg */ float y. func iile prototip nu mai sunt necesare. = tipurile argumentelor func iei. tip_arg2. y = 10. y) /* Parametrii functiei sunt: */ int x.real */ { printf ("%f".. Exemplu: Programul următor va determina compilatorul să emită un mesaj de eroare sau de avertisment deoarece acesta încearcă să apeleze func ia func() având al doilea argument de tip int. tip_arg2. în care nu vom mai avea o declara ie de func ie prototip: #include <stdio. y). /* x . } /* Nu se afiseaza nepotrivire */ 173 . Dacă func iile sunt declarate înaintea liniei de program main(). tip_arg1. /* y . cum a fost declarat în func ia func(): #include <stdio. } Func iile prototip se folosesc pentru a ajuta compilatorul în prima fază în care func iile utilizate sunt definite după programul principal.intreg */ float y. y/(float) x). Declararea unei func ii prototip se face conform următorului format: tip nume_func ie (tip_arg1. Func ii prototip După cum se ştie.11. y. x = 10.. float). Acesta trebuie înştiin at asupra tipul datei returnat de o func ie pentru a aloca corect memoria. y/(float) x). x = 10. /* x .) unde: tip = tipul valorii întoarse de func ie. în loc de float.//Prototipul functiei func() void main (void) { int x. /* y .

Func ii recursive Func iile C pot fi recursive.h> void func(). float y) /* Parametrii formali includ tipul */ { printf ("%f". func (x. O func ie este recursivă dacă o instruc iune din corpul func iei este o instruc iune de apel al aceleiaşi func ii. x = 10. Din cele de mai sus se observă ca folosirea func iilor prototip ne ajută la verificarea corectitudinii programelor. 174 . y = 10. y/(float) x). Această func ie se poate organiza recursiv. Având în vedere 0!=1. float y) /* Parametrii formali includ tipul */ { printf ("%f". }//afisare avertisment de conversie sau.12.Utilizând recomandările ANSI-C din 1989. folosind func ia prototip: #include <stdio. } void func (int x. func (x. această func ie se poate organiza astfel: long factorial (int n) { if (n == 0) return (1). ştiind că: n! = n(n-1)!. adică se pot autoapela direct sau indirect. deoarece nu este permisă apelarea unei func ii cu alte tipuri de argumente. y. } Î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. x = 10. Uneori o func ie recursivă se numeşte şi func ie circulară. y). programul de mai sus se poate scrie mai compact: #include <stdio. } void main (void) { int x.h> void func (int x. decât tipul celor declarate. Un exemplu de o astfel de func ie este func ia factorial() care determină factorialul unui număr. /* Declarare prototip fara parametri formali ! */ void main (void) { int x. Compilatorul se informează despre tipul parametrilor formali la parcurgerea corpului defini iei func iei. y = 10. y). 8. y. y/(float) x).

} Programul de apel al acestei func ii se scrie sub forma: # include <stdio. cum sunt listele. else return (n * factorial(n-1)). } Un alt exemplu interesant este dat de şirul lui Fibonacci. Din aceasta cauză stiva trebuie dimensionată corespunzător. printf("Introduceti un numar intreg : \n"). } long factorial (int n) { if (n == 0) return (1). } Observa ie: Atunci când o func ie se autoapelează recursiv. în care termenul general an este dat de rela ia de recuren ă: an = an-1+ an-2 .else return (n * factorial(n-1)). atât timp cât stiva este folosită intens pentru fiecare apel recursiv. la fiecare apel al func iei se memorează pe stivă atât valorile parametrilor actuali. else return (n * factorial (n-1)).factorial(n)). Codul func iei poate fi scris sub forma: long fib(int n) { if (n == 0) return (0). unde a0 = 0 şi a1=1. Recursivitatea este convenabilă în mod deosebit pentru opera ii pe structuri de date definite recursiv. De asemenea şi execu ia programului poate să nu fie mai rapidă. scanf ("%d. else return (fib(n-1)+fib(n-2)). &n). 175 . else if (n == 1) return (1). printf ("(%d) ! = %ld".h> void main (void) { int n. } Utilizarea recursivită ii poate să nu conducă la o reducere a memoriei necesare. cât şi întregul set de variabile dinamice definite în cadrul func iei.n. arborii etc. O variantă echivalentă a func iei factorial() definită mai sus ar fi următoarea: long factorial(int n) { if (!n) return (1). Dar codul recursiv este mai compact şi de multe ori mai uşor de scris şi în eles decât echivalentul său recursiv.

Domeniul de vizibilitate este în general determinant şi în stabilirea duratei de via ă a variabilei. Segment de memorie text Con ine instruc iunile programului. de către toate Permanentă. aceasta poate fi temporară (există numai pe perioada în care func ia care o 176 . Din punctul de vedere al duratei de via ă a variabilei. segment de memorie statică (sau de date) şi segment de memorie dinamică (sau stivă).8. Clase de memorare (specificatori sau atribute) Din punct de vedere al execu iei programelor C. deci (memorie program) programul executabil Segment de memorie Con ine variabilele a caror loca ie rămâne fixă statică Segment de memorie Con ine variabilele de tip automatic. cunoscute în mod tradi ional ca segment de memorie text. pe func iile dintr-un fişier parcursul rulării sursă sau din mai multe programului fişiere sursă executabil Static Local sau global Permanentă. parametrii func iilor şi apelurile şi retururile dinamică (de tip stivă) de/din func ii În tabelul următor se prezintă caracteristicile claselor de memorie.13. Specificator de memorie Domeniul de vizibilitate al variabilei Durata de via ă a variabilei Plasament În memoria dinamică (de tip stivă) În regiştrii microproceso rului În memoria statică In memoria statică Auto Local fiecărei func ii Temporară. cât timp este in memorie programul executabil Vizibilitatea precizează domeniul sau locul în care o variabilă este vizibilă. numai (registru) când se execută func ia în care este declarată Extern Global. numai (automatic) sau fiecărui bloc în care când se execută a fost declarată func ia în care este declarată Register Local fiecărei func ii Temporară. memoria computerului este organizată în trei zone.

Variabilele auto sunt plasate în memoria stivă. iar domeniul de vizibilitate este local. O variabilă declarată extern într-un modul program semnalează compilatorului faptul că această variabilă a fost declarată într-un alt modul. Variabilele register sunt identice cu cele auto cu excep ia faptului că stocarea nu are loc în memoria stivă ci în regiştrii interni ai microprocesorului în scopul sporirii vitezei de execu ie a programelor. . Din punct de vedere al modulării unor programe. 177 . Dacă tipul se declară explicit în declaratorul variabilei. . iar din punctul de vedere al duratei de via ă sunt volatile. Stocarea are loc în memoria statică iar durata de via ă este permanentă. 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 oricărei func ii).auto. aşa cum sunt marea majoritate a variabilelor declarate numai prin tip. la încărcarea programului în memorie şi lansarea sa în execu ie. adică dispar din memoria stivă după reîntoarcerea din func ie.extern. Aceste variabile sunt globale.register. După prima ini ializare. Acesta este un specificator implicit. Aceste variabile sunt de două feluri: . pe toată perioada execu iei programului. o variabilă static nu mai poate fi reini ializată (de exemplu. Variabilele static sunt plasate în memoria statică. deci nu este nevoie să îl invocăm la declararea variabilelor. Variabilele cele mai folosite sunt cele care sunt declarate în blocurile apar inând unei func ii. adică nu dispar din memoria statică după reîntoarcerea din func ie.declară este activată) sau permanentă (există pe toată durata de execu ie a programului). iar din punctul de vedere al duratei de via ă sunt permanente. declarate explicit. la un nou apel al func iei în care este ini ializată). numai pentru func ia în care variabila a fost declarată. iar domeniul de vizibilitate este local. . declarate explicit. declarate explicit.static. numai pentru func ia în care variabila a fost declarată. este preferabil să divizăm un program complex în mai multe module program care se leagă în faza de link-editare. Ini ializarea unei variabile static diferă de cea a unei variabile auto prin aceea că ini ializarea este făcută o singură dată. adică sunt văzute de orice modul de program şi de orice func ie componentă a unui modul program.

h> short receip(). Exemplu: 178 .} şi ob inem rezultatul First = 1 Second = 2 Limbajul C suportă patru specificatori ai claselor de memorare: auto. Specificatorul extern Se utilizează pentru a face cunoscute anumite variabile globale declarare într-un modul de program (fişier) altor module de programe (fişiere) cu care se va lega primul pentru a alcătui programul complet. implicit. Specificatorii de memorare preced restul declara iei unei variabile care capătă forma generală: specificator_de_memorare specificator_de_tip lista_de_variabile. variabilele locale au clasa de memorare automată (auto). cu ajutorul func iei receip(). Se tipăreşte.receip()).} short receip() { short number = 1. Specificatorul auto Se foloseşte pentru a declară varibilele locale (obiectele dintr-un bloc). un număr care este mai întâi ini ializat cu valoarea 1 şi returnat incrementat cu o unitate.h> short receip(). return number++. void main(){ printf("First = %d\n". register. printf("Second = %d\n". return number++.receip()).Iată ilustrat acest lucru prin două exemple simple. printf("Second = %d\n".receip()). În cazul folosirii variabilelor implicite locale auto se rulează programul: # include <stdio. extern. Aceştia precizează modul de memorare al variabilelor care îi urmează. vom avea # include <stdio.} short receip() { static short number = 1. deoarece. utilizarea acestuia este foarte rară. static.receip()).} şi se ob ine rezultatul: First = 1 Second = 1 Dacă se modifică în func ia receip() variabila number din auto în static. void main(){ printf("First = %d\n". Totuşi.

/* variabile globale */ main( ) { extern int first. . } func1() { x = 123. numar_serie = numar_serie + 23. Se observă de asemenea că func ia nu atribuie nici o valoare ini ială variabilei numar_serie. Un exemplu de func ie care necesită o astfel de variabilă este un generator de numere care produce un nou număr pe baza celui anterior. y. 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ă. func22() { x = y / 10. Dacă compilatorul găseşte o variabilă ce n-a fost declarată. . Variabile locale statice Când cuvântul cheie static se aplică unei variabile locale.}//folosire optionala declaratie extern Variabile statice Obiectele statice pot fi locale unui bloc sau externe tuturor blocurilor. . return (numar_serie). din sau în func ii. . dar în ambele situa ii ele îşi păstrează valoarea la ieşirea şi intrarea. } Modulul 2 extern int x. serie() {static int numar_serie. char ch. main() { . extern char ch. 179 . . } func23() { y = 10. . Exemplu: int first. atunci acesta o va căuta automat printre variabilele globale. . . ceea ce înseamnă că valoarea in ială a acesteia este 0. last.Modulul 1 int x. . } 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. y. compilatorul C crează pentru aceasta o memorie permanentă în acelaşi mod ca şi pentru o variabilă globală. } Se observă că variabila numar_serie continuă să existe între două apeluri ale func iei serie() fără ca aceasta să fi fost declarată ca variabilă globală. . .

Exemplu: static int numar_serie. delay++) for (i = 0. Acest specificator precizează faptul ca variabilele declarate cu acest modificator sunt des utilizate şi se pastrează de obicei în registrele CPU. delay < 10. unsigned int delay. main() { register unsigned int j. după care apelul func iei serie() va genera următorul număr din serie. Exemplu: Aceasta func ie calculeaza me pentru întregi : int_putere (m. return (numar_serie). i < 64000.Variabile globale statice O variabilă globală cu atributul static este o variabilă globală cunoscută numai în modulul în care a fost declarată. temp = 1. for (.{ numar_serie = val_init. Deci o variabilă globală statică nu poate fi cunoscută şi nici modificată din alte module de program (alte fişiere). register int e. long t. for (delay = 0. 180 . } Apelul func iei serie_start() cu o valoare intreagă ini ializează seria generatoare de numere. //var. e--) temp * = m. return temp. Specificatorul register nu se aplica variabilelor globale. e) int m. } În acest exemplu au fost declarate ca variabile registru atât e cât şi temp. Specificatorul register Acest modificator se aplică numai variabilei de tip int şi char. } /* initializarea variabilei numar_serie */ serie_start(val_init) int val_init. i++). Exemplu : unsigned int i. De obicei utilizarea unei variabile registru conduce la micşorarea timpului de execu ie al unui program. t = time ('\0'). { register int temp. globala este cunoscuta numai in acest fisier serie() { numar_serie = numar_serie + 23. e.

/* prototip functie */ void main() { char s1[80]. Pointerul permite de asemenea func iilor să fie pasate (trecute) ca argumente în alte func ii. delay++) for (j = 0. else printf ("Neegal\n"). check(s1. /* p preia adresa de intrare a functiei */ p = strcmp. void *p. t = time ('\0'). int (*cmp) ()) /* cu int (*cmp) () se declara un pointer functie */ { printf (" Test de egalitate \n "). 2) numele trebuie cunoscut de compilator ca şi func ie.h> # include <ctype.} Dacă se execută acest program se va găsi că timpul de execu ie al buclei registru este aproximativ jumătate din timpul de execu ie al variabilei non-registru.14.b)) printf ("Egal\n"). Deoarece în C nu există o modalitate de a declara direct un pointer func ie. if (!(*cmp) (a.p).time ('\0')-t).s2.printf("Timpul pentru bucla non-registru: %ld\n" . j < 64000. Acest pointer poate fi utilizat în locul numelui func iei. s2[80]. Chiar dacă o func ie nu este o variabilă. Adresa unei func ii se ob ine utilizând numele func iei fără nici o paranteză sau argumente (ca în cazul tablourilor). Adresa atribuită pointerului este punctul de intrare al func iei.time('\0')-t). Exemplu: # include <stdio. delay < 10. gets(s2). char *b. int strcmp(). for (delay = 0. printf ("Timpul bucla registru: %ld". aceasta are o loca ie fizică în memorie care poate fi atribuită unui pointer. j++). } void check (char *a. 8. } Declararea lui strcmp() în main() s-a facut din două motive: 1) programul trebuie să ştie ce tip de valoare returnează strcmp(). Pointeri la func ii Într-un fel. gets(s1).h> void check(). un pointer func ie este un nou tip de dată. 181 . acesta se declară indirect folosind un pointer void care poate primi orice fel de pointer.

strcmp). b) realizează apelul func iei. if (!(*cmp) (a. s2. char *b.h> int strcmp().b)) printf ("Egal\n"). Exemplu: # include <stdio. gets (s1). gets (s2). s2[80].h> # include <ctype. if (!(*p) (s1. else printf ("Neegal\n").s2)) printf ("Egal\n"). } void check (char *a. strcmp).Apelul func iei check() se face având ca parametri doi pointeri la caracter şi un pointer func ie. s2[80]. else printf("Neegal\n"). /* p este pointer la functie */ p = strcmp. int (*p)(). check (s1. /* prototip functie */ void main() { char s1[80]. } 182 . printf (" Test de egalitate \n "). int (*cmp) ()) // se defineste functia check() /* cu int (*cmp) () se declara un pointer functie */ { printf (" Test de egalitate \n "). în acest caz strcmp() iar a şi b sunt argumentele acestuia. Exemplu: # include <stdio. gets (s2). /* prototip functie */ void main() { char s1[80].h> void check ().h> # include <ctype. gets (s1). int strcmp(). s2. } Observa ie: Func ia check() poate utiliza direct func ia strcmp() sub forma: check (s1. Instruc iunea : (*cmp)(a.

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) până la sfârşitul unitatii de translatare.Capitolul IX PREPROCESAREA Un preprocesor C realizează substituirea macrodefini iilor. FALSE. Forma generală a directivei #define este : #define identificator şir Se observă că directiva #define nu con ine ". TRUE. Preprocesorul C con ine următoarele directive: #if #include #ifdef #define #ifndef #undef #else #line #elif #error #pragma 9. 183 . 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ă când îl întâlneşte în textul sursă. Linia se termina cu CR. Instruc iunea: printf ("%d %d %d". Exemplu: # define TRUE 1 # define FALSE 0 Când în program se întâlnesc numele TRUE şi FALSE. respectiv 0. va afişa pe ecran 0 1 6. acestea se vor înlocui cu 1. ". o serie de calcule adi ionale şi incluziunea fişierelor.1. Liniile programului sursă care încep cu "#". În secven a de atomi lexicali "şir" nu trebuie să apară spa iu. TRUE + 5). precedat eventual de spa iu comunică cu preprocesorul.

y. astfel: # define MAX_SIZE 100 float balance [ MAX_SIZE ]. .(x<y)?x:y). Dacă şirul este prea lung şi nu încape pe o linie. . . . b) în care a = x şi b = y. 184 . . Directiva #define poate fi folosită şi pentru precizarea dimensiunii unui tablou. . Exemplu: # define ONE 1 /* Se defineşte 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. . . .b) a < b ? a : b void main() { int x. x = 10.După definirea unui macro_name. . acesta poate fi folosit pentru definirea altui macro_name. . MIN (x. printf("Numarul mai mic este: %d ". . .y)). . Ultima linie este echivalentă cu : printf ("standard error on input\n"). # define XYZ this is a test . . instruc iunea printf() va arata astfel : printf("Numarul mai mic este: %d". 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. se poate scrie: # define E_MS "standard error on input \n" . y = 20. . printf ("XYZ"). se doreşte definirea unui mesaj standard de eroare. atunci când în program se întâlneşte identificatorul E_MS. . . de exemplu. . Exemplu: Programul următor nu va afişa "this is a test". . printf (E_MS). . . . . Exemplu : directiva #define poate avea şi # define MIN (a . deoarece argumentul lui printf() nu este închis între ghilimele. Se va afişa XYZ şi nu "this is a test". Macro_nameul dintr-o argumente. } După substituirea lui MIN(a. . Dacă.

aceasta trebuie să con ină numai variabile constante definite anterior utilizării lor. De aceea.Directiva #error Directiva #error for ează compilatorul să stopeze opera ia de compilare când această este intilnita în program. #endif } Observa ie: Expresie_constanta se evaluează în timpul compilării. iar cea de-a doua la fişiere header create în directorul de lucru al utilizatorului (directorul curent). Directivele # include pot fi folosite şi una în interiorul celeilalte. Forma generală a directivei este: #error mesaj_de_eroare Aceasta linie determină procesorul să scrie mesajul de eroare şi să termine compilarea. Exemplu: #define MAX 100 void main() { #if MAX > 99 printf("Se compileaza pentru tablouri > 99\n"). Este utilizata în primul rind pentru depanarea programelor. Directiva # include Directiva # include comandă compilatorului să includă în fişierul ce con ine directiva #include un alt fişier sursă al cărui nume este specificat în directivă.2. compilatorul va sări peste acest bloc. Formele directivei sunt : # include <nume_fisier> # include "nume_fisier" Prima formă se referă la fişiere header (cu extensia . Expresie_constanta nu trebuie să con ină operatorul sizeof. #else. Directive pentru compilare condi ionată Limbajul C con ine câteva directive care ne permit să compilăm selectiv anumite por iuni de program. compilatorul va compila fragmentul de cod cuprins între #if şi #endif.h) care se găsesc în subdirectorul include din fiecare mediu de programare C. Directivele #if. 9. #elif şi #endif Forma generală a lui #if este: #if expresie_constanta secventa de instructiuni #endif Dacă expresie_constanta este adevărată. iar dacă expresie_constanta este falsă. 185 .

. . 2. . #elif expresie_N Secventa_de_instructiuni_N #endif Dacă "expresie" este adevărată se compilează "Secventa_de_instructiuni" şi nu se mai tastează nici o altă expresie #elif. care înseamnă "if defined" şi "if not defined". . . . Exemplu: MAX > 100 #if VERSIUNE_SERIALA int port = 198. #elif. . #endif #else char out_buffer[100]. . #endif este: #if expresie Secventa_de_instructiuni #elif expresie_1 Secventa_de_instructiuni_1 #elif expresie_2 Secventa_de_instructiuni_2 . Dacă "expresie" este falsă.Directiva #else lucrează similar cu instruc iunea determinând o alternativă de compilare. #else printf("Se compileaza pentru tablouri < 99\n"). . #endif } else Deoarece MAX = 10. . N. #endif #if Directivele #ifdef şi #ifndef O altă metodă de compilare condi ionată utilizează directivele #ifdef şi #ifndef. Exemplu : # define MAX 10 void main() { #if MAX > 99 printf("Se compileaza pentru tablouri > 99\n"). compilatorul va compila numai codul cuprins între #else şi #endif. . . #elif int port = 200. . . i = 1. deci va tipări mesajul : Se compilează pentru tablouri < 99 Directiva #elif inlocuieşte "else if" şi este utilizată pentru realizarea op iunilor multiple de tip if / else / if utilizate la compilare. . Forma generală a directivelor #if . corespunzatoare primei "expresie_i" adevărată. Directivele #if şi #elif se pot include unele pe altele. . compilatorul verifică următoarele expresii în serie. compilându-se "Secventa_de_instructiuni_i". . 186 .

cât şi #ifndef pot utiliza un #else. #endif #ifndef JERY printf ("Jery not defined \n"). Directiva #line O linie cu una din formele: 187 . Exemplu: #define LENGTH 100 #define WIDTH 100 char array[LENGTH][WIDTH]. atunci programul va afişa : Hello anyone !. #else printf("Hello anyone !\n"). Principala utilizare a lui #undef este de a permite localizarea unui macro_name numai în anumite sec iuni ale programului. atunci se va compila blocul dintre #ifndef şi #endif. cât şi WIDTH până se întâlneşte directiva #undef. compilatorul va compila "Secventa_de_instructiuni" dintre #ifdef şi #endif. Dacă nu s-a definit TOM. #undef LENGTH #undef WIDTH Acest program defineşte atât LENGTH. Exemplu: # define TOM 10 void main() { #ifdef TOM printf("Hello TOM !\n"). dar nu #elif. Atât #ifdef. Directiva #undef Se utilizează pentru a anula defini ia unui macro_name definit printr-o directivă #define. Forma generală a lui #ifndef este: #ifndef macro_name Secventa_de_instructiuni #endif Dacă macro_name nu este definit prîntr-o directivă #define.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. #endif } Programul va afişa: Hello TOM ! şi JERY not defined.

__LINE__). după instruc iunea #line 100. /* linia 102 */ } Instructiunea printf() va afişa valoarea 102 deoarece această reprezintă a treia linie în program. Directiva vidă O linie de forma: # nu are nici un efect. Aceştia sunt: 188 . care la compilare se expandează pentru a produce informa ii speciale. Directiva warn determină compilatorul să emită un mesaj de avertisment. Forma generală a directivei inline este : #pragma inline şi avertizează compilatorul că programul sursă con ine şi cod în limbajul de asamblare. din motive de diagnosticare a erorilor. 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. Limbajul C defineşte două instruc iuni #pragma: warn şi inline. programul sursă se află în fişierul curent. Exemplu: Următoarea secven ă face ca numărul de linie să înceapă cu 100. Dacă lipseste "nume_fişier". că numărul de linie al urmatoarei linii din programul sursă este dat de "număr". iar numele fişierului în care se află programul sursă este dat de "nume_fişier". Forma generală a lui warn este : #pragma warn mesaj unde "mesaj" este unul din mesajele de avertisment definite în C.#line numar "nume_fiaier" #line numar determină compilatorul să considere. # line 100 void main() /* linia 100 */ { /* linia 101 */ printf ("%d\n" . Macro_names (macrosimboluri) predefinite Limbajul C con ine câ iva identificatori predefini i. "nume" este numele ac iunii #pragma dorite.

de către mai mul i utilizatori. Aceste macrosimboluri. atunci implementarea este diferită de cea standard. O func ie de uz general este o func ie care poate fi folosită întro varietate de situa ii şi. 189 .3. 2001] programele C constau din fişiere sursă unice. caz în care nu mai sunt necesare prototipurile. împreună cu simbolurile definite cu #define nu pot fi redefinite. Este de preferat ca aceste func ii de uz general să nu primească informa ii prin intermediul unor variabile globale ci prin intermediul parametrilor. __DATA__ un şir care con ine data compilării sub forma luna/zi/an. modularizarea internă constă în descompunerea sarcinii globale a unui program în func ii de prelucrare distincte. Pe scurt. Acestea pot fi comparate cu nişte mici piese de lego cu ajutorul cărora se pot construi ulterior structuri (programe) foarte complexe. Scrierea programului principal (main) se concentrează mai ales pe apelul acestor func ii. 9. În cazul în care corpul de defini ie al func iilor utilizator se află după corpul de defini ie main. __TIME__ un şir care con ine ora compilării sub form: hh:mm:ss __STDC__ constanta 1. Un singur fişier sursă este în general suficient în cazul programelor mici. Sporeşte astfel foarte mult flexibilitatea în folosirea acestor func ii. Modularizarea internă este un principiu de bază al programării în C şi constă în utilizarea pe scară largă a func iilor definite de utilizator. Acest identificator este 1 numai în implementarile standard. probabil. Modularizarea programelor De obicei (vezi [Mocanu. Programul este modularizat cu ajutorul func iilor prin divizarea sa în nuclee func ionale. este necesar ca să declarăm prototipul func iilor utilizate de main() pentru a informa corect compilatorul despre tipul variabilelor returnate de func ii. dacă constanta este orice alt număr. __FILE__ un şir care con ine numele fişierului care se compilează. cu excep ia fişierelor header.__LINE__ o constanta zecimală care con ine numele liniei sursă curente. O altă modalitate este aceea de a defini func iile utilizator înaintea func iei principale main().

Aceste fişiere se numesc module. un fişier sursă mai mare se poate diviza în două sau mai multe fişiere sursă mai mici. Modulul principal este fişierul care con ine func ia principală main(). Celelalte fişiere sursă. Un workspace cuprinde unul sau mai multe Projects (proiecte) dintre care numai unul este principal şi restul sunt subordonate (subprojects). Ceea ce merită să subliniem este faptul că. Prezentarea exhaustivă a organizării acestui mediu de dezvoltare a aplica iilor C/C++ este un demers în afara prezentei lucrări. anume un workspace care include un singur project. se numesc module 190 . Fiecare proiect este compus la rândul său din mai multe fişiere. aceste fişiere sunt strâns legate între ele pentru a forma în final un tot unitar echivalent cu programul complex ini ial (dinainte de divizare).Modularizarea externă constă în divizarea unui program foarte complex în mai multe subprograme. de acelaşi tip sau de tipuri diferite.0 No iunea cea mai cuprinzătoare este aceea de Workspace (spa iu de lucru) care cuprinde în esen ă o colec ie de proiecte corelate şi prelucrabile împreună. în cadrul cel mai întâlnit. acest proiect con ine mai ales fişiere sursă şi fişiere de tip header. În figura de mai sus se prezintă un ecran al Microsoft Visual C++ din MSDN 6. dacă există. Evident. Astfel.

h" void main() { char choice. break. switch (choice) { case 'e' : enter().h con ine: # include <stdio. char city[15].h bd_bib. extern void init_list().c (bib . vom modulariza un exemplu anterior.h este fişierul header asociat cu modulul secundar bd_bib. local1.) { choice = menu().c este: # include "local. init_list(). Acest fişier header trebuie să con ină toate directivele şi declara iile de variabile necesare pentru o corectă compilare separată a modulului cu care se asociază.bază de date) este modulul principal. Pentru a exemplifica cele de mai sus. FILE *fp.save(). cel care con ine func ia main(). anume al unei baze de date simple. char state[10].h> # define SIZE 100 struct addr { char name[20]. 191 .h> # include <ctype.load().bibliotecă) care con ine toate defini iile func iilor utilizator. În mod asemănător. break.c local. extern void display().secundare. unsigned int zip.h> # include <string. care va con ine următoarele 4 fişiere: bd_main. Modulul bd_main.h bd_main. extern char menu().c (bd . El are asociat fişierul header local. De obicei. Workspace-ul va con ine un singur project. char street[30]. case 'l' : load(). extern void enter(). break. case 's' : save(). .c local1. case 'd' : display(). } addr_info[SIZE]. }}} Fişierul local. case 'q' : exit(). cu fiecare modul secundar se asociază un fişier header propriu separat. break.h. for (.exit(). Con inutul lor este prezentat în continuare.

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

i++) if(fread(&addr_info[i].t<SIZE.fp) !=1) printf (" File write error \n ").name).addr_info[t]. printf("%30s". printf("%5d". unsigned int zip.1.h> # define SIZE 100 extern struct addr { char name[20].t++) { if (*addr_info[t].} else printf ("File read error\n").name) if(fwrite(&addr_info[i]."Street").} for (i = 0. if ((fp = fopen("maillist". char city[15]. extern FILE *fp.name!='\0') { printf("%20s". char state[10]. i++) if(*addr_info[i].1."State"). else if (feof(fp)) { fclose (fp). 193 . printf("\n%20s"."City").sizeof(struct addr).city). de asemenea.}}} Fişierul local1. printf("%5s\n".addr_info[t].street). Se poate verifica cum fiecare modul în parte este compilabil fără erori. for (t=0.for (i = 0. printf("%30s".fp)==1).addr_info[t].sizeof(struct addr). fclose (fp).zip)."Name"). erori. return. return."Zip").addr_info[t].state). printf("%15s".h> # include <string."rb")) == NULL) { printf("Cannot open file\n ").} /* Functia load() */ void load() { register int i.h> # include <ctype. i < SIZE. iar la link-editare nu se semnalează. i <= SIZE. getchar(). printf("%10s". printf("%15s". char street[30].h con ine: # include <stdio. } addr_info[SIZE]. printf("%10s". } /* Functia display() */ void display() { register int t.addr_info[t].

Func iile de intrare/ieşire. tipurile şi macrodefini iile din "stdio. În limbajul C toate fişierele sunt tratate ca o înşiruire de octe i. În C un fişier reprezintă o sursă sau o destina ie de date. aşa cum în alte limbaje există fişiere cu organizare relativă sau indexată. Aceste opera ii se realizează prin intermediul unor func ii din biblioteca standard a limbajului C. Într-o altă 194 . deşi în anumite sisteme. Ele se mai numesc suporturi reutilizabile deoarece zona utilizată pentru păstrarea înregistrărilor unui fişier poate fi ulterior reutilizată ulterior pentru păstrarea înregistrărilor unui alt fişier. Biblioteca acceptă fişiere de tip text şi binar. Programatorul poate să interpreteze datele după cum doreşte Prin urmare. Aceste elemente se numesc înregistrări. O altă caracteristică a limbajului C constă în faptul că nu există un sistem de gestionare a fişierelor care să permită organizări de date. ele asigură o bună portabilitate a programelor. intrarea standard respectiv ieşirea standard sunt în mod implicit reprezentate de terminalul de la care s-a lansat programul. Aceste func ii pot fi aplicate în mod eficient la o gamă largă de aplica ii datorită multiplelor facilită i pe care le oferă.h Limbajul C nu dispune de instruc iuni de intrare/ieşire.Capitolul X INTRĂRI/IEŞIRI 10. acestea sunt identice. care poate fi asociată cu un disc sau cu alte periferice. Func ii de intrare şi ieşire . neexistând structuri de date specifice care să se aplice acestor fişiere. De asemenea.h" reprezintă aproape o treime din bibliotecă. prin scrierea/citirea datelor se scriu/citesc un număr de octe i fără o interpretare specifică. Prin fişier în elegem o mul ime ordonată de elemente păstrate pe diferite suporturi. Suporturile cele mai des utilizate sunt cele magnetice (floppy sau harddiscuri).stdio. În C. fiecare linie având zero sau mai multe caractere terminate cu ' \n '. fiind implementate într-o formă compatibilă pe toate sistemele de operare. de exemplu UNIX.1. Un fişier de tip text este o succesiune de linii.

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

eventual chiar mai mult. sfârşitul de fişier se generează în func ie de sistemul de operare considerat. Invers. Când se prelucrează un astfel de fişier se crează o imagine a acestuia în memoria internă (RAM) a calculatorului.să se reducă numărul de opera ii de acces la disc. Dimensiunea trebuie aleasă în func ie de aplica ie inând cont de faptul că prin mărirea bufferului creşte viteza de execu ie dar scade dimensiunea memoriei disponibile codului programului şi invers. Bufferele au o mărime implicită. dimensiunea de 256 octe i. Un fişier stocat pe suport magnetic se mai numeşte şi fişier extern. Bufferul de tastatură are. aceştia se vor înscrie de fapt secven ial în buffer şi nu direct pe disc. dar ea poate fi modificată prin program. Opera iile de citire continuă în acest mod până la citirea tuturor octe ilor din buffer. Dacă în continuare se vor solicita încâ 2 octe i. din care 254 sunt puşi la dispozi ie. atunci sistemul citeşte într-un buffer întreg sectorul de pe disc (512 octe i) în care se găsesc şi cei 2 octe i solicita i. În acest fel. Dacă un program efectuează o opera ie de citire a 2 octe i dintr-un fişier. Această imagine se mai numeşte şi fişier intern. Orice fişier are o înregistrare care marchează sfârşitul de fişier. Scrierea va continua astfel până la umplerea bufferului. dacă un program efectuează o opera ie de scriere a unui număr de octe i pe disc. spre exemplu. în func ie de dimensiunea bufferului (zonei tampon). aceştia vor fi prelua i din bufferul din memorie. Pentru sistemele de operare MS-DOS sau MIX şi RSX11 se tastează CTRL/Z iar pentru UNIX se tastează CTRL/U. În cazul fişierelor de intrare ale căror date se introduc de la terminal. În acest scop se folosesc bufferele. fără a mai fi nevoie să mai accesăm discul pe care se află fişierul din care se face citirea. prin micşorarea sa creşte memoria cod disponibilă dar scade viteza de lucru. 196 . în general mai mare decât cantitatea solicitată de o opera ie de I/O. Un buffer este o zonă de memorie în care sistemul memorează o cantitate de informa ie (număr de octe i). moment în care se va face o nouă umplere a bufferului cu noi date prin citirea următorului sector de pe disc. reducând numărul de opera ii de acces la disc (pentru citire sau scriere) creşte viteza de execu ie a programelor şi fiabilitatea dispozitivelor de I/O. 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 goleşte bufferul prin scriere).

10. sistemul de operare păstrează un indicator de fişier care indică pozi ia curentă în fişier. la deschiderea unui fişier pentru citire indicatorul de fişier va indica la începutul fişierului. dar multe dintre ele pot depinde de implementare. Dacă se va face o opera ie de citire a 2 octe i se vor citi octe ii cu numărul de ordine 0 şi 1 iar indicatorul va indica spre următorul octet. conexiunea este întreruptă prin închidere. Alte opera ii care sunt executate frecvent în prelucrarea fişierelor sunt: • Crearea unui fişier (acest fişier nu există în format extern) • Actualizarea unui fişier (deja existent) • Adăugarea de înregistrări unui fişier deja existent • Consultarea unui fişier • Pozi ionarea într-un fişier • Redenumirea unui fişier • Ştergerea unui fişier Ca şi opera iile de deschidere şi închidere de fişiere. În timpul lucrului cu fişierele. pozi ie la care se va face următoarea opera ie de scriere sau citire. tastatura şi porturile seriale şi paralele) sunt deschise în mod text.2. Opera iile de deschidere şi închidere a fişierelor se poate realiza în C prin func ii specializate din biblioteca standard I/O a limbajului. interfe ele standard (cu ecranul. Principalele func ii sunt grupate în tabelul de mai jos: 197 . Deschiderea unui fişier întoarce un pointer la un obiect de tip FILE. adică cel cu numărul de ordine 3. care con ine toate datele necesare pentru controlul fişierului. Opera ii cu fişiere În acest subcapitol vom detalia principalele opera ii efectuate asupra unor fişiere. În momentul începerii execu iei unui program. De exemplu. opera iile indicate mai sus pot fi realizate printr-un set de func ii aflate în biblioteca standard I/O a limbajului. Pentru o mai corectă în elegere a acestor func ii le vom structura după nivelul la care se utilizează: inferior sau superior. Aceste func ii realizează ac iuni similare sub diferite sisteme de operare. În cele ce urmează se prezintă func iile care au o utilizare comună pe diferite medii de programare şi sunt cele mai frecvent utilizate.Un fişier intern este conectat la un fişier extern sau dispozitiv prin deschidere.

Tipul FILE este un tip structurat care depinde de sistemul de operare.Func ii pentru prelucrarea pe caractere a unui fişier: putc (scriere caracter) şi getc (citire caracter). . Pentru ca sistemul de operare să poată opera asupra fişierelor ca fluxuri (stream) de intrare/ieşire trebuie să cunoască anumite informa ii despre ele.Func ii pentru Intrări/Ieşiri de şiruri de caractere: fgets şi fputs. pozi ie curentă. În această variabilă. cum ar fi: .h". Un pointer-fişier este o variabilă pointer de tip FILE. precum: • Nume • Dimensiune • Atribute fişier • Descriptorul fişierului Un pointer-fişier este un pointer la informa iile care definesc diferitele aspecte ale unui fişier: nume.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. . care se numeşte bloc de control al fişierului. Dacă facem abstrac ie de cazurile speciale de calculatoare tip 198 . definită în "stdio. FCB (File Control Block) sistemul păstrează informa ii despre fişierul deschis.Func ii pentru Intrări/Ieşiri cu format: fscanf şi fprintf. stare. Acest lucru se realizează prin opera ia de deschidere a fluxurilor (stream-urilor). Pointerul fişier În urma opera iei de deschidere se crează în memorie o variabilă de tip structură FILE care este o structură predefinită.

programul primeşte un pointer la variabila creată.3. care se referă la primul port paralel PRN la care se conectează de obicei imprimanta (LPT). Nivelul inferior de prelucrare a fişierelor La acest nivel opera iile de prelucrare a fişierelor se execută fără o gestiune automată a zonelor tampon. care se referă la dispozitivul standard pentru afişarea mesajelor de eroare (ecranul). Orice opera ie de scriere la stream-ul stdout înseamnă scriere pe ecran. FILE *stderr. care se referă la dispozitivul standard de intrare (tastatura). Orice opera ie de citire de la stream-ul stdin înseamnă citire de la tastatură. Programatorul are în gestiune o zonă declarată 199 . Spre deosebire de stdin. care se referă la dispozitivul standard de ieşire (ecranul). În limbajul C există 5 stream-uri standard. } FILE. Se spune că s-a deschis un stream (flux de date). stdout este ne-bufferizat deoarece orice scriere pe ecran se face direct la scrierea unui caracter în fişierul stdout. Se mai spune că stdin este cu buffer la nivel de linie. FILE *stdout. care se referă la primul port serial COM1. char _flag. definite în <stdio. int _cnt. unsigned char *_base. pe majoritatea implementărilor tipul FILE se defineşte prin următoarea structură: typedef struct { unsigned char *_ptr. făcându-se apel direct la sistemul de operare. char _file. Este bufferizat la nivel linie. Este ne-bufferizat. 10. deci un pointer la o structură de tip FILE. FILE *stdaux. Este ne-bufferizat.VAX sau U3B.h>: FILE *stdin. Toate opera iile care se fac pe acest stream se referă la fişierul asociat stream-ului. În urma deschiderii unui fişier. Bufferul folosit are o dimensiune de 254 de caractere şi bufferul se goleşte la tastarea NL (‘\n’). FILE *stdprn. Variabila de tip FILE este creată şi gestionată de către suportul pentru exploatarea fişierelor în limbajul C.

Fişierele deschise la acest nivel pot fi prelucrate în citire (consultare). Numele func iilor de nivel inferior. atunci numele este precedat de o construc ie de forma: litera:\nume_1\nume_2\…\nume_k unde: 200 . în cazul citirii dintr-un fişier. Dacă fişierul nu se află în fişierul curent. secven a de octe i CR-LF (0DH. Dacă un fişier se deschide în modul text. Calea spre fişier trebuie să respecte conven iile sistemului de operare MS-DOS în general. urmat de extensia fişierului. fie un pointer spre un astfel de şir de caractere.mod) unde: df – este un număr întreg care reprezintă descriptorul de fişier spf – este specificatorul fişierului care se deschide mod – defineşte modul de prelucrare a fişierului Specificatorul de fişier este fie un şir de caractere.drept buffer şi trebuie să ină cont de faptul că această bufferizare este la nivel linie. El identifică în continuare fişierul respectiv în toate opera iile realizate asupra lui. În cea mai simplă formă ea este un şir de caractere care defineşte numele fişierului. Aceasta presupune că fişierul se găseşte în directorul curent. 10. În forma cea mai simplă func ia _open se apelează printr-o expresie de atribuire de forma: df = _open(spf. în cazul MS-DOS sau Windows CTRL/Z este interpretat în cazul citirii drept caracter de sfârşit de fişier (EOF). De asemenea.1. La revenirea din ea se returnează un aşa numit descriptor de fişier. scriere (adăugare de înregistrări) sau citire/scriere (actualizare sau punere la zi). Deschiderea unui fişier Orice fişier înainte de a fi prelucrat trebuie deschis. Deschiderea unui fişier existent se realizează prin intermediul func iei _open. În cea mai simplă formă el este un nume sau mai general o cale care indică plasamentul pe disc al fişierului care se operează. orientate pe text (transfer de octe i) încep de obicei cu _ (underline). Acesta este un număr întreg. iar în cazul scrierii în fişier caracterul LF este expandat la secven a CR-LF. motiv pentru care opera ia de deschidere a unui fişier este de mare importan ă. atunci.3. Con inutul şirului de caractere depinde de sistemul de operare folosit. 0AH) este translatată (înlocuită) cu un singur caracter LF.

Fişierul se prelucrează în mod binar Fişierul este de tip text. Crează şi deschide un nou fişier pentru scriere.C“.. D.O_RDWR). adică se prelucrează pe caractere sau octe i (implicit) _O_WRONLY _O_RDWR _O_APPEND _O_CREAT _O_BINARY _O_TEXT 201 .pentru citire 1 . Spre exemplu. caracterul ‘\’ se dublează. În func ie de opera ia dorită. În acest caz func ia _open returnează valoarea (-1). mod poate avea valorile: 0 .. Nu are nici un efect dacă fişierul este deja existent.pentru scriere 2 .C din directorul JOC de pe dscheta A se deschide în citire/scriere. putem folosi o comandă de deschidere de forma: int d.pentru citire/scriere Deschiderea unui fişier nu reuşeşte dacă unul dintre parametri este eronat. pentru hard-disk) nume_i – este un nume de subdirector. int pmode] ). caz în care fişierul BIO. este defini ia generală a func iei _open. int _open( const char *filename. int oflag [. Deoarece calea se include între ghilimele. 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 fişierului Fişierul se deschide numai în citire (read-only) Nu se poate specifica împreună cu _O_RDWR sau _O_WRONLY Fişierul se deschide numai în scriere (write-only) Nu se poate specifica împreună cu _O_RDWR sau _O_RDONLY Fişierul se deschide în citire/scriere (read/write) Fişierul se deschide pentru adăugarea de înregistrări la sfârşitul său.litera – defineşte discul (în general A. B pentru floppy-disk şi C. d=_open(“A:\\JOC\\BIO.

Men ionăm că în MSDN aceste variabile se mai numesc şi oflag (open-flag) şi sunt definite în fişierul header FCNTL.H. În cazul în care oflag este _O_CREAT, atunci este necesară specificarea constantelor op ionale pmode, care se găsesc definite în SYS\STAT.H. Acestea sunt: _S_IREAD - este permisă numai citirea fişierului _S_IWRITE - este permisă şi citirea (permite efectiv citirea/scrierea fişierului) _S_IREAD | _S_IWRITE - este permisă şi scrierea şi citirea fişierului. Argumentul pmode este cerut numai când se specifică _O_CREAT. Dacă fişierul există deja, pmode este ignorat. Altcumva, pmode specifică setările de permisiune asupra fişerului care sunt activate când fişierul este închis pentru prima oară. _open aplică masca curentă de permisiune la fişier înainte de setarea accesului la fişier. Pentru a crea un fişier nou se va utiliza func ia _creat pentru a-l deschide. De fapt se deschide prin creare un fişier inexistent. Func ia este definită astfel: int _creat( const char *filename, int pmode ); în care parametrii au fost descrişi mai sus. Protec ia unui fişier este dependentă de sistemul de operare. Spre exemplu, în UNIX protec ia se defineşte prin 9 bi i ataşa i oricărui fişier, grupa i în 3 grupe de câte 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. Numărul octal 0751 permite proprietarului toate cele 3 opera ii indicate mai sus (7 = 1112), grupul la care apar ine proprietarul poate citi şi executa fişierul (5 = 1012) iar al i utilizatori pot numai executa fişierul (1 = 0012). Func ia _creat poate fi apelată şi în cazul în care se deschide un fişier deja existent, caz în care se pierde con inutul vechi al fişierului respectiv şi se crează în locul lui unul nou cu acelaşi nume. Fiecare din func iile _open sau _creat returnează un specificator de fişier (handle) pentru fişierul deschis. Acest specificator este o valoare întreagă pozitivă. Implicit, stdin are specificatorul 0, stdout şi stderr au specificatorii 1 respectiv 2 iar fişierele disc care sunt deschise primesc pe rând valorile 3, 4,..etc. până la numărul maxim admis de fişiere 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 fişier read-only sau modul de partajare a fişierului nu permite opera ia specificată sau calea nu specifică un nume de fişier ci de director. EEXIST – (valoare 17) flagurile _O_CREAT şi _O_EXCL sunt specificate, dar numele de fişier este al unui fişier deja existent. EINVAL – (valoare 22) unul dintre argumentele oflag sau pmode sunt invalide. EMFILE – (valoare 24) nu mai sunt disponibile specificatoare de fişier (prea multe fişiere deschise). ENOENT – (valoare 2) fişierul sau calea nu au fost găsite. Variabila globală errno păstrează 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 păstrează 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 aceeaşi valoare cu codul erorii de sistem. Numai pentru opera ii de I/O se foloseşte _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 următoarele 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 fişier
Scrierea într-un fişier se realizează folosind func ia _write. Se presupune că fişierul respectiv a fost în prealabil deschis prin func iile _open sau _creat. Ea este asemănătoare 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 căreia i se atribuie numărul de octe i scrişi în fişier. df – este descriptorul de fişier returnat de func ia _open la deschiderea sau _creat la crearea fişierului. zt - este un pointer spre zona tampon definită de utilizator, zonă din care se face scrierea. n – este dimensiunea zonei tampon sau numărul de octe i care se doreşte 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 fişierul asociat cu descriptorul handle. Opera ia de scriere începe la pozi ia curentă a pointerului de fişier asociat cu fişierul dat. Dacă fişierul este deschis cu indicatorul _O_APPEND, opera ia de scriere începe la sfârşitul fişierului. După o opera ie de scriere pointerul de fişier este incrementat cu numărul de bi i scrişi efectiv. Dacă fişierul a fost deschis în mod text (implicit), atunci _write tratează CTRL/Z drept un caracter ce indică sfârşitul logic al fişierului. Când se scrie într-un dispozitiv, _write tratează CTRL/Z din buffer drept terminator al opera iei de ieşire. Î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 numărul de bi i din buffer. În caz contrar scrierea este eronată: aceasta semnifică faptul că pe disc a rămas mai pu in spa iu (în octe i) decât numărul de octe i ai bufferului. Dacă valoarea returnată este -1, se semnalizează eşecul opera iei de scriere. În acest caz variabila globală errno poate avea una din valorile EBADF (care semnifică un descriptor de fişier invalid sau că fişierul 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 ieşirile standard (display). Astfel, pentru a scrie pe ieşirea standard identificată prin stdout se foloseşte descriptorul 1, iar pentru a scrie pe ieşirea standard pentru erori, stderr, se foloseşte descriptorul de fişier 2. De asemenea, în acest caz nu este nevoie să apelăm func ia _open sau _creat deoarece fişierele 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 afişa mesajul:
Wrote 36 bytes to file Press any key to continue

10.3.3. Citirea dintr-un fişier
Citirea dintr-un fişier deschis în prealabil cu func ia _open se realizează cu ajutorul func iei _read. Ea returnează numărul efectiv al octe ilor citi i din fişier. 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 căreia i se atribuie numărul de octe i citi i din fişier. df – este descriptorul de fişier returnat de func ia open la deschiderea sau creat la crearea fişierului. zt - este un pointer spre zona tampon definită de utilizator, zonă în care se face citirea. n – reprezintă numărul de bi i care se citesc Func ia _read citeşte maximum count octe i în buffer din fişierul asociat cu descriptorul handle. Opera ia de citire începe de pe pozi ia curentă îndicată de pointerul de fişier asociat cu fişierul dat. După opera ia de citire, pointerul de fişier indică spre următorul octet necitit din fişier. Dacă fişierul a fost deschis în mod text, citirea se termină la întâlnirea caracterului CTRL/Z, care este interpretat drept indicator de sfârşit de fişier. _read returnează numărul de bi i citi i din fişier, care poate fi mai mic decât count dacă sunt mai pu ini decât count octe i rămaşi în fişier sau dacă fişierul este deschis în mod text. În acest caz fiecare pereche CR-LF (carriage return–linefeed) (CR-LF) este înlocuită cu un singur caracter LF. Numai acest caracter LF se consideră în valoarea returnată. Înlocuirea nu afectează pointerul de fişier. Dacă func ia încearcă să citească după sfârşitul fişierului, se returnează valoarea 0. Dacă descriptorul de fişier (handle) este invalid sau dacă

206

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

4. 10.3. În practică apar însă şi situa ii în care noi dorim să scriem şi să citim înregistrări într-o ordine oarecare.} La execu ia programului se va afişa următorul mesaj: Read 36 bytes from file Press any key to continue 10. 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 fişier (handle) al fişierului pe care dorim să-l închidem. Programatorul poate închide un fişier folosind func ia _close. În acest caz se spune că accesul la fişier este aleator. deoarece numărul fişierelor ce pot fi deschise simultan este limitat între 15 şi 25. Defini ia func iei este: int _close( int handle ). _close( fh ). Închiderea unui fişier După terminarea prelucrării unui fişier el trebuie închis. Pentru a realiza un acces aleator este nevoie să ne putem 208 . Acest mod de acces la fişier se numeşte secven ial şi el este util când dorim să prelucrăm fiecare înregistrare a fişierului.5.fiecare apel al func iei _write scrie înregistrarea în pozi ia următoare din fişier. Acest lucru se realizează automat dacă programul se termină prin apelul func iei exit. Se recomandă ca programatorul să închidă orice fişier de îndată ce s-a terminat prelucrarea lui.3. în func ie de sistemul de operare. Func ia _close închide fişierul asociat cu descriptorul handle.printf( "Read %u bytes from file\n". astfel încât: . bytesread ).fiecare apel al func iei _read citeşte înregistrarea din pozi ia următoare din fişier . Func ia _close returnează valoarea 0 la o închidere reuşită şi -1 în caz de incident. Pozi ionarea într-un fişier Opera iile de citire/scriere într-un fişier se execută secven ial. Men ionăm că fişierele corespunzătoare intrărilor şi ieşirilor standard nu trebuie închise de programator.

2) permite să se facă pozi ionarea la sfârşitul fişierului. origine) unde: v – este o variabilă de tip întreg căreia i se atribuie valoarea returnată de către func ie (0 sau -1) df – este descriptorul de fişier (handle) a cărui valoare a fost definită la deschiderea sau crearea fişierului.deplasamentul se socoteşte de la începutul fişierului. Următoarea opera ie de citire/scriere se va efectua de la noua loca ie.h> 209 . Opera ia următoare realizată prin apelul func iei _read sau _write se va realiza din această pozi ie. 0) Exemplu: #include <io.h> #include <fcntl. apelul: v = _lseek(df. 1 . Func ia _lseek mută pointerul de fişier asociat cu descriptorul handle (df) pe o nouă loca ie care este situată la offset octe i de origin. Defini ia func iei este: long _lseek( int handle. Pozi ionarea la începutul fişierului se face prin apelul: v = _lseek(df. 0l.h> #include <stdlib. long offset.deplasamentul se socoteşte din pozi ia curentă a capului de citire/scriere. deplasament – este o variabilă de tip long şi con ine numărul de octe i peste care se va deplasa capul de citire/scriere al discului. 2 . În continuare se pot adăuga articole folosind func ia _write. Argumentul origin trebuie să fie una dintre următoarele constante. deplasament. Ea poate fi apelată prin: v = _lseek(df.deplasamentul se socoteşte de la sfârşitul fişierului.H: SEEK_SET – începutul fişierului (valoare 0) SEEK_CUR – pozi ia curentă a pointerului de fişier (valoare 1) SEEK_END – sfârşitul fişierului (valoare implicită 2) Func ia _lseek returnează valoarea 0 la pozi ionare corectă şi -1 la incident. definite în STDIO. int origin ). origine – are una din valorile 0 . 0l.pozi iona oriunde în fişierul respectiv O astfel de pozi ionare este posibilă pe hard-uri şi floppy-uri prin func ia _lseek. Spre exemplu. Men ionăm că prin apelul lui _lseek nu se realizează nici un fel de transfer de informa ie ci numai pozi ionarea în fişier.

else printf( "Pozitia ultima este = %ld\n". else printf("Pozitia pentru inceputul fisierului = %ld\n". fh = _open( "write. SEEK_END ). SEEK_SET ). Defini ia func iei este: int _unlink( const char *filename ).#include <stdio. 0L.3. pos ). _close( fh ). if( pos == -1L ) perror( "_lseek inceput nu a reusit!" ). else printf( "Pozitia curenta = %ld\n". if( pos == -1L ) perror( "_lseek sfarsit nu a reusit!" ).o". 0L. _O_RDONLY ).} În urma execu iei programului se va afişa: Pozitia pentru inceputul fisierului = 0 Pozitia curenta = 10 Pozitia ultima este = 36 Press any key to continue 10. buffer. Func ia _unlink şterge de pe disc fişierul specificat prin filename. /* Pozitia pointerului fisier */ char buffer[10].6 Ştergerea unui fişier Un fişier poate fi şters apelând func ia _unlink astfel: v = _unlink(spf) unde v este o variabilă de tip întreg căreia i se atribuie valoarea 0 pentru ştergere reuşită şi (-1) pentru ştergere nereuşită. /* Gaseste pozitia curenta: */ pos = _lseek( fh. /* Pozitionare pe ultima pozitie: */ pos = _lseek( fh. SEEK_CUR ). pos ). /* Muta pointerul fisier cu 10 octeti */ _read( fh. long pos. if( pos == -1L ) perror( "_lseek pozitia curenta nu a reusit!" ).h> void main( void ) { int fh. /* Pozitionare la inceputul fisierului: */ pos = _lseek( fh. pos ). spf este specificatorul de fişier folosit la deschidere a fişierului. Exemplu: 210 . 10 ). 0L.

c.3). while (_read(0.} Citirea nu se va opri după 3 caractere.o" ) == -1 ) perror( "Nu se poate sterge 'WRITE. inclusiv grupul final CR+LF.O'" ).1)>0) _write(1.7.3)>0) _write(1.O'\n" ). else printf( "S-a sters 'WRITE.} În urma execu iei programului se afişează: S-a sters 'WRITE.1).c. Vom ilustra în continuare responsabilitatea pe care o are programatorul în gestionarea zonelor tampon. ci func ia _read va continua să func ioneze până la tastarea ENTER (CR+LF). Lucrul la nivelul inferior nu este chiar atât de simplu pe cât pare.h> void main() /* copiaza standard */ { char c[1].h> # include <io. # include <stdio. deci programul arată astfel: # include <stdio.h> void main( void ) { if( _unlink( "write.O creat si prelucrat anterior.h> void main() { char c[3]. Să se scrie un program care copiază intrarea standard la ieşirea standard. */ #include <stdio./* Acest program sterge fisierul WRITE. Exemple de utilizare a func iilor de intrare/ieşire de nivel inferior 1.} intrarea standard la iesirea Men ionăm că cel de-al doilea parametru al func iei _read sau _write trebuie să fie pointer spre caractere. Imediat func ia _read va tipări grupele de 3 caractere introduse. Această problemă se poate rezolva uşor prin folosirea func iilor getchar şi putchar. Acum o vom rezolva folosind func iile _read şi _write.3.O' Press any key to continue 10. Zona tampon definită este supraînscrisă de fiecare dată când se introduc noi caractere. Să considerăm exemplul anterior în care zona tampon o mărim la 3 caractere.c. while (_read(0. 211 .c.h> # include <io.

h> # define LZT 10 // lungime zona tampon void main() /* copiaza intrarea standard standard */ { char zt[LZT]. 2. având o valoare nedefinită.Dacă de la tastatură vom introduce 123456<CR><LF> atunci se va tipări primul grup (prima înscriere a zonei tampon) 123. Dacă în continuare vom introduce 1<ENTER> atunci se va tipări 1 urmat de două rânduri noi deoarece fiecare CR sau LF sunt expandate de stdout în perechi <CR><LF>. iar următoru se înscrie de către program. anume codurile ASCII ale lui 4 şi 5 şi se va tipări <CR><LF>6. Prin scriere nu se vor trimite spre stdout decât numărul de caractere citit de la stdin. Să se scrie un program care citeşte un şir de numere flotante de la intrarea standard şi crează 2 fişiere fis1. 123456 123456 6 Primul grup 123456 este scris prin ecou de la tastatură.zt. while ((n=_read(0. care este de 254 de caractere.} la iesirea Programatorul trebuie să ină cont însă şi de alte amănunte cum ar fi dimensiunea implicită a bufferului stdin.zt.dat şi fis2. apoi a doua grupă 456 şi grupul <CR> şi <LF> va supraînscrie primele două caractere ale bufferului. prezentată mai jos. Dacă mărim la 5 dimensiunea bufferului şi de la tastatură introducem 12<ENTER>. primul con inând numerele de ordin impar citite de la intrarea standard 212 . Problemele de mai sus legate de gestiunea bufferului în/din care se face citirea/scrierea pot fi depăşite cu o modificare simplă. atunci se va tipări 12 12 ¦ deoarece cel de-al 5-lea octet al bufferului nu a fost alocat prin citire.LZT))>0) _write(1.h> # include <io. # include <stdio.dat.n). int n.

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

nume).nume). union unr nrcit.list_fis(df2.nume2).nume1). inchid_fis(df1. df2=deschid_fis_cit(nume2). df1=creare_fis(nume1).h> # include <fcntl.j+=2. df2=creare_fis(nume2).char *nume. char tnr[sizeof(float)].df2.} void list_fis(int df.sizeof(float)))>0) { printf("%6d: %g\n".h> # include <io. while ((i=_read(df.dat.nume1. date_fis(df1. deci func ia de listare se va modifica pentru citirea din 2 în 2 a înregistrărilor.1). union unr { float nr. Să se realizeze programul de mai sus folosind un singur fişier fis.}. se va face un salt cu o înregistrare pentru a pozi iona capul de citire/scriere peste înregistrarea următoare. printf("Nu se poate deschide fisierul in creare\n")._S_IWRITE|_S_IREAD))==-1) { printf("%s: ". 214 .} if (i<0) { printf("%s: ".nume).} _close(df). Programul va diferi fa ă de cel anterior prin faptul că înregistrările se stochează într-un singur fişier.h> # include <sys/types.} void main() { int df1. df1=deschid_fis_cit(nume1).} return df.2).nrcit.h> #include <stdlib.df2.nrcit. printf("Nu se poate deschide fisierul in citire\n").nume2.nr). exit(1).h> char nume[]="fis. list_fis(df1.nume2). int creare_fis(const char *nume) { int df. if ((df=_creat(nume. # include <stdio.inchid_fis(df2.j.h> # include <sys/stat. printf("Eroare la citire din fisierul\n"). După fiecare citire din fişier. else j=2. if (ord%2) j=1.dat".int ord) { int j. exit(1).i.nume1.tnr.} 3.printf("%s: ".

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

df=creare_fis(nume). Forma generală de declarare a func iei fopen() este: FILE *fopen(char *filename.} Atragem aten ia asupra modului în care lucrează func iile de intrare/ieşire pentru stdin şi stdout fa ă de cele pentru disc. Func iile printf şi scanf sunt proiectate pentru a lucra implicit cu fişierele stdout respectiv stdin.void main() { int df.mod) unde: pf . inchid_fis(df. Nivelul superior de prelucrare a fişierelor Nivelul superior de prelucrare a fişierelor se referă la aşa numitul format binar de reprezentare a informa iei în fişiere care la rândul său face apel la informa ia structurată. deci cu monitorul şi tastatura.este un pointer spre tipul FILE spf – este specificatorul fişierului care se deschide mod – este un şir de caractere care defineşte modul în care se deschide fişierul. Func ia fopen() Func ia fopen se apelează printr-o expresie de atribuire de forma: pf = fopen(spf. 10.nume).nume). 216 . 10.nume). Dacă intrările şi ieşirile pentru perifericele standard le putem executa în formatul dorit cu ajutorul func iilor specializate scanf şi printf. dar care sunt de nivel superior. Există func ii specializate pentru scrierea/citirea pe disc cu format. Ceea ce merită să subliniem este faptul că echivalentele de nivel superior pentru fişiere ale func iilor printf() şi scanf() sunt fprintf() şi fscanf(). date_fis(df. df=deschid_fis_cit(nume). Bufferul este alocat automat şi gestionat de func ii C specializate. Echipamentele periferice pot fi considerate fişiere externe şi deci func iile specializate pentru I/O cu fişiere pot fi folosite şi pentru opera ii de I/O cu echipamentele periferice. pentru lucrul cu discul variabila float este tratată sub forma unui grup de 4 octe i care se scriu sau se citesc pe disc aşa cum este reprezentarea lor internă.1. list_fis(df.4.4. char *mode).

ieşirea standard şi ieşirea standard pentru erori. Men ionăm că stdin. stdout şi stderr sunt pointeri spre tipul FILE şi permit ca func iile de nivel superior de prelucrare a fişierelor să poată trata intrarea standard. Numele fişierului con ine cel mult FILENAME_MAX caractere. 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 sfârşit d fişier EOF înainte de a adăuga la sfârşitul fişierului. Modul "a+" şterge identificatorul de sfârşit de fişier EOF înainte de adăugarea de înregistrări la sfârşitul fişierului. Modul "a+" este cerut pentru adăugarea la sfârşitul unui fişier care are marker terminator CTRL/Z = EOF. La un moment dat pot fi deschise cel mult FOPEN_MAX fişiere. După ce s-a făcut o adăugare. ca în "rb" sau "w+b" se indică un fişier binar. Singura deosebire constă în 217 . comanda MS-DOS TYPE tipăreşte datele până la markerul original EOF şi nu până la ultima dată adăugată. la fel ca şi restul fişierelor. După adăugare comanda MS-DOS TYPE va tipări toate datele con inute în fiier. Dacă modul "mode" include "b" după litera ini ială.Func ia deschide fişierul al cărui nume este specificat prin "filename" (de obicei un fişier disc) şi întoarce un pointer la FILE pentru opera ie reuşită şi NULL pentru opera ie nereuşită.

/* Deschidere pentru scriere */ if( (stream2 = fopen( "test2. Func ia fclose() se utilizează pentru a închide un fişier deschis cu fopen(). } Dacă pentru opera ia de citire se încearcă deschiderea unui fişier inexistent.c". /* Deschidere in citire (esec daca fisierul "test1.c". "w+" )) == NULL ) printf( "Fisierul 'test2.c".4. Cu toate acestea.c" nu exista) */ if( (stream1 = fopen( "test1. *fopen(). 10. else 218 . La execu ia func iei exit se închid automat toate fişierele deschise. *stream2. se recomandă ca programatorul să închidă un fişier de îndată ce s-a terminat prelucrarea lui. Func ia fclose() scrie mai întâi în fişier datele rămase în fişierul buffer apoi închide fişierul. void main( void ){ int numclosed. exit(1). unde "fp" este pointerul la fişier returnat după apelul func iei fopen().faptul că în acest caz programatorul nu trebuie să deschidă sau să închidă fişierele respective. Exemplu: FILE *fp. fclose() întoarce zero (0) pentru închidere reuşită şi EOF (-1) dacă apare o eroare."w"). fopen() va returna o eroare.c' nu a fost deschis\n" ). "r" )) == NULL ) printf( "Fisierul 'test1. Exemplu: /* Acest program deschide fisierele numite "test1. /* se declara pointerii de tip file *fp si *fopen() */ fp = fopen("test". altfel se poate ajunge în situa ia de a se depăşi numărul limită admis pentru fişierele care pot fi simultan deschise într-un program. "w")) == NULL) { puts ("Cannot open file\n"). else printf( "Fisierul 'test1.2.c" si _fcloseall pentru a inchide restul fisierelor deschise */ #include <stdio.c" si "test2.Apoi foloseste fclose pentru a inchide "test1.h> FILE *stream1. Func ia fclose() Forma generală de declarare a func iei fclose() este: int fclose(FILE *fp). /* se deschide fisierul " test " pentru screiere */ Pentru detectarea unei erori la deschiderea unui fişier se utilizează secven a: if ((fp = fopen("test".c' a fost deschis\n" ).c' nu a fost deschis\n" ).

int rename (char *oldname.printf( "Fisierul 'test2. char *newname). b) Func ia feof() int feof(FILE *fp) Func ia feof() întoarce o valoare diferită de zero dacă indicatorul de sfârşit de fişier este valid şi o valoare zero dacă indicatorul de sfârşit de fişier nu este valid. 10. numclosed ). /* Inchide fisierul cu pointerul stream1 */ if( fclose( stream1 ) ) printf( "Fisierul 'test1.4.4. c) Func ia perror() void perror(const char *s) 219 . /* Toate celelalte fisiere se inchid: */ numclosed = _fcloseall( ). Func iile rename() şi remove() Func ia rename() schimbă numele vechi al fişierului.} În urma execu iei programului se ob ine: Fisierul 'test1. Întoarce o valoare diferită de zero dacă încercarea reuşeşte. Func ia remove() elimină fişierul cu numele specificat. Întoarce o valoare diferită de zero dacă incercarea nu reuseste.c' a fost deschis Fisierul 'test2.3. cu numele nou.c' nu a fost inchis\n" ). "oldname". 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. Func ia remove() int remove(char *filename).c' a fost deschis\n" ). Forma generală de declarare este: int ferror(FILE *fp) unde "fp" este un pointer la fişier. astfel încât o incercare ulterioară de deschidere a fişierului va eşua. Func ii de tratare a erorilor a) Func ia ferror() Aceasta func ie determină dacă în urma unei opera ii cu fişiere sa produs sau nu o eroare. "newname".4. printf( "Numarul fisierelor inchise cu _fcloseall: %u\n".c' a fost deschis Numarul fisierelor inchise cu _fcloseall: 1 Press any key to continue 10.

int num_bytes. "buffer" poate fi o simplă variabilă.FILE *fp) Func ia fread() citeşte din fişierul specificat prin "fp" cel mult "count" obiecte. acesta putând fi mai mic decât cele cerute. ferror(). "wb")) == NULL){ printf (" Cannot open file\n "). Forma generală de declarare: int fread(void *buffer. return.int num_bytes. s. 1. Forma generală de declarare: int fwrite(void *buffer. Exemplu: Programul următor scrise un număr real pe disc # include "stdio. în caz de eroare este mai mic decât "count".4. "error message") 10. care. Pentru a determina starea func iei se pot utiliza func iile feof().Func ia perror() scrie s şi un mesaj de eroare care depinde de implementare. fiecare obiect având lungimea egală cu "num_bytes" şi îi trimite în zona de memorie indirectată prin "buffer" . *fp este un pointer fişier la fişierul deschis anterior cu fopen(). Func ia întoarce numărul de obiecte citite. FILE *fp) Func ia fwrite() scrie din zona (tabloul) "buffer" în fişierul indirectat prin "fp". if ((fp = fopen ("test"."%s %s\n. fclose (fp). return.int count.} 220 . sizeof (float). Func ia întoarce numărul de obiecte scrise. } fwrite (&f. astfel: fprintf(stderr. fp).h. /* tabloul balance */ if ((fp = fopen("balance".23. Func ii cu acces direct a) Func ia fread() Permite citirea unui bloc de date. "w+")) == NULL) { printf ("Cannot open file\n").h" void main() { FILE *fp. corespunzator cu intregul din errno. } Aşa cum se vede din acest program. b) Func ia fwrite() Permite scrierea unui bloc de date. Exemplu: Programul următor copiază un tablou de numere reale "balance". în fişierul "balance": # include "stdio. float f = 12. float balance[100]. "count" obiect de lungime "nr_bytes".h" void main() { FILE *fp.int count.5.

numread. 1. Func ii pentru pozi ionare a) Func ia fseek() Determină pozi ionarea fişierului la citire sau scriere.} else printf( "Fisierul nu a putut fi deschis\n" ). . sizeof (balance).început de fişier. . . . list ). fclose( stream ). fclose( stream ). începând cu pozi ia selectată. fp). . ..h> void main( void ) { FILE *stream. caracterelor citite = 25 Continutul bufferului = zyxwvutsrqponmlkjihgfedcb Press any key to continue 10. . fclose (fp). .stream ). "offset" este deplasamentul (număr octe i) noii pozi ii fa ă de "origin". printf( "Continutul bufferului = %.out". . 25. 221 .25. . .6. i < 25.25s\n". . . .out". .sizeof(char). } Exemplu: Programul următor deschide fişierul FREAD. .} În urma execu ie programului se ob ine: S-au scris 25 caractere Nr. fwrite (balance. . sizeof( char ). Forma func iei: int fseek(FILE *fp. .4. numread). . numwritten. . . i++ ) list[i] = (char)('z' . . . . . . . . . long offset. int i. printf( "S-au scris %d caractere\n".} else printf( "Probleme cu deschiderea fisierului\n" ). stream ). "r+t" )) != NULL ) { /* Incearca sa citeasca 25 caractere */ numread = fread( list. char list[30].OUT şi scrie în el 25 de caractere şi apoi îl redeschide şi citeşte din nou caracterele din fişier după care afişează numărul caracterelor citite şi con inutul.i). /* Scrie 25 caractere in fisier */ numwritten = fwrite(list. "w+t" )) != NULL ) { for ( i = 0. caracterelor citite = %d\n". numwritten ). . . . if( (stream = fopen( "fread. . #include <stdio. printf("Nr. int origin) unde "fp" este un pointer-fişier returnat prin apelul func iei fopen(). iar "origin" este una din următoarele macrodefini ii: SEEK_SET . /* Deschide fisierul in mod text: */ if( (stream = fopen( "fread.

Singurele opera ii garantate să func ioneze corect când se utilizează fseek asupra fişierelor deschise în mod text este pozi ionarea cu offset 0 relativă la orice pozi ie din fişier şi pozi ionarea fa ă de începutul fişierului cu un offset returnat de func ia ftell(). atunci pointerul indică începutul fişierului. SEEK_END .pozi ie curentă.SEEK_CUR . Exemplu: Pentru a citi cel de-al 235 byte din fişierul numit "test" se poate folosi următorul program: func1() /* se declara func ia func1() */ { FILE *fp. Func ia returnează 0 dacă se execută cu succes şi o valoare nenulă în caz de eroare. char line[81].h> void main( void ) { FILE *stream. În cazul fişierelor deschise în mod text. Nu se recomanda utilizarea func iei fseek() pentru fişiere text. } fseek(fp. Exemplu: /* Acest program deschide fisierul FSEEK. dar se recomandă folosirea lor în special asupra fişierelor binare. "rb")) == NULL) { printf ("Cannot open file\n"). Func ia returnează valoarea curentă a pointerului fişier. Pozi ia este exprimată prin offsetul fa ă de începutul fiierului.OUT pointerul in diverse locuri din fisier */ #include <stdio. return getc(fp). acest offset nu reflectă întotdeauna exact numărul de octe i datorită transla iei CR-LF. Func ia fopen şi toate celelalte func ii vor căuta să înlăture caracterul CTRL/Z terminator de fişier (EOF).sfârşit de fişier. 0). if ((fp = fopen("test". 235L. si muta 222 . Transla iile CR-LF efectuate în mod text pot cauza func ionarea defectoasă a func iei fseek. se sugerează utilizarea acesteia numai pentru fişiere binare. Este preferată folosirea simultană a func iilor fseek şi ftell pentru a opera asupra fişierelor text. exit (1).} /* se citeste un caracter de la pozitia 235 */ Observa ie: L modifică constanta 235 la tipul long int. Func ia ftell() este definită astfel: long ftell( FILE *stream ). Dacă nu s-a efectuat nici o opera ie de I/O de la deschiderea fişierului în mod APPEND (adăugare).

lista_argumente) Func ia fprintf() realizează conversia şi scrierea la ieşire în fişierul indirectat cu "fp" sub controlul formatului.out".dacă primul caracter nu este un semn se va scrie un blanc la început. line ). SEEK_SET). fiecare determinând conversia şi tipărirea argumentelor din lista de argumente. 19L.int result. "format".\n" ).se utilizează în conversiile numerice şi indică umplerea cu zerouri la începutul câmpului. result = fseek( stream. a) Func ia fprintf() Forma acestei func ii este: int fprintf(FILE *fp. fgets( line. stream = fopen( "fseek. printf( "%s".\n" ). "0" . Acesta este fisierul 'fseek. if( stream == NULL ) printf( "”Fisierul fseek. Şirul "format" con ine două tipuri de obiecte: caractere obişnuite care sunt copiate în fişierul de ieşire şi descriptori de conversie. " " . 223 .4. 80.determină alinierea la stânga a argumentului convertit în câmpul de reprezentare. sau orice valoare negativă.precizează că numărul va fi reprezentat cu semn.}} În urma execu ie programului se ob ine: Pointerul fisier este plasat la mijlocul primei linii.out'. if( result ) perror( "Fseek esec" ). else { printf( "Pointerul fisier este plasat la mijlocul primei linii. "w+" ). "+" . Între % şi caracterul de conversie pot exista: 1) Indicatori (în orice ordine): "-" . else {fprintf( stream. Ieşiri cu format Func iile de tip printf() asigură conversiile de ieşire cu format. dacă apare o eroare. Valoarea întoarsă de func ie este numărul de caractere scrise.out'.out nu s-a deschis\n" ).} fclose( stream ). Fiecare descriptor începe cu caracterul % şi se încheie cu un caracter de conversie. "Fseek incepe aici: " "Acesta este fisierul 'fseek. Press any key to continue 10. "format".7. stream ).

dar este 0 dacă s-a ales această optiune (exemplu: %05d). sau numărul de cifre semnificative pentru conversiile "g" sau "G"."#" . pentru "x" sau "X". iar o precizie egală cu zero elimina punctul zecimal. pentru "e". sau "f". care indică numărul maxim de caracetre care se vor tipări după virgulă pentru "e". "G".2f . Dacă argumentul specificat are mai pu ine caractere.nota ie zecimala cu semn.indică o formă de ieşire alternativă : pentru "0". "E". %e. 2) Un număr care indică lungimea minimă a câmpului de reprezentare. 3) Un punct ce separă lungimea câmpului de precizie. %X . "g".nota ie zecimală de forma: [-]m.nota ie zecimală fără semn. De exemplu: %10. %u . "E". pentru "g" şi "G" nu se vor elimina zerourile de la sfârşit. %s .ddddddE+/-XX 224 .caracterele din şir sunt tipărite până se întâlneşte '\0' sau cât timp numărul de caractere tipărit precizia. "f" ieşirea va avea întotdeauna un punct zecimal.nota ie zecimală de forma [-]mmm. Argumentul convertit va fi tipărit într-un câmp cu o lungime cel pu in egală cu cea specificată. prima cifra va fi zero. sau precizia.va afişa un număr de cel pu in 10 caractere cu 4 caractere după virgulă. %d. Caracterul de umplere este de obicei spatiul.va alinia la stânga un număr real cu 2 zecimale întrun câmp de reprezentare de cel pu in 10 caractere. func ie de aliniere. Lungimea. sau amândoua se pot specifica şi prin "*".ddd. atunci câmpul va fi umplut la stânga sau la dreapta. 4) Un număr.dddddde+/-xx sau [-]m.un singur caracter. %5.4f .7s . la începutul fiecărui număr nenul se va scrie "0x" sau "0X".nota ie hexazecimală fără semn (fără 0x sau 0X). %i . %-10. %E .va afişa un şir de cel pu in 5 caractere dar nu mai lung de 7 caractere. %f . dacă va fi nevoie şi mai mare. precizia. Descriptorii de conversie utiliza i de C sunt: %c . sau numărul maxim de caractere ce se vor tipări dintr-un şir. unde numărul d-urilor este indicat de precizie. precizia implicită este 6. %x.

Aceşti modificatori se pot aplică caracterelor de conversie d. "hello") (%5. %li. Există doi modificatori de format care permit func iei fprintf() să afişeze întregii long şi short.7s". %p .nota ie octalăa fără semn (fără 0 la început). Atunci %hu va preciza că data este de tip short unsigned int.45 3.2f". se tipăreşte "%". lista_argumente) Exemplu: printf() ("%-5.nu se face conversie. iar o precizie egală cu 0 va elimina punctul zecimal). în caz contrar se utilizează %f.h> FILE *stream.2f". o. 3.afiseaza un pointer. char s[] = "this is a string". sau precizie.5. "123456789") ieşire 123. i.45 hello hello 1234567 Exemplu de utilizare a functiei fprintf. numărul de caractere scrise până în acel moment este scris în argument.h> #include <process.] b) Func ia printf() Forma func iei : int printf("format". 225 .se utilizează %e sau %E dacă exponentul este mai mic decât -4. lista-argumente) Func ia printf() este echivalentă cu : fprintf(stdout. %% .unde numărul de d-uri este indicat de precizie (precizia implicită este 6. "format". 123. f şi g şi indică faptul că numerele tiparite sunt de tip double. /* 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.4565) ("%10s". %lu). Modificatorul l poate prefixa şi caracterele de conversie e. double fp = 1. "hello") ("%-10s". void main( void ) { int i = 10. Modificatorul h comandă func iei fprintf() să afişeze short int.456) ("%5. u şi x. %n .nu se realizează conversie de argument. %g. precedându-i pe aceştia (exemplu: %ld. %G . %o .

un număr op ional care indică lungimea câmpului. fclose( stream ). LF.citeşte un număr întreg zecimal fără semn. Un câmp de intrare se defineşte ca un şir de caractere diferite de cele albe şi se întinde până la următorul caracter alb (spa iu.spa ii sau caractere HT sau VT care se ignoră. un caracter op ional h.out".4. fprintf( stream. Nu se adaugă '\0'.} 10.specificatori de conversie. Rezultatul conversiei unui câmp de intrare este plasat în variabilă indicată de argumentul corespunzător din lista de argumente.citeşte un singur caracter. fprintf( stream. care indică lungimea argumentului şi un caracter de conversie. constând dintr-un caracter %. stream = fopen( "fprintf.8. c ). În caz contrar. "w" ). tab-uri. un caracter op ional de suprimare a atribuirii.caractere obişnuite (diferite de %) care indică următorul caracter diferit de caracterele albe cu care începe fişierul de intrare. %u . implicit este 1. func ia întoarce numărul de elemente care au primit valori. VT. "%s%c". "%f\n". Şirul "format" poate con ine: . FF).out" ). i ). respectându-se numărul de caractere indicat de lungimea câmpului. Descriptorii de conversie utiliza i în C pentru citire sunt: %c . system( "type fprintf. "format". Func ia întoarce EOF dacă se detectează sfârşitul de fişier sau apare o altă eroare înainte de orice conversie. câmpul de intrare este ignorat. .citeşte un număr întreg zecimal. s. fprintf( stream. fără a se face nici o atribuire. Intrări 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.char c = '\n'. l sau L. 226 . CR. fp ). . fiecare argument trebuind să fie un pointer. %d . caracterele următoare sunt plasate în tablourile indicate. Dacă se indică suprimarea atributului prin "*" ca în %*s. lista_argumente) Func ia fscanf() citeşte din fişierul indirectat prin "fp" sub controlul formatului "format" şi atribuie valorile citite argumentelor următoare. "%d\n".

citeşte un întreg scurt. &x. "%d. &r. 227 . %n . b) Func ia scanf() Forma func iei: int scanf("format". va suspenda atribuirea datei citite. "format".se scrie în argument numerele de caractere citite până în acel moment. address). /* se citesc doua numere separate prin spa iu. %s . %p . sau hexazecimal. &y). apoi caracterul ". cu 0x sau 0X la început).%i . %g . Un caracter obişnuit în şirul "format" determină ca func ia fscanf() să citească un caracter ce coincide cu cele din "format".şir de caractere diferite de caracterele albe. %d" face că fscanf() să citească un întreg. fscanf() va fi încheiată. /* se citeşte un întreg în variabilă count */ scanf ("%s". Instruc iunea : scanf("%20s". %*c este ignorat (nu este atribuit). 10 să fie atribuit lui x. %f.întreg hexazecimal (cu sau fără 0x sau 0X la început). cu 0 la început. face ca. De exemplu.întreg octal (cu sau fără zero la început). Toate variabilele menite să primească valori prin fscanf() trebuie să fie transmise prin adresele lor. &count).numere zecimale în virgulă mobilă. Aceasta înseamnă că toate argumentele trebuie să fie pointeri la variabilele utilizate ca argumente.citeşte un număr întreg (intregul poate fi octal. %h . iar 20 se atribuie lui y. Nu se citeşte nimic din intrare. %o . tab sau linie noua */ Un * plasat între % şi caracterul de conversie. sir). &c). %e.citeşte valoarea pointerului. indicând spre un tablou de caractere destul de mare pentru a memora şirul şi caracterele terminator '\0' care se va adauga. dacă de la tastatură se introduce 10/20. Dacă calculatorul nu găseşte caracterul specificat. lista-argumente) Exemple: scanf ("%d". lista-argumente) Func ia scanf() este echivalenta cu: fscanf(stdin. Astfel. instruc iunea : scanf("%d%*c%d"." şi apoi un alt întreg. /* se citeşte un şir de caractere în vectorul address */ scanf ("%d %d". %x .

"%c". name). "a-string". &y). }} /* Acest program scrie date cu format cu printf intr-un fisier. nu se încheie decât dacă după ultimul caracter se introduce un spa iu. Dacă se introduce un şir de mai mult de 20 caractere. Dacă de la tastatura se introduce 10#20. care va plasa restul caracterelor tot în "şir". SEEK_SET ). 3. printf( "%c\n". &l ). fclose( stream ).out".h> FILE *stream. sir). &x. "%ld". /* Citeste datele inapoi din fisierul disc: */ fscanf( stream. "%f". 65000. Func ii de citire şi scriere a caracterelor a) Func ia fgetc() int fgetc(FILE *fp) 228 .4. "w+" ). printf( "%f\n". if( stream == NULL ) printf( "Fisierul fscanf. iar restul se pierd.14159. printf( "%ld\n". instruc iunea : scanf("%s#%s". "%s". &c ). /* Tipareste datele citite din fisier: */ printf( "%s\n". fp ). void main( void ) { long l. Pentru caracterele rămase se poate apela din nou func ia scanf() sub forma : scanf("%s". &fp ).citeşte nu mai mult de 20 caractere în variabilă şir. s ). "%s %ld %f%c". 'x' ). float fp. vor fi re inute numai primele 20. 0L. Instruc iunea : scanf("%s ". c ). /* Seteaza pointerul la inceputul fisierului: */ fseek( stream. #include <stdio. l ). Apoi foloseste fscanf pentru a citi datele din fisier */ 10. Exemplu de utilizare a func iilor fscanf şi fprintf. va plasa 10 în x şi 20 în y. char s[81].9. else { fprintf( stream. char c. fscanf( stream. s ).out nu a fost deschis\n" ). stream = fopen( "fscanf. fscanf( stream. fscanf( stream.

c". primul caracter fiind preluat după apasarea tastei CR. Exemplu de utilizare a func iei fgetc().h> void main( void ) { FILE *stream. /* 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. caracter de tip unsigned char (convertit la int) sau EOF dacă s-a detectat sfârşitul de fişier sau a apărut o eroare. d) Func iile getche() şi getch() int getche(void) 229 . ch. "r" )) == NULL ) exit( 0 ). } c) Func ia getchar() int getchar(void) Func ia getchar() este echivalentă cu getc (stdin) . char buffer[81].} b) Func ia getc() int getc (FILE *fp) Această func ie este identică cu fgetc() cu deosebirea că este o macrodefini ie. Exemplu: Pentru a citi un fişier text până la întâlnirea indicatorului de sfârşit de fişier se scrie: ch = getch (fp). } /* Adauga null la sfarsitul fisierului */ buffer[i] = '\0'. while (ch != EOF) { ch = getc (fp). /* Deschide fisierul pentru a citi o inregistrare */ if( (stream = fopen( "fgetc.Func ia fgetc() întoarce următorul caracter al fişierului indirectat cu "fp". /* Citeste primele 80 de caractere si le plaseaza in "buffer": */ ch = fgetc(stream). int i. for(i=0.h> #include <stdlib. Observa ie: "fp" este un pointer-fişier returnat de func ia fopen(). buffer ). Dezavantajul func iei getchar() este că această poate păstra în bufferul de intrare nu unul. fclose( stream ). ci mai multe caractere. i++ ) { buffer[i] = (char)ch. putând să evalueze fişierul mai mult decât o dată.(i<80) && (feof(stream)==0). printf( "%s\n". ch = fgetc( stream ).

Întoarce valoarea caracterului scris. oprindu-se dacă a detectat NL (New Line) care este inclus în tablou. dacă apare vreo eroare. char *p. /* Acest program utilizeaza fgets pentru afisarea unei linii dintr-un fisier la display */ #include <stdio. înlocuit prin'\0'. Exemplu de folosire a func iei fgets. e) Func ia gets() char *gets(char *s) Func ia gets() citeşte un şir de caractere introduse de la tastatură şi îl plasează în vectorul indirectat prin s. FILE *fp) Func ia fgets() citeşte în tabloul s cel mult n-1 caractere. dacă apare o eroare sau sfârşit de fişier. else printf( "%s". int n. Func ia întoarce tabloul s.h> void main( void ) { char strptr1[] = "Test pentru fputc !!\n". */ #include <stdio. line). stream ) == NULL) printf( "fgets error\n" ). Func ia întoarce vectorul s sau EOF. în caz de eroare. /* Tipareste linia folosind fputc. FILE *fp) Func ia fputc() scrie caracterul "ch" convertit la unsigned char. if( (stream = fopen( "fgets. §irul se termină cu '\n' ce va fi înlocuit cu '\0'. /* Acest program foloseste fputc si _fputchar pentru a trimite un sir de caractere la stdout. char strptr2[] = "Test pentru _fputchar!!\n". Func ia getche() preia un caracter cu "ecou" iar getch() preia caracterul fără ecou. "r" )) != NULL ) { if( fgets( line.c". char line[100]. */ 230 .int getch(void) Func iile introduc un caracter de la tastatură. sau EOF. Func iile asteaptă până se apasă o tastă şi apoi întorc valoarea acesteia. sau NULL.h> void main( void ) { FILE *stream. }} a') Func ia fputc() int fputc(int ch. în fişierul indirectat prin "fp". 100. f) Func ia fgets() char *fgets(char *s. fclose( stream ). Exemplu de utilizare a func ie fputc.

FILE *fp) Func ia putc() este echivalenta cu fputc() cu excep ia că este o macrodefinitie. în caz contrar.} Func ia ungetc() int ungetc(int ch. în caz de eroare. stdout)!=EOF). while((*p != '\0') && fputc(*(p++). Exemplu de utilizare a functiilor fprintf() şi fscanf() : 231 . Întoarce EOF. func iile care încep cu _ (subliniere) dar şi cu f (de la file) sunt destinate lucrului cu interfe ele standard stdin şi stdout. stdout ). o valoare nenegativă. în caz de eroare. putând evalua fişierul mai mult decât o dată. Func iile getw() şi putw() Aceste func ii se folosesc pentru a citi.FILE *fp) Func ia ungetc() pune argumentul "ch" inpoi în fişier. Întoarce EOF. (Aceasta functie nu compatibila ANSI */ p = strptr2.\n". Se poate pune inapoi în fişier doar un singur caracter.FILE *fp) Func ia fputs() scrie şirul "s" (care nu este neapărat necesar să con ină '\n') în fişierul "fp". Aceste func ii lucrează exact că func iile getc() şi putc(). respectiv a scrie întregi dintr-un sau într-un fişier disc. /* Identic cu _fputchar. f') Func ia fputs() int fputs(const char *s.} este În general. Func ia întoarce caracterul ce trebuie pus. sau EOF. e') Func ia puts() int puts(const char *s) Func ia puts() scrie şirul "s" şi NL în "stdout". Marcajul EOF nu poate fi pus înapoi. dacă apare o eroare. sau o valoare nenegativă în caz contrar. d') Func ia de la punctul d) nu are un corespondent pentru ieşire. b') Func ia putc() int putc(int ch. Spre exemplu. c') Func ia putchar() int putchar(int ch) Func ia putchar(ch) este echivalenta cu putc (ch. stdout).h> void main( void ) { fputs( "Hello world from fputs. /* Acest program foloseste fputs pentru a scrie o linie la terminalul standard */ #include <stdio. de unde va fi preluat la următoarea citire. while((*p != '\0') && _fputchar(*(p++))!=EOF).p = strptr1.

} } while (choice != 'q').h" void ad_num(void). do { choice = menu(). /*prototipul functiilor */ void cauta(void). nume. char menu(void)."%*c"). void main() { char choice. break. &numar). numar. "r")) == NULL) { printf("Cannot open file\n "). nume2. char nume[80]. schimb. } printf ("Nume ?"). numar))."%s%d%d%d". Q(uit) : ").} } fclose (fp). numar). schimb. numar.&schimb. fscanf(stdin. break. printf ("\n"). if ((fp = fopen ("telefon".} 232 . if (!strcmp(nume. do { printf ("A(dauga)."%s %d %d %d\n". a_code. nume.&a_code. ch = tolower (getche())."%s%d%d%d". switch (choice) { case 'a' : ad_num(). &a_cod. # include "stdio. fscanf(stdin. } void ad_num() /* Adauga un nou numar */ {FILE *fp. return ch. schimb. int a_code. &numar). int a_code. } void cauta() /* Cauta un numar dandu-se un nume */ { FILE *fp.nume. &schimb. a_cod. "a")) == NULL) { printf ("Cannot open file \n"). gets (nume).fprintf(fp. char name[80]. fclose (fp). /* Se cauta numarul */ while (!feof (fp)) { fscanf(fp. C(auta). nume2)) { printf("%s : (%d) %d %d\n".Programul permite actualizarea unei agende telefonice. case 'c' : cauta(). exit (1). /* Se deschide fisierul pentru citire */ if ((fp = fopen ("telefon". } char menu() {/* Afiseaza meniul si preia optiunea */ char ch. exit (1). } while (ch != 'q' && ch != 'a' && ch != 'c'). nume2[80].} printf("Introduceti numele si numarul: "). break. schimb. /* inlatura CR din sirul de intrare */ /* Se scrie in fisier */ printf("%d".

h>. deci ecranul are o capacitate de 25*80=2000 de caractere. func iile de gestiune a ecranului în mod text au acces la tot ecranul. Prezentăm func iile standard mai importante utilizate la gestiunea ecranului în mod text. Acesta poate fi gestionat în două moduri: în mod text sau în mod grafic. clipirea caracterului.Capitolul XI UTILIZAREA ECRANULUI ÎN MOD TEXT Biblioteca standard a limbajului C şi C++ con ine func ii pentru gestiunea ecranului. Pozi ia pe ecran a unui caracter se defineşte printr-un sistem de două coordonate întregi (x. iar col ul din dreapta jos are coordonatele (80. Setarea ecranului în mod text Se realizează cu ajutorul func iei textmode care are următorul prototip: void textmode (int modtext) 233 . 11.1. Fereastra este o parte a ecranului în formă de dreptunghi şi care poate fi gestionată separat de restul ecranului. Modul text presupune că ecranul este compus dintr-un număr de linii şi un număr de coloane. Accesul poate fi însă limitat la o parte din ecran utilizând aşa numitele ferestre. Atributele unui caracter de pe ecran sunt următoarele: coordonatele x şi y. Toate func iile standard de gestiune a ecranului în mod text au prototipurile în fişierul antet <conio.y) unde: x este numărul coloanei în care este situat caracterul y este numărul liniei în care este situat caracterul Col ul din stânga sus al ecranului are coordonatele (1.25). culoarea caracterului. În mod curent se utilizează 25 de linii a 80 de coloane fiecare.1). culoarea fondului pe care este afişat caracterul. În mod implicit.

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. (x2. Definirea unei ferestre După setarea ecranului în mod text. este activ tot ecranul şi acesta are caracteristicile indicate în paragraful precedent. 11. Ştergerea unei ferestre O fereastră activă se şterge utilizând func ia clrscr care are următorul prototip: void clrscr(void) După apelul acestei func ii. Acest lucru poate fi realizat cu ajutorul ferestrelor. aceasta nu are nici un efect. De multe ori însă se doreşte partajarea ecranului în zone care să poată fi gestionate în mod independent.y2) – reprezintă coordonatele col ului dreapta jos.2. La un moment dat este activă o singură fereastră şi anume acea definită la ultimul apel al func iei window.3. int y2). Dacă parametri de la apelul func iei window sunt erona i. Fondul ei are culoarea definită prin culoarea de fond curentă.unde modtext poate fi exprimat simbolic sau numeric în modul următor: Modul text Caractere albe pe fond negru.y1) – reprezintă coordonatele col ului stânga sus . unde: (x1. 40 de coloane Color 40 de coloane Caractere albe pe fond negru. în timp ce celelalte moduri se pot seta pe adaptoare color. 234 . 11. int y1. fereastra activă (sau tot ecranul dacă nu s-a definit în prealabil o fereastră cu func ia window) devine vidă. int x2. O fereastră se defineşte cu ajutorul func iei window care are următorul prototip: void window (int x1.

7] şi are semnifica ia din tabelul de mai sus. şi care au următoarele prototipuri: int wherex(void). _CH=6. geninterrupt(0x10).4. int y). geninterrupt(0x10). _CH=0x20. Dacă la apelul func iei coordonatele sunt definite în afara ferestrei active. respectiv liniei. Pozi ia cursorului din fereastra activă poate fi determinată cu ajutorul func iilor wherex şi wherey care returnează numărul coloanei. Setarea culorilor Culoarea fondului se setează textbackground ce are următorul prototip: cu ajutorul func iei void textbackground(int culoare). unde (x. unde culoare este un întreg în intervalul [0. Deplasarea cursorului Cursorul poate fi plasat pe un caracter al ferestrei active folosind func ia gotoxy ce are următorul prototip: void gotoxy (int x. adică în pozi ia de coordonate (1. y) reprezintă coordonatele caracterului pe care se plasează cursorul. 235 . din fereastra activă pe care se află cursorul. 11. În cazul în care se doreşte ascunderea cursorului (facerea invizibilă a cursorului) se utilizează o secven ă ce utilizează func ia geninterrupt: { _AH=1. Pot fi setate ambele culori precum şi clipirea caracterului folosind func ia textattr ce are următorul prototip: void textattr (int atribut) unde atribut se defineşte cu ajutorul rela iei: atribut=16*culoare_fond+culoare_caracter+clipire.Func ia clrscr pozi ionează cursorul pe caracterul din stânga sus al ferestrei active.1). int wherey(void).5. _CL=7. func ia este ignorată. } Cursorul poate fi rafiaşat utilizând următoarea secven ă: { _AH=1.} 11.

– afişează date sub controlul formatelor în mod similar func iei printf.int putch (int c). .Culoarea caracterelor se setează cu ajutorul func iei textcolor ce are următorul prototip: void textcolor(int culoare). . . int bottom.copiază textul cuprins în dreptunghiul definit de coordonatele (left. .6. – afişează un şir de caractere în mod similar func iei puts. . . bottom) – dreapta jos la adresa de memorie indicată de pointerul destination. . .şterge sfârşitul liniei începând cu pozi ia cursorului.void delline (void). int top.int gettext (int left. 236 .void clreol (void). Func ii pentru gestiunea textelor Pentru afişarea caracterelor se pot folosi func iile: . top) – stânga sus şi (right.int cputs (const char *str).int cprintf (const char *format).15] a cărui semnifica ie este explicată de tabelul următor: Culoare Negru Albastru Verde Turcoaz Roşu Purpuriu Maro Gri deschis Gri închis Albastru deschis Verde deschis Turcoaz deschis Roşu 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. unde culoare este un întreg în intervalul [0. – afişează un singur caracter. void *destination). int right.şterge toată linia pe care este pozi ionat cursorul.

. Exemplu: Următorul program desenează o fereastră şi scrie un număr în aceasta.y. .v. 237 . ELEM *stiva[MAX]. int chenar.int.citeşte un caracter fără ecou de la tastatură. func ia returnează codul ASCII al caracterului citit de la tastatură. func ia returnează codul ASCII al caracterului citit de la tastatură. bottom) – dreapta jos de la adresa de memorie indicată de pointerul source.h> <conio.h> <dos. adică după ce este citit caracterul este afişat automat pe ecran. .h> <alloc. }ELEM. Dacă a fost apăsată o tastă se returnează o valoare diferită de zero.inserează o linie vidă în fereastra activă.h> #define MAX 100 #define SIMPLU 1 #define DUBLU 2 typedef struct{ int x. desttop). int top. . int istiva.int).. top) – stânga sus şi (right.int n) //Afiseaza o fereastra limitata de un chenar { extern ELEM *stiva[].void insline (void). . int right. void *source ). .int getch (void). bottom) – dreapta jos în dreptunghiul cu coordonatele col ului din stânga sus (destleft.int culoare. #include #include #include #include #include <stdio.int sus.h> <stdlib. void vertical(int. int bottom. void orizontal(int.citeşte textul cuprins în dreptunghiul definit de coordonatele (left.int getche (void). int bottom. .controlează dacă s-a tastat ceva la tastatură. . adică după ce este citit caracterul nu mai este afişat pe ecran. extern int istiva. .int jos.int puttext( int left.citeşte un caracter cu ecou de la tastatură. .int. int destleft.int kbhit (void).int fond.mută textul cuprins în dreptunghiul definit de coordonatele (left.int movetext( int left. . int top. top) – stânga sus şi (right. int desttop ).u. altfel se returnează valoarea 0. void *zonfer.int).int dr. void fereastra(int st. int right.

switch(chenar){ case SIMPLU: putch(192). break.jos. switch(chenar){ case SIMPLU: putch(191). //Trasare chenar if (chenar){ textcolor(WHITE).sus.sus. break. } if ((stiva[istiva]=(ELEM *)farmalloc(sizeof(ELEM)))==0){ printf("\nMemorie insuficienta\n"). textattr(16*fond+culoare).jos). break.chenar).1. case DUBLU: putch(201).jos-sus+1).dr. if((gettext(st. } stiva[istiva]->x=st. } orizontal(dr-st-2.//memoreaza partea din ecran pe care se va afisa fereastra if(istiva==MAX){ printf("\nPrea multe ferestre!"). switch(chenar){ case SIMPLU: putch(218). } istiva++. } vertical(jos-sus. gotoxy(1.dr. stiva[istiva]->y=sus. exit(1). case DUBLU: putch(200).chenar). 238 . break. //Activeaza fereastra si o afiseaza pe ecran window(st. stiva[istiva]->u=dr. stiva[istiva]->v=jos. case DUBLU: putch(187). clrscr(). break. highvideo(). exit(1).2.stiva[istiva]->zonfer))==0){ printf("\nEroare la memorarea ecranului!"). exit(1).

int col. fereastra(4. break. vertical(jos-sus-1.lin++). gotoxy(dr-st. } orizontal(dr-st-2. } } void vertical(int a. switch(chenar){ case SIMPLU: putch(217).2.10. break. geninterrupt(0x10). } gotoxy(3. //Ascunde cursorul _AH=1. break.60.chenar). cprintf("%d".dr-st. break.int chenar) { while(a--) switch(chenar){ case SIMPLU: putch(196).int chenar) { while(a--) { gotoxy(col.jos-sus+1).20. } } } void main(void) { clrscr().6). switch(chenar){ case SIMPLU: putch(179).2.n). break. getch(). _CH=0x20.3).chenar). case DUBLU: putch(188). } void orizontal(int a. case DUBLU: putch(205). textattr(16*fond+culoare).break.4.3. } 239 . } normvideo().int lin. case DUBLU: putch(186). break.

1. int far *gm). 12. Ini ializarea modului grafic Pentru a se putea lucra în mod grafic trebuie realizată o ini ializare utilizând func ia initgraph. Func ia detectgraph detectează adaptorul grafic prezent la calculator şi păstrează valoarea corespunzătoare acestuia în zona spre care pointează gd. Ele se află în subdirectorului BGI. Aceste func ii se numesc drivere. Prototipul ei este: void far detectgraph(int far *gd.h. unde: Pointerul gd păstrează adresa uneia din valorile din tabelul următor (î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 nişte func ii standard corespunzătoare adaptorului grafic. Aceasta poate fi folosită singură sau împreună cu o altă func ie numită detectgraph care determină parametrii adaptorului grafic. 240 .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 fişierul graphics. În continuare sunt prezentate cele mai importante func ii ce permit gestiunea ecranului în mod grafic.

initgraph(&gd. int far unde: gd şi gm sunt pointeri ce au aceeaşi semnifica ie ca şi în cazul func iei detectgraph. Cele mai utilizate adaptoare sunt cele de tip EGA şi VGA. detectgraph(&gd. De exemplu dacă BGI este subdirector al directorului BORLANDC.&gm. ”C:\\BORLANDC\\BGI”).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 defineşte în aşa fel încât el să fie cel mai performant pentru adaptorul grafic curent. 241 . initgraph(int far *gd. Apelul func iei detectgraph trebuie să fie urmat de apelul func iei initgraph.int far *gm.gm. atunci se utilizează şirul de caractere: ”C:\\BORLANDC\\BGI” Exemplu: Pentru setarea în mod implicit a modului grafic se poate utiliza următoarea secven ă de instruc iuni: …………………………… int gd. …………………………… Doar după apelul func iei initgraph pot fi utilizate şi alte func ii de gestionare a ecranului în mod grafic. cale este pointer spre şirul de caractere care defineşte calea subdirectorului BGI care con ine driverele. Aceasta setează modul grafic în conformitate cu parametri stabili i de apelul prealabil al func iei detectgraph şi are următorul prototip: void far *cale).&gm).

void far getmoderange(int grafdriv. Fiecărui pixel îi corespunde o culoare ce este pătrată în memoria video. void far retorecrtmode(void) – ce permite revenirea la modul precedent. În afara acestor func ii mai pot fi utilizate şi următoarele func ii: void far setgraphmode (int mode) – utilizată pentru setarea modului grafic unde mode are valorile 0 – 4 pentru VGA.Utilizatorul poate defini el însuşi parametri pentru ini ializarea modului grafic. initgraph(&gd.y) unde: x – defineşte coloana în care este afişat pixelul. ”C:\\BORLANDC\\BGI”). int far *max) – defineşte valorile minimale şi maximale ale modului grafic utilizat. int far getgraphmode(void) – returnează codul modului grafic. y – defineşte linia în care este afişat pixelul. Culorile se codifică prin numere întregi din intervalul [0. Gestiunea culorilor Adaptoarele grafice sunt prevăzute cu o zonă de memorie în care se păstrează date specifice gestiunii ecranului. …………………………… setează modul grafic corespunzător unui adaptor grafic CGA cu rezolu ia 320*200 de puncte. 12. void far closegraph(void) – se utilizează pentru a ieşi din modul grafic. secven a următoare: …………………………… int gd=1. În mod grafic. Cele 64 de 242 .2. De exemplu.&gm. 0 – 2 pentru VGA. ecranul se consideră format din puncte luminoase numite pixeli.int far *min. Numărul maxim de culori care pot fi afişate cu ajutorul unui adaptor EGA este 64. char *far getdrivername(void) – returnează pointerul spre numele drieverului corespunzător adaptorului grafic curent. Această zonă de memorie poartă denumirea de memorie video..gm=0. 63] şi prin constante simbolice. void far graphdefaults(void) – repune parametri grafici la valorile implicite. char *far getmodename(int mod) – returnează pointerul spre numele modului grafic definit de codul numeric mod. Pozi ia pe ecran a unui pixel se defineşte prin două valori întregi: (x. 0-1 pentru EGA.

int far getbkcolor(void) – returnează indexul din tabloul care defineşte paleta pentru culoarea fundalului.culori nu pot fi afişate simultan. void far setcolor(int culoare) – setează culoarea utilizată pentru desenare. 63]). void far setallpalette(struct palettetype far* paleta) – modifică mai multe culori din paletă. culoarea fondului este întotdeauna cea corespunzătoare indicelui zero. iar culoarea pentru desenare este cea corespunzătoare indicelui 15. În cazul adaptorului EGA pe ecran se pot afişa cel mult 16 culori ce formează o paletă. 15] iar cod între [0. Palettetype este o structură definită ca mai jos: struct palettetype { unsigned char size. void far setpalette(int index.int cod) – setează o nouă culoare în paleta ce este utilizată la colorare (index ia valori între [0. int far getcolor(void) – returnează indexul din tabloul care defineşte paleta pentru culoarea de desenare. Paleta implicită este dată de tabelul următor: 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. Pentru controlul culorilor pot fi utilizate următoarele func ii: void far setbkcolor(int culoare) – modifică culoarea fundalului. 243 .

int far gety(void) – returnează pozi ia pe verticală a pixelului curent. 12. adică pe ecran se pot afişa m linii a n pixeli fiecare. Utilizarea textelor în mod grafic Afişarea 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 charsize) unde: font – defineşte setul de caractere şi poate lua următoarele valori: 244 .4. Informa ii referitoare la ecran pot fi ob inute cu ajutorul următoarelor func ii: int far getmaxx(void) – returnează coordonta maximă pe orizontală.y) numite coordonatele pixelului. Pozi ia unui pixel este dată de două numere întragi (x. Modificarea paletei curente cu ajutorul func iei setpalette sau setallpalette conduce la schimbarea corespunzătoare a culorilor afişate pe ecran în momentul apelului func iilor respective. Setarea ecranului În mod grafic. unde size – este dimensiunea paletei. }. int far getpalettesize(void) – returnează numărul culorilor componente ale paletei.0). int far getmaxy(void) – returnează coordonta maximă pe verticală.3. void far getpalette(struct palettetype far* paleta) – determină codurile culorilor componente ale paletei curente. Coloanele se numerotează de la stânga la dreapta. ecranul se compune din n*m puncte luminoase (pixeli). colors – este un tablou ale cărui elemente au ca valori codurile culorilor componente ale paletei care se defineşte. Pixelul aflat în stânga sus are coordonatele (0.signed char colors[MAXCOLORS+1]. 12.int direc ie. int far getmaxcolor(void) – returnează numărul maxim de culori diminuat cu 1. iar liniile de sus în jos. int far getx(void) – returnează pozi ia pe orizontală a pixelului curent.

oriz – defineşte încadrarea pe orizontală. . . Dimensiunile în pixeli ale unui şir de caractere se pot determina utilizând func iile textheight şi textwidth: 245 .în centru: CENTER_TEXT.marginea superioară: TOP_TEXT.char far* şir) . x. .în stânga: LEFT_TEXT. După setarea acestor parametri pot fi afişate texte folosind func iile outtext şi outtextxy care au următoarele prototipuri: void far outtext(char far* şir) . unde şir este un pointer spre zona de memorie în care se păstrează caracterele de afişat. astfel: . charsize – defineşte dimensiunea caracterului în pixeli.de jos în sus: VERT_DIR.int y. 10 Matricea utilizată pentru afişarea caracterului (în pixeli) 8*8 16*16 24*24 …….de la stânga la dreapta: HORIZ_DIR. . void far outtextxy(int x. astfel: . vert – defineşte încadrarea pe verticală. unde şir este un pointer spre zona de memorie în care se păstrează caracterele de afişat. 80*80 b) void far settextjustify(int oriz.marginea inferioară: BOTTOM_TEXT..în dreapta: RIGHT_TEXT. astfel: .Constantă simbolică DEFAULT_FONT TRIPLEX_FONT SMALL_FONT SANS_SERIF_FONT GOTHIC_FONT Valoare 0 1 2 3 4 direc ie – defineşte direc ia de scris a textului. int vert) – defineşte cadrajul textului.în centru: CENTER_TEXT. afişează caracterele începând cu pozi ia curentă de pe ecran. . astfel: Valoarea parametrului 1 2 3 ….y defineşte pozi ia de pe ecran unde se face afişarea.

12.sus) – coordonatele col ului stânga sus a zonei de pe ecran ce se salvează. utilizatorul are posibilitatea de a vizualiza altă pagină decât cea activă utilizând această func ie (această func ie poate fi utilă pentru anima ie). void far setvisualpage(int nrpag) – cu toate că în mod normal este vizualizată pe ecran pagina activă.d – indicator cu privire la decuparea desenului. Aceste păr i se numesc ferestre grafice. .void far textheight(char far* şir) – returnează înăl imea în pixeli a şirului păstrat în zona spre care pointează şir.sus) – coordonatele col ului stânga sus al ferestrei.void far* zt) – salvează o zonă dreptunghiulară de pe ecran. după apelul acestei func ii. atunci func iile de afişare a textelor şi de desenare nu pot scrie sau desena în afara limitelor ferestrei.5. şi anume culoarea de fond. int jos. void far clearviewport(void) – şterge fereastra activă.(dr. Dacă d are valoarea 1. Următoarele func ii sunt utilizate pentru prelucrarea ferestrelor grafice: void far setviewport(int st. iar pozi ia curentă a cursorului este punctul de coordonate relative (0. Gestiunea imaginilor În modul grafic.(st. int sus. unde: . ecranul poate fi partajat în mai multe păr i ce pot fi gestionate independent. Func iile următoare sunt utilizate pentru gestionarea paginilor void far setactivepage(int nrpag) – activează o pagină al cărei număr este specificat de parametrul nrpag. int dr. 246 . int dr. . int d) – defineşte o fereastră grafică.0). void far cleardevice(void) – şterge tot ecranul iar pozi ia curentă a cursorului este col ul din stânga sus al ecranului. void far textwidth(char far* şir) – returnează lăl imea în pixeli a şirului păstrat în zona spre care pointează şir. void far getimage(int st.(st.jos) – coordonatele col ului dreapta jos al ferestrei. int sus. Imaginea ecranului se păstrează în memoria video a adaptorului grafic şi formează o pagină. to i pixelii ferestrei au aceeaşi culoare. void far getviewsettings(struct viewporttype far* fereastra) – returnează parametri ferestrei active. unde: . int jos.

int culoare) – afişează un pixel pe ecran în punctul de coordonate (x.y) (relativ la fereastra activă) şi având culoarea culoare. sus. Parametrul op se defineşte 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 completând datele aflate în memorie 12. .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.(dr. int y) – determină culoarea unui pixel aflat pe ecran în pozi ia (x. int sus. 247 .zt – pointer spre zona de memorie în care se păstrează imaginea ce se va afişa pe ecran.op – defineşte opera ia între datele aflate în zona spre care pointează zt şi cele existente pe ecran în zona dreptunghiulară definită de parametri st.(st. int sus. . void far moveto(int x.y). . int y) – mută cursorul în dreptul pixelului de coordonate (x.(dr.y). int y. unde: . int op) – afişează oriunde pe ecran o zonă dreptunghiulară salvată cu func ia getimage. int dr. unsigned far imagesize(int st.void far* zt. unsigned far getpixel(int x.sus) – coordonatele col ului stânga sus a zonei de pe ecran. 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. .sus) – coordonatele col ului stânga sus a zonei de pe ecran ce se salvează.6.jos) – coordonatele col ului dreapta jos a zonei de pe ecran. int jos.(st. int jos) – determină dimensiunea unei zone dreptunghiulare de pe ecran. unde: . void far putimage(int st.

int ycentru.…. int yf) – trasează un segment de dreaptă între punctele de coordonate (xi. int unghifin. int dy) – mută cursorul în dreptul pixelului de coordonate (x+dx.yi) şi (xf. parametrul nr specificând numărul de laturi iar tabpct este un pointer spre un tablou de întregi ce definesc vârfurile liniei poligonale păstrate sub forma: abscisa_i. void far rectangle(int st.ycentru). unsigned şablon. cu (xcentru. int raza) – trasează un cerc. unde (x. int y) – trasează un segment de dreaptă între punctul curent şi punctul de coordonate (x. int unghistart. semiaxa mare definită de parametrul semiaxamare iar semiaxa mică definită de parametrul semiaxamică..y+dy). void far linerel(int dx. int sus.void far moverel(int dx.int raza) – trasează un arc de cerc. nr+1. unde: stil – este un întreg din intervalul [0.y) sunt coordonatele punctului curent. void far line(int xi.ycentru) coordonatele centrului şi raza raza acestuia. unde (x.y) reprezintă coordonatele pixelului curent. int far* tabpct) – trasează o linie polignală. int unghifin.yf). int unghistart.2. void far setlinestyle(int stil.y+dy). void far circle(int xcentru. int jos) – trasează un dreptunghi definit de col urile diagonal opuse. void far lineto(int x. int ycentru. void far arc(int xcentru. unghiurile fiind exprimate în grade sexagesimale.int semiaxamare. int grosime) – defineşte stilul utilizat pentru trasarea liniilor. int xf. int dy) – trasează un segment de dreaptă între punctul curent şi punctul de coordonate (x+dx. void far ellipse(int xcentru. int semiaxamică) – trasează un arc de elipsă cu centrul în punctul de coordonate (xcentru.ordonata_i unde i are valorile 1.4] care defineşte stilul liniei conform următorului 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 aceeaşi dimensiune Stil definit de utilizator prin şablon 248 . int dr. int yi.y). int ycentru. void far drawpoly(int nr.

int dr. int sus. int semiaxamică) – desenează o elipsă colorată.int culoare) – este utilizată pentru a defini o haşură a utilizatorului. int ycentru. astfel: 249 . void far fillpoly(int nr. int jos) – are aceeaşi semnifica ie cu func ia rectangle însă dreptunghiul este colorat. void far pieslice(int xcentru. void far bar3d(int st. int ind) – func ia desenează o prismă colorată pentru ind diferit de zero. void far setfillstyle(int haşura. int semiaxamare. void far fillellipse(int xcentru. int unghifin.şablon – defineşte stilul liniei şi are sens doar când parametrul stil are valoarea 4. haşura – defineşte haşura 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 dr. void far getlinesettingstype(struct linesettingstype far* linieinfo) – este utilizată pentru a determina stilul curent. int ycentru. pentru ind=0. int sus. astfel: NORM_WIDTH – valoarea 1 pixel şi THICK_WIDTH – valoarea 3 pixeli. int profunzime. int culoare) – defineşte modul de colorare al figurilor. astfel: culoare – defineşte culoarea utilizată pentru haşurare.int raza) – desenează un sector de cerc colorat. int jos. int far* tabpct) – desenează un poligon colorat. nu se trasează partea de sus a prismei. grosime – defineşte lă imea liniei în pixeli. void far bar(int st. int unghistart.

line(0. printf("reintroduce-ti intervalul astfel incat sa cuprins intre -1 si 1:\n ").y. fie 250 .h> #include <graphics.&a). sunt respectate { while ((a<l1)||(b>l2)) { clrscr().h> #include <conio. line(x. void far floodfill(int x.0. line(x. line(x. h_utilizator – este un pointer spre o zonă de memorie care defineşte haşura utilizatorului. setbkcolor(0). #include <stdio.y.y). scanf("%f".0. } void interval(int l1. setbkcolor(14).y) – reprezintă coordonatele unui punct din interiorul domeniului închis. void far getfillsettings(struct fillsettingstype far* stilculoare) – este utilizată pentru determinarea stilului curent de colorare.y-4. astfel: (x. line(2*x-4.b.x+4.2*y).y).2*x. culoare – defineşte culoarea utilizată la trasarea conturului figurii (interiorul este colorat în conformitate cu setările efectuate de func ia setfillstyle).culoare – defineşte culoarea de haşurare. float a.x. setcolor(12). Exemplu: Prezentăm în acest exemplu un model de utilizare a modului grafic pentru trasarea graficelor unor func ii matematice elementare. line(2*x-4.x-4.y+4. printf("a=").h> int x. void desen(void) //functia care deseneaza axele si //coloreaza ecranul { cleardevice().y). int l2) //functia care verifica // daca intervalele pe care sunt definite functiile // trigonometrice. cleardevice().4). int y.0. int culoare) – este o func ie utilizată pentru colorarea unui domeniu închis. printf("\n").2*x.4).2*x.h> #include <math.

t.y1.i<=b.} void main() { int p.l.} float acosx(float x) { return acos(x). p=0.} float tanx(float x) { return tan(x). h=0. for(i=a.} void grafic(float (*trig)(float))//functia care traseaza // graficul functiilor trigonometrice { float ymax.i+=h) if (ymax<abs(trig(i))) ymax=abs(trig(i)).} float cosx(float x) { return cos(x). ly=(y-25)/(ymax+1). setbkcolor(1). 251 . if (ly>lx) ly=lx. putpixel(x+i1.i1. printf("\n").ly.} } float sinx (float x) { return sin(x).h.i<=b. printf("1 : sin(x)\n"). initgraph(&dr. if (ymax>y/2) ymax=y-25. p=1. else lx=(x-25)/(abs(b)+1).&b)."c:\\borlandc\\bgi"). while(p==0) { setbkcolor(1). for(i=a. } desen().i.i+=h) { y0=trig(i).&modgr.001*(b-a).y0. y1=y0*ly.y-y1.lx. modgr.y=getmaxy()/2. i1=i*lx .} float atanx(float x) { return atan(x).4).} float ctanx(float x) { return 1/tan(x). ymax=0.dr=DETECT. scanf("%f".printf("b="). if (abs(a)>abs(b)) lx=(x-25)/(abs(a)+1).} float actanx(float x) { return atan(1/x).} float asinx(float x) { return asin(x). x=getmaxx()/2.

printf("6 : arccos(x)\n").cleardevice(). } 252 . printf("3 : tg(x)\n"). printf("Alegeti nr. scanf("%f". } getch(). scanf("%d". &l). p=0. grafic(asinx).}} closegraph(). case 5:interval(-1. cleardevice().1). switch(t) { case 1:grafic(sinx). printf("8 : arcctg(x)\n"). do{ printf("a="). printf("b="). scanf("%f".&t).&a). case 4:grafic(ctanx). case 7:grafic(atanx). desen(). break. case 3:grafic(tanx). case 8:grafic(actanx).printf("2 : cos(x)\n"). case 2:grafic(cosx). if (l==1){clrscr(). while(t<1 || t>8 ) { printf("Reintroducet t-ul cuprins intre 1 si 8\n "). if (a>b) printf("Reintroduce-ti intervalul astfel incat a sa fie mai mic ca b:\n "). break. break. setbkcolor(0).} printf("Dati intervalul: \n"). printf("\n"). grafic(acosx). } while(a>b). printf("\n").&t). break. clrscr(). break. break.1). case 6:interval(-1. scanf("%d". printf("7 : arctg(x)\n"). printf("Doriti graficul altei functii? 1-DA 0-NU :"). break.&b). defalut: p=0 . scanf("%d". printf("4 : ctg(x)\n"). printf("t="). printf("5 : arcsin(x)\n"). break. corespunzator functiei dorite: ").

.tan(x) .1 în 0.sinusul lui x. Dacă un argument al unei func ii matematice nu este în domeniul pentru care a fost definită func ia.func ii hiperbolice.h". Dacă o func ie produce un rezultat prea mare pentru a fi reprezentat printr-un double. din 0. Toate func iile matematice sunt incluse în fişierul antet "math. # include <math. 13. Exemplu: Programul următor afişează valorile sinusului. apare o depăşire.func ii trigonometrice. func ia returnând HUGE_VAL cu semnul adecvat iar "errno" este modificat la ERANGE. utilizate pentru a semnala erorile de domeniu şi de plajă ale func iei. Macrodefini iile EDOM şi ERANGE se găsesc în fişierul "errno. iar "errno" este modificat la ERANGE în func ie de implementare.h" şi sunt constante întregi diferite de zero.tangenta lui x. . Dacă se produce subdepăşire.1 Func ii trigonometrice . atunci func ia întoarce 0 şi în domeniul de eroare. . x în radiani x în radiani x în radiani .cos(x) .h") este o valoare pozitivă de tip double. . Aceste func ii se împart în următoarele categorii: . cosinusului şi tangentei unghiului a[-1. . func ia întoarce zero.+1] radiani.cosinusul lui x.0.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.1. . 253 .h> void main() { double val = -1.alte tipuri. Acesta mai con ine o serie de macrodefini ii cum ar fi EDOM. "errno" este modificat la EDOM. HUGE_VAL (aflată tot în "errno. .sin(x) .func ii exponen iale şi logaritmice. ERANGE şi HUGE_VAL.

0)).+1]. .logaritmul natural al lui x.4 Func ii exponen iale şi logaritmice . asin(val)). x > 0 . Exemplu: Programul următor afişează valorile logaritmului natural şi logaritmului zecimal din 1 în 1 al numerelor de la 1 la 10. } while (val <= 1.log10(x) . .exponentiala lui x.do { printf("sinusul lui %f este %f\n". . printf("arccos lui %f este %f\n".log(x) . do { printf("arcsin lui %f este %f\n". exp(1. val += 0.cosh(x) . do{printf("%f %f %f\n". } 13.acos(x) . .exp(x) . cu x [-1. asin(val)). x R .cosinus hipebolic de x.} while (val <= 1. 254 .1.arcsinusul lui x. } 13. x R .1. sin(val)). asin(val)).arccosinusul lui x.0).arctangenta lui x.tanh(x) . printf("tangenta lui %f este %f\n". val. x R . printf("arctg lui %f este %f\n". x R . x R .1] . val += 0. val. Exemplu: Programul următor afişează valorile arcsinusului.1.h> void main() { double val =1. x>0 .sinus hipebolic de x.h> void main() { double val = -1. 13. # include <math. cu x [-1. cos(val)). # include <math.returneaza arctg (y/x). .logaritmul zecimal al lui x.0).val. din 0. val. tan(val)). Exemplu: printf ("Valoarea lui e este: %f".3 Func ii hiperbolice .1 în 0.x) . val. .sinh(x) .atan(y. val.val. printf("cosinusul lui %f este %f\n".0.asin(x) .atan(x) . .1] . arccosinusului şi arctangentei unghiului a[-1.0.log10(val)).log(val). .2 Func ii trigonometrice inverse .tangenta hipebolica de x.

care returnează numere întregi aleatore.5 Generarea de numere aleatoare În multe aplica ii este necesară generarea de numere aleatoare.random(100)).10-random(20)). # include <math.} . for(k=0. aleator.y)). for(k=0. for(k=0. Func ia rand are următorul prototip: int rand(void) şi returnează un număr întreg. Pentru asemenea cazuri limbajul C dispune de două func ii.h> void main(void) { int k.pow(x.0. pow(x. val_maxima]. y = 0. cuprins în intervalul [0. printf(”Valorile furnizate de functia rand\n”).y).h> #include <stdlib. } while (val < 11.0).0.h> void main() { double x =10.k++) printf(”%d ”. Func ia random are prototipul: int random(int val_maxima) şi returnează un număr întreg.k<100. Următorul program exemplifică utilizarea acestor func ii: Exemplu: #include <stdio. } 13.val ++.h).} while (val < 11.rand()). aleator. printf(”Valori reale intre 0 si 1\n”).k++) printf(”%d ”.0). O eroare de domeniu apare dacă x = 0 şi y = 0 sau dacă x < 0 şi y nu este întreg. do { printf ("%f\n".k<100.k++) printf(”%d ”.k<10. y ++. cuprins în intervalul de la 0 la RAND_MAX (valoare definită în fişierul antet stdlib. printf(”Valori intregi intre -10 si 10\n”).random(10)/10.0). func ia calculeaza xy. for(k=0.k++) printf(”%f ”. rand şi random.} 255 . printf(”Valorile furnizate de functia random(100)\n”). Exemplu: Programul următor afişeaza primele 11 puteri ale lui 10.k<10. Pentru generarea de numere aleatoare în virgulă mobilă se împarte rezultatul func iei random la o valoare întreagă.

ambele păr i ale rezultatului sunt 0.împarte pe x în parte întreagă şi parte frac ionară. ceil(1. convertit la double (ex. floor(-1.calculeaza x*2n . y) Caracterizarea func iei . . .02) va returna valoarea -2.6 Alte tipuri de func ii matematice Nume func ie sqrt(x) ceil(x) floor(x) fabs(x) ldexp(x. mai mic sau egal cu x. memorează partea întreagă în "*ip" şi întoarce partea frac ionară. fiecare cu acelaşi semn că x. mai mare că x.restul în virgulă mobila a lui x/y.cel mai mic întreg. 256 . convertit la double (exemplu: floor(1.cel mai mare întreg.0.întoarce x într-o func ie normalizată în intervalul [1/2. cu acelaşi semn ca x. care se memoreaza în "*exp". modf(x. unde n este de tip int.05) va returna valoarea 2).modulul numărului x.0). dacă x este 0. . Modul de utilizare al acestor func ii este similar cu al celorlalte func ii matematice descrise în acest capitol. modf(x. . n) fmod(x.13. double *ip) . sqrt(x)= x .02) va returna valoarea 1. 1] şi o putere a lui 2.radicalul lui x. double *ip) .

1 Memoria conven ională Primul PC compatibil IBM utiliza de obicei între 64Kb şi 256Kb memorie RAM (Read Only Memory). alte dispozitive HARD mapate în memorie şi BIOS (Basic Input-Output Services – servicii intrareieşire de bază).1. Volumul şi tipul de memorie instalată poate fi determinat utilizând comanda DOS: C:>MEM /CLASSIFY (pentru versiuni ale sistemului de operare DOS mai mari de varianta 5). Programele DOS rulează. driverele de dispozitive. memoria conven ională este importantă când se rulează programe în cadrul unei ferestre DOS sub Windows. ceea ce înseamnă că eliberarea memoriei conven ionale nu are semnifica ie sub acest sistem de operare. memoria conven ională a unui PC este formată din primul 1Mb de RAM. cu primii 640Kb de memorie conven ională. Pe atunci această memorie era mai mult decât suficientă.1 Gestionarea memoriei Un calculator poate avea trei tipuri de memorie: conven ională. Sistemul de operare Windows utilizează modelul de memorie virtuală pentru a gestiona memoria. extinsă şi expandată. ceea ce afectează performan a programelor. 14. Astăzi. în mod obişnuit. Sistemul de operare DOS dispune de capacită i de gestionare a memoriei ce pot maximiza performan ele calculatorului. Structura memoriei conven ionale a unui calculator personal este următoarea: 257 . PC-ul utilizează restul de 384Kb de memorie (numită memorie rezervată sau memorie superioară) pentru memoria video a calculatorului. În programare memoria constituie un factor important ce influen ează viteza de lucru a programelor.Capitolul XIV ELEMENTE DE PROGRAMARE AVANSATĂ 14. Fiecare tip de memorie are diferite viteze de acces. Însă.

sau de ambele. 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 număr mare de date). În acest caz datele sunt accesate rapid prin utilizarea de adrese near.COM Memorie pentru programe Intrări CONFIG. Din această cauză trebuie ales modelul cel mai mic pentru necesită ile programului. Modele sunt foarte importante deoarece.SYS Nucleul DOS Zona de comunica ii BIOS Vectori de întrerupere BIOS PC-ul împarte memoria în blocuri de 64Kb numite segmente. însă cu cât memoria utilizată este mai mare cu atât viteza de execu ie a programului scade. apelurile de func ii se fac utilizând adrese far. 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. 258 . c) medium – utilizează un segment de 64Kb pentru date şi două sau mai multe segmente pentru codul programului. dacă se utilizează un model de memorie necorespunzător. în schimb însă.BIOS ROM Memorie rezervată Memorie video COMMAND. Majoritatea compilatoarelor acceptă următoarele 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). În mod obişnuit. Modelul de memorie defineşte numărul de segmente pe care le poate folosi pentru fiecare. Compilatorul va alege un model de memorie suficient de mare pentru a rula programul. programul poate să nu de ină suficientă memorie pentru execu ie. b) small – utilizează un segment de memorie pentru cod şi un segment pentru date (este cel mai obişnuit model de memorie).

o op iune în cadrul liniei de comandă a compilatorului. else{ printf(”Completeaza matricea\n”).k++) matrice_uriasa[k]=k%32768.matrice_uriasa[k]).h> void main(void) { long int k. În tabelul următor 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 următoarea secven ă de instruc iuni: #ifndef _MEDIUM_ printf(”Programul cere modelul de memorie medium\n”). hfree(matrice_uriasa). for(k=0.k<100000L. f) huge – este un model utilizat doar în cazul utilizării unor matrici mai mari de 64Kb. } } Pentru selectarea unui anumit model de memorie se include. Pentru a stoca o astfel de matrice programul trebuie să utilizeze cuvântul cheie huge pentru a crea un pointer.e) large – alocă mai multe segmente atât pentru date cât şi pentru cod şi este cel mai lent model de memorie din cele prezentate până acum. if ((matrice_uriasa=(int huge*) halloc(100000L.k<100000L. astfel: int huge *matrice_uriaşă după care programul trebuie să utilizeze func ia halloc pentru alocarea memoriei şi func ia hfree pentru eliberarea acesteia.h> #include <malloc. El trebuie utilizat doar ca ultimă resursă. int huge *matrice_uriasa. de regulă. Exemplul următor alocă o matrice de 400000 octe i: Exemplu: #include <stdio. Majoritatea compilatoarelor predefinesc o constantă specifică pentru a determina modelul curent de memorie. for(k=0.sizeof (long int)))==NULL) printf(”Eroare la alocarea matricii”).k++) printf(”%d ”. 259 .

exit(1). spa iul de memorie ocupat de stivă diferă. Valoarea minimă a stivei este 4Kb.sys. numite pagini în care se încarcă paginile logice ale programului. care în DOS este de obicei himem. De exemplu. C plasează aceşti parametri în stivă.3 Memoria extinsă Calculatoarele cu procesoare peste 386 utilizează adresarea pe 32 de bi i ceea ce le dă posibilitatea de accesare directă de până la 4Gb de memorie. În 260 . 14. #endif Atunci când 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).4 Stiva Stiva este o regiune de memorie în cadrul căreia programele păstrează temporar datele pe durata execu iei. Intel şi Microsoft au creat o specifica ie pentru memoria expandată.1. trebuie încărcat un driver de dispozitiv pentru memoria extinsă.1. 14. Pentru a utiliza însă memoria extinsă este necesară trecerea la modul protejat de lucru al procesorului. Stiva este numită astfel deoarece ultimele valori depuse sunt primele extrase.2 Memoria expandată În cazul programelor mari o memorie de numai 1Mb este insuficientă. Mai întâi în zona de memorie superioară se alocă un bloc de 64Kb după care acest bloc de memorie este împăr it în patru sec iuni de 16Kb. atunci când programele transmit parametri către o func ie. companiile Lotus. De exemplu un program de 128Kb este împăr it în opt pagini de 16Kb fiecare care sunt încărcate în func ie de necesită ile programului în zona rezervată de 64Kb. Pentru a accesa memoria extinsă. fie func ia fmalloc pentru a aloca memorie far. Când func ia îşi încheie execu ia aceştia sunt scoşi din stivă. În func ie de modelul de memorie utilizat. mod de lucru în care datele unui program nu pot fi scrise peste datele altui program ce rulează simultan cu acesta. Programatorii au numit memoria de peste 1Mb memorie extinsă. care combină software şi o platformă specială de memorie expandată pentru a „păcăli” PC-ul în scopul accesării unor volume mari de memorie.1. Pentru a permite accesul la mai mult de 1Mb de memorie. 14.

Mul i programatori confundă serviciile DOS cu serviciile BIOS. să acceseze dispozitive.h> #include <dos._stklen). datele depuse în stivă pot fi suprapuse peste datele programului. Exemplul următor prezintă modul de determinare a dimensiunii stivei utilizând func ia _stklen. sistemul DOS pune la dispozi ie servicii ce permit programelor să aloce memorie. serviciile BIOS. Tabelul următor prezintă rela ia dintre componenta HARD a calculatorului. va apărea o eroare de depăşire a stivei (stack-overflow). DOS şi componenta SOFT. Programatorii au au proiectat rutinele BIOS pentru a fi utilizate de programe în limbaj de asamblare. Dacă programul a dezactivat testarea stivei. Biblioteca limbajului C oferă o interfa ă la multe servicii DOS. şi să gestioneze alte resurse ale sistemului. Dacă un program plasează în stivă mai multe informa ii decât poate re ine aceasta.cazul modelelor compact sau large. În plus. Programe DOS BIOS HARDWARE Nivel înalt | | Nivelul cel mai jos 261 .h> void main(void) { printf(”Dimensiunea stivei este de %d octeti”.2 Servicii DOS şi BIOS Aşa cum am men ionat în paragraful anterior. DOS este un sistem de operare pentru calculatoarele compatibile IBM PC. Pe scurt. Sistemul DOS permite rularea programelor şi păstrează informa ia pe disc. prin intermediul func iilor. cum ar fi imprimanta. 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ă. majoritatea compilatoarelor de C dispun de func iide bibliotecă ce permit utilizarea acestor servicii fără a avea nevoie de limbaje de asamblare. C alocă pentru stivă un întreg segment de 64Kb. Exemplu: #include <stdio. pentru a citi caractere de la tastatură sau pentru a citi sau scrie pe disc. } 14. totuşi. BIOS-ul reprezintă serviciile de intrare-ieşire de bază.

d. UNIX. iar programele deasupra sistemului DOS. 1 pentru LPT2. BIOS este situat imediat deasupra componentei hardware. Func ia biosprint returnează o valoarea înteagă pe un octet ai cărui bi i au următoarea semnifica ie: 0 – dispozitiv în pauză.2. Uneori însă. 2 – citeşte starea imprimantei.h: int biosprint(int comanda. 3 – eroare I/O.Aşa cum se observă.culatorului. programul nu va mai trebui modificat pentru a putea fi rulat sub WINDOWS sau UNIX. În acest caz. Însă. 14.int octet.1 Serviciile BIOS Prezentăm în continuare o serie de servicii BIOS ce pot fi accesate utilizând func ii de bibliotecă ale limbajului C. 1) accesul la imprimantă Înainte ca un program să scrie ieşirea la imprimantă utilizând indicatorul de fişier stdprn se poate face o verificare dacă imprimanta este conectată şi dacă are hârtie utilizând func ia biosprint din fişierul antet bios. Toate versiunile de WINDOWS vor apela propriile lor servicii de sistem.int nr_port) unde comanda specifică una din următoarele opera ii: 0 – tipăreşte octetul specificat. aceasta să fie utilizată pentru a mări portabilitatea programelor şi la calculatoarele ce utilizează alte sisteme de operare (WINDOWS. serviciile de sistem WINDOWS apelează până la urmă serviciile BIOS penttru a accesa componentele hardware ale cal. 1 – ini ializează portul imprimantei. programele pot evita serviciile DOS şi BIOS şi pot accesa direct o componentă hardware (cum este cazul memoriei video). serviciiile DOS deasupra serviciilor BIOS. 5 – lipsă hârtie. ş. Se recomandă ca ori de câte ori poate fi utilizată o func ie de bibliotecă C în locul unui serviciu DOS sau BIOS. 4 – imprimantă selectată. etc. 262 .m. Parametrul octet specifică valoarea ASCII a caracterului ce se doreşte a fi scris la imprimantă iar nr_port specifică portul imprimantei care poate fi 0 pentru LPT1.).a.

7 – dispozitivul nu este ocupat. int unitate. care este 0 pentru A. 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. func ia returnează valoarea 0. int head. valoarea returnată precizează eroarea. C-ul pune la dispozi ie func ia _bios_keybrd ce are următoarea sintaxă: unsigned _bios_keybrd(unsigned comanda) 263 . int nr_sector. void *buffer) unde parametrul unitate precizează numărul unită ii. Dacă apare o eroare. 1 pentru B. 2) opera ii intrare/ieşire Opera iile intrare/ieşire de nivel jos pot fi realizate utilizând func ia biodisk ce are următoarea sintaxă: int biodisk(int operatie. int sector. 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 Citeşte numărul precizat de sectoare Scrie numărul precizat de sectoare Verifică numărul precizat de sectoare Formatează pista specificată Formatează pista specificată şi marchează sectoarele defecte Formatează unitatea începând 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 Citeşte bufferul sectorului Scrie bufferul sectorului Testează dacă unitatea este pregătită Recalibrează unitatea Execută diagnosticarea unită ii de RAM Execută diagnosticarea unită ii Execută diagnosticarea internă a controlerului Dacă se execută cu succes. 3) servicii de tastatură din BIOS Pentru accesul la serviciile de tastatură din BIOS. Parametrii head.6 – confirmare dispozitiv. şi aşa mai departe. track. int track.

înseamnă că nici o intrare de la tastatură nu este prezentă. Pentru aceasta se utilizează func ia _bios_equiplist care are următoarea sintaxă: 264 . utilizatorul a apăsat 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 apăsată Bit 2 – CTRL este apăsată Bit 1 – SHIFT stânga este apăsată Bit 0 – SHIFT dreapta este apăsată Indică func iei să citească un caracter de la tastatură. utilizatorul a apăsat CTRL C Func ia acceptă inclusiv tastele speciale. Dacă func ia returnează 0. cum ar fi tastele cu săge i Determină dacă este prezent un caracter la bufferul tastaturii. inclusiv tastele speciale.unde parametrul comanda specifică opera ia dorită şi poate avea una din următoarele 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. 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 apăsată Bit 10 – CTRL dreapta este apăsată Bit 9 – ALT stânga este apăsată Bit 8 – CTRL stânga este apăsată 4) ob inerea listei cu echipamente din BIOS Unele programe necesită determinarea caracteristicilor hardware ale calculatorului. cum ar fi tastele cu săge i Returnează starea tastelor de control. Dacă valoarea returnată este 0xFFFF. Dacă func ia returnează 0. Dacă valoarea returnată este 0xFFFF. înseamnă că nici o intrare de la tastatură nu este prezentă.

unde 0 corespunde lui COM1. 13 – imprimanta serială. 0 – prezen a unită ii de disc flexibile. bitul are valoarea 0 dacă există DMA şi 1 dacă nu există. 10-mod video 80x25 color. 11-mod video 80x25 mono. 12 – adaptorul de jocuri. 1 lui COM2 şi aşa mai departe.int port. 1 – prezen a coprocesorului matematic.char octet). 5) controlul intrărilor şi ieşirilor pentru portul serial Pentru a executa opera ii intrare/ieşire utilizând portul serial se utilizează func ia bioscom ce are următoarea sintaxă: unsigned bioscom(int comanda. Parametrul octet specifică fie octetul pentru ieşire. 01-32Kb. 5:4 – modul video: 00-neutilizat. 1164Kb. Valoarea returnată de această func ie nu cuprinde memoria extinsă. fie valorile de comunicare dorite. 3:2 – dimensiunea memorie RAM: 00-16Kb. 11:10:9 – numărul de porturi seriale COM (de la 0 la 7). 7:6 – numărul drieverelor de disc. Parametrul comanda specifică opera ia dorită şi poate avea una din următoarele valori: _COM_INIT _COM_RECEIVE _COM_SEND _COM_STATUS Stabileşte valorile pentru comunicare ale portului Primeşte un octet de la port Trimite un octet la port Returnează valorile portului Parametrul port specifică portul serial ce se doreşte a fi utilizat.unsigned _bios_equiplist(void). expandată sau superioară. 10-48Kb. 01-mod video 40x25 mono. 8 – prezen a DMA (Direct Memory Acces). 7) citirea cronometrului BIOS 265 . Func ia returnează o valoare pe 16 bi i a căror valoare are următoarea semnifica ie: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 15:14 – numărul de imprimante paralele instalate (de la 0 la 3). 6) determinarea volumului de memorie conven ională BIOS Pentru a determina memoria conven ională ce poate fi utilizată de către un proggram se utilizează func ia biosmemory ce are următoarea sintaxă: int biosmemory(void).

h> void main() { unsigned frecventa. Acest cronometru este util pentru a genera punctul ini ial al unui generator de numere aleatoare. utilizată pentru a citi sau a fixa cronometrul BIOS.long *batai).2 ori pe secundă. 1 – pentru a fixa valoarea cronometrului la valoarea timp_nou.frecventa+=50) { sound(frecventa). 1) suspendarea temporară a unui program Execu ia unui program poate fi suspendată temporar utilizând func ia sleep.frecventa<=1000.frecventa-=50) { sound(frecventa).frecventa>=500. Sintaxa acestor func ii este următoarea: long biostime(int operatie. } for (frecventa=1000. void sound(unsigned frecventa) deconectează difuzorul. } } 266 .BIOS are incorporat un ceas intern ce bate de 18.h> #include <conio.2 Serviciile DOS În acest paragraf prezentăm o serie de servicii DOS ce pot fi accesate utilizând func ii de bibliotecă ale limbajului C.h: void sleep(unsigned secunde). delay(50). parametrul secunde specificând numărul de secunde pe care este suspendat programul. Această func ie poate fi. Multe compilatoare de C pun la dispozi ie două func ii pentru accesul la cronometrul BIOS: biostime şi _bios_timeofday. de asemenea. 2) utilizarea sunetelor Generarea de sunete ce utilizează difuzorul calculatorului se realizează utilizând func iile sound şi nosound: void sound(unsigned frecventa) generează un sunet cu frecven a frecventa.long timp_nou). Parametrul opera ie poate lua două valori: 0 – dacă se doreşte ca func ia să citească valoarea curentă a cronometrului. 14. long _bios_timeofday(int operatie.h din fişierul antet dos. Programul următor generează un sunet de sirenă dezactivat la apăsarea unei taste: Exemplu: #include <dos.2. do{ for (frecventa=500. delay(50).

} 3) ob inerea de informa ii despre erori în DOS În cazul în care un serviciu al sistemului DOS eşuează. apelul serviciului DOS nu a avut nici o eroare. Clasa erorii descrie categotia erorii. cu o întârziere.//sursa erorii }.//actiune recomandata int de_locus.while(!kbhit()). //eroare int de_class. astfel: Mai întâi încearcă din nou. apoi cere interven ia 02H utilizatorului Cere interven ia utilizatorului pentru solu ie 03H Renun ă şi elimină 04H Renun ă. apoi cere interven ia utilizatorului 01H Încearcă din nou. //clasa erorii int de_action. dar nu elimina 05H Ignoră eroarea 06H Încearcă din nou după interven ia utilizatorului 07H Parametrul de_locus specifică sursa erorii. astfel: 01H Resurse depăşite 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 neîntâlnit 09H Format nevalid 0AH Articol blocat 0BH Eroare de suport 0CH Articolul există 0DH Eroare necunoscută Parametrul de_action indică programului cum să răspundă erorii. Dacă func ia returnează valoarea 0. astfel: 267 . unde structura DOSERROR are următoarele câmpuri: struct DOSERROR{ int de_exterror. nosound(). programele pot cere informa ii suplimentare despre acea eroare folosind func ia dosexterr: int dosexterr(struct DOSERROR *info_eroare).

Pentru astfel de cazuri se utillizează func ia segread: void segread(struct SREGS *segs). 268 . altfel returnează -1. Parametrul comanda este un şir de caracter care con ine numele comenzii DOS sau a fişierului de comenzi.int outport (int adresa_port). Func ia delay are însă ca parametru o constantă exprimată în milisecunde: void delay(unsigned milisecunde).citeşte un cuvânt de la portul specificat de parametrul adresa_port. datele şi stiva sunt controlate de compilator utilizând patru registre de segment: CS.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. similară func iei sleep. 7) apelarea unei comenzi interne DOS Pentru apelarea unei comenzi DOS sau a unui fişier pentru comenzi se utilizează func ia system: int system(const char *comanda). .scrie un cuvânt de la portul specificat de parametrul adresa_port. . unsigned int ds.int outportb (int adresa_port). ES.int inport (int adresa_port). . 6) suspendarea unui program Pentru suspendarea unui program pe un anumit interval de timp se poate utiliza func ia delay. compilatoarele de C pun la dispozi ie următoarele func ii: . SS. se returnează valoarea 0.citeşte un octet de la portul specificat de parametrul adresa_port.int inportb (int adresa_port). . . DS. . unsigned int cs. . Dacă func ia reuşeşte să execute comanda. unsigned int ss. } 5) accesul la valorile de port Pentrul controlul hardware de nivel inferior. În unele cazuri este necesar să se cunoască valoarea acestor registre.scrie un octet de la portul specificat de parametrul adresa_port. Structura SREGS are următoarele câmpuri: struct SREGS { unsigned int es.

void _enable(void). } Dacă se doreşte crearea unui program de tratare a unei întreruperi. Determinarea vectorului de întrerupere se realizează utilizând func ia _dos_getvect în modul următor: void interrupt(* _dos_getvect(unsigned nr_intr))(). Parametrul nr_intr specifică întreruperea al cărui vector trebuie modificat.Programul următor prezintă utilizarea func iei system. void interrupt(* handler)()). _dos_getvect(k)). Programul următor va afişa vectorii pentru toate întreruperile calculatorului: Exemplu: #include <stdio. unde parametrul intrerupere specifică întreruperea generată.k++) printf(”Intrerupere: %x Vector %lx\n”.h> void main(void) { if(system("DIR")) printf("EROARE!\n"). vectorul de întrerupere trebuie atribuit acestui program. 269 . for(k=0. Exemplu: #include <stdlib.k. Parametrul nr_intr specifică numărul întreruperii dorite ce poate avea valori de la 0 la 255.h> void main(void) { int k. Dacă se doreşte 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). Această atribuire se realizează cu ajutorul func iei _dos_setvect: void _dos_setvect(unsigned nr_intr. Pentru activarea şi dezactivarea întreruperilor se utilizează func iile: void _disable(void).h> #include <stdio.h> #include <dos.k<=255. } 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.

Atunci când este compilat şi link-editat un program.obj ce con ine o serie de func ii pe care dorim să le păstrăm într-o bibliotecă. 270 . Aceste fişiere con in biblioteci obiect.lib care să con ină acest fişier obiect se realizează cu următoarea linie de comandă: C:\>tlib biblioteca.3. se remarcă multe fişiere cu extensia LIB. Crearea unei bilioteci biblioteca.3 Bibliotecile C Dacă se examinează fişierele ce înso esc un compilator C.3.lib + functie. ştergerea unuia sau mai multor fişiere obiect din bibliotecă. 14. Func ia definită în acest fişier obiect poate fi reutilizată în alt program utilizând următoarea instruc iune: C:\>bc fisier_nou.c prin compilare se ob ine fişierul obiect func ie. se poate compila fişierul ce con ine func ia respectivă pentru a crea codul obiect (de exemplu din fişierul func ie. func iile acestuia sunt disponibile pentru compilarea şi legarea noilor programe. numele programului de bibliotecă şi op iunile liniei de comandă pe care programul le acceptă vor diferi. 14. Presupunem că în urma compilării am creat fişierul obiect func ie. Când sunt create func ii utile ce sunt necesare şi în alte programe.obj Totuşi. se pot construi biblioteci în care aceste func ii să fie păstrate.1 Reutilizarea unui cod obiect În cazul creării unei func ii utile care se doreşte reutilizată.2 Lucrul cu fişiere bibliotecă Opera iile acceptate de fişierele bibliotecă sunt următoarele: crearea unei biblioteci. adăugarea unuia sau mai multor fişiere obiect la bibliotecă.c func ie.14. editorul de legături examinează fişierele LIB pentru a rezolva referin ele la func ii.obj După ce fişierul bibliotecă a fost creat. înlocuirea unui fişier obiect cu altul.obj). acest mod de a reutiliza codul unor func ii este destul de dificil de utilizat în cazul în care se doreşte reutilizarea unui număr mare de func ii aflate în fişiere obiect separate. listarea rutinelor pe care le con ine biblioteca. În continuare prezentăm opera iile ce pot fi realizate cu func iile bibliotecă utilizând programul TLIB al compilatorului Borland C. În func ie de compilator.

Func ia de biliotecă TLIB a compilatorului Borland C are următoarea sintaxă: tlib cale comandă.(elimină un modul din bibliotecă). în linia de comandă din sistemul de operare DOS trebuie scrisă următoarea instruc iune: C:\>SET INCLUDE=C:\BORLANDC\INCLUDE 271 . pentru a vedea dacă acel fişier există sau nu. Dacă la compilarea programului se afişează un mesaj de eroare. Când compilatorul întâlneşte o instruc iune #include în program. trebuie verificat subdirectorul care con ine fişierele antet. el compilează codul din fişierul antet ca şi cum ar fi scris în fişierul sursă. Dacă se găseşte fişierul respectiv. 14. comandă – este formată dintr-un simbol şi numele unui fişier obiect. .3 Fişierele antet Fiecare program foloseşte una sau mai multe instruc iuni #include pentru a cere compilatorului de C să folosească instruc iunile incluse într-un fişier antet. * (extrage un modul din bibliotecă într-un fişier cu acelaşi nume. Simbol poate fi unul din caracterele: + (adaugă un modul la bibliotecă). avertizând că nu se poate deschide un anumit fişier antet. fisier – reprezintă numele fişierul în care se scrie ieşirea opera iei efectuate asupra bibliotecii. fără al elimina). Fişierele antet con in defini ii frecvent utilizate şi furnizează compilatorului informa ii referitoare la func iile sale. -+ (înlocuieşte un modul din bibliotecă). fişier unde: - - cale – este un şir de caractere care specifică calea până la bilioteca asupra căreia se efectuează opera ia. -* (extrage şi elimină un modul din bibliotecă).

2001.. M. Mocanu M.. Osborne / McGraw Hill. 272 .. Editura Petrovici V.. Schild H. N. Muşatescu C. 3. 1993. Turturea D. Editura SITECH. 1993. 6. Negrescu L. Craiova.BIBLIOGRAFIE 1. Craiova.... Zaharia. Introducere în limbajul MicroInformatica. Ini iere în Turbo C++ şi Borland C.. Editura Teora. Goicea F.. Cluj Napoca.. Reprografia Universitã ii din Craiova. Programarea în limbajul C. Laşcu M..Tham C. Prentice Hall. 11. Holzner S. Brady Books.M. Indrumar de laborator. Learning to program in C. Iordache Şt.... 1995. 13. Englewood... Kernighan. 1993.W. Borland.D. Borland C++ Programming. Real-time software for control: program examples in C. 7. D.. B. C. Introducere în C++. Ed. Bulac. 8.. Cluj Napoca. 2.. Exemple în limbajele C şi C++. 5. New York. Tehnicã. 2002. Cliffs. Bãdicã C. 10. 1978. Ed.. Prentice Hall. 1993.J. Editura Tehnicã. Bucureşti. Ghid de programare în limbajele C/C++. 12. Marian Gh. 1988. 1990. Bucureşti. 1983 Auslander D. Editura ROM TPT. Bucureşti. Limbajul C. Somnea D. Limbajul PASCAL. 1992. 9. Plum T. Albastră. Structuri de date şi algoritmi. 1999. Ritchie. Prentice-Hall. Marian Gh. 4. Programarea orientatã pe obiecte. Using Turbo C. Pãdeanu L. C. The C programming languages...