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

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

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

Limbajul C++. În principiu. Conceptele programării orientate pe obiecte au influen at în mare măsură dezvoltarea limbajelor de programare în ultimul deceniu. Uneori s-au făcut mai multe extensii ale aceluiaşi limbaj.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ă. Limbajul C a fost dezvoltat şi el în această direc ie şi în anul 1980 a fost dat publicită ii limbajul C++. 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. lucru deosebit de important pentru sistemele de gestiune a bazelor de date. Limbajul C++ a fost implementat pe microcalculatoarele compatibile IBM PC în mai multe variante. . ele simplifică interac iunea dintre programe 2 . De obicei. La ora actuală. Cele mai importante implementări ale limbajelor C++ pe aceste calculatoare sunt cele realizate de firmele Microsoft şi Borland.. şi nu în ultimul rând binecunoscutul de acum limbaj Java. majoritatea limbajelor de programare moderne au fost dezvoltate în direc ia programării orientate pe obiecte. limbajul Avalon/C++ destinat calculului distribuit.portabilitatea programelor – ce permite utilizarea programelor scrise în C pe o mare varietate de calculatoare şi sisteme de operare. specializat în aplica ii Internet. ceea ce a condus la apari ia de limbaje care să permită utilizarea ei în scrierea programelor. ca şi limbajul C. elaborat de Bjarne Stroustrup de la AT&T. Prin anii ’80 interesul pentru programarea orientată pe obiecte a crescut. limbajul O ce încearcă să îmbine facilită ile de nivel înalt cu cele ale programării de sistem. multe limbaje au fost extinse astfel încât ele să admită conceptele mai importante ale programării orientate pe obiecte. . limbajul C++ are ca extensii limbajul E ce permite crearea şi gestiunea obiectelor persistente. Interfe ele utilizator au atins o mare dezvoltare datorită facilită ilor oferite de componentele hardware ale diferitelor calculatoare.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. De exemplu. 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.

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

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

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

supercomputer: Un computer extrem de rapid care poate executa sute de milioane de opera ii într-o secundă.acesta este dotat standard cu tastatură. înmul iri. Memoria secundară Echipament de intrare UNITATEA CENTRALĂ Echipament de ieşire Memoria principală Fig. Din punct de vedere fizic. mainframe: Un calculator multiutilizator capabil să lucreze simultan cu sute sau chiar mii de utilizatori. minicalculator (minicomputer): Un calculator multiutilizator (multi-user) capabil să lucreze simultan cu zeci sau chiar sute de utilizatori. monitor şi dispozitive periferice de stocare a datelor. 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). 1. Aceasta este asemănător unui PC dar are un microprocesor mai puternic şi un monitor de înaltă calitate (rezolu ie mai mare).2. scăderi. 1. împăr iri.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. Prima componentă realizează efectiv 6 . comparări de numere). mouse. 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.2 Arhitectura minimală a unui sistem de calcul sta ii de lucru (workstation): Un calculator monoutilizator de mare putere.

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

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. G (giga) se scriu de obicei cu litere mari şi reprezintă mii. 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 la apari ia primului prototip 4004. astfel: 1 KB=210B=1024 B 1 MB=210KB=1 048 576 B 1 GB=210MB=230 B Abrevierile K (kilo). astăzi. aşa cum suntem obişnui i în mod normal să lucrăm. 8 . 1. Nu se întrevede nici o dificultate care să frâneze această rată de dezvoltare". Într-un număr aniversar al publica iei Communications of the ACM. co-fondator al companiei Intel. 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. şi multiplii unui byte vor fi puteri ale lui 2. Deoarece reprezentarea numerelor în calculator se face în baza 2 şi nu în baza 10. 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. 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.2. care se măsoară prin numărul de tranzistori pe cip. 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). M (mega). Pentru citirea informa iilor nu se folosesc bi i în mod individual ci aceştia sunt grupa i într-o succesiune. s-a dublat aproape constant la fiecare 18 luni. un sistem desktop ce utilizează un cip Pentium Pro este evaluat la circa 7$ pe MIPS.2 Memoria Microprocesorul are capacitatea de a memora date care urmează a fi prelucrate. Gordon Moore. era optimist în privin a evolu iei PC-urilor şi a microprocesoarelor: "complexitatea microprocesoarelor. De aceea. miliarde. respectiv.guvernul SUA în perioada lansării primelor echipaje umane către Lună. un calculator necesită şi o memorie care să găzduiască date şi programe. cât şi rezultatele intermediare. milioane şi.

dă semne că lucrurile nu se vor opri aici. 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. Dacă primele procesoare puteau accesa doar 1 MB de memorie astăzi un procesor Pentium poate accesa peste 256 MB. iar opera iunea de înscriere cu programe se mai numeşte „arderea memoriilor”. Principala componentă a unui calculator utilizată pentru memorarea programelor o reprezintă hard discul. floppy disk-uri sau discuri flexibile. În memoria RAM informa ia este stocată temporar.5 inch cu capacitatea de 1. 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ă. Acesta poate fi asemănat cu o dischetă de mare capacitate. Primele discuri apărute pentru PC-uri. Aceste memorii pot fi doar citite (con inutul lor nu poate fi modificat). permiteau stocarea a maximum 160 KB de informa ie. 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. a apărut necesitatea existen ei unor memorii externe. Astăzi mai există doar dischete cu diametrul de 3. Dischetele folosesc metode magnetice de memorare a informa iei motiv pentru care ele se mai numesc şi suporturi magnetice de informa ie. care să fie solicitate la nevoie. 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. integrată într-o unitate 9 . din ce în ce mai rapidă. De exemplu. Înscrierea con inutului acestor memorii se face de către fabricant. 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. Memoria principală este formată din două tipuri de circuite: cip-uri ROM şi cip-uri RAM. Circuitele de tip ROM (Read Only Memory) au memorate programele care controlează ini ial calculatorul (sistemul de operare).Memoria unui calculator are două componente: memoria principală (internă) şi memoria secundară (externă). numite şi dischete.44 MB. 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.

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

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

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

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

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

Pentru sisteme mai mari. 15 . Anumite sisteme de operare permit sute sau chiar mii de utilizatori concuren i. 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). multiprocesor: Permit execu ia unui program pe mai mult de un microprocesor.Aplica ie Disk-drive Sistem de operare Mouse Monitor Tastaturã Imprimantã Fig. 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. În principal sistemul de operare asigură ca diferite programe şi diferi i utilizatori să nu interfereze unele cu altele. asigurând inaccesibilitatea persoanelor neautorizate la resursele sistemului.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. Ele ac ionează ca un gestionar al traficului de date şi al execu iei programelor. multitasking: Permit mai multor programe să ruleze în acelaşi timp (execu ie concurentă). 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. 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. Sistemul de operare este de asemenea responsabil cu securitatea. 1.

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

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

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

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

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

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

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

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. fals) sau cele două cifre binare1 şi 0. adevărat) şi F (false. prin abuz de limbaj. un număr binar are mai multe cifre binare.Capitolul II REPREZENTAREA DATELOR ÎN CALCULATOR Se ştie că un calculator numeric prelucrează numere binare. 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ă. 23 . Ca şi un număr zecimal. De aici rezultă necesitatea compatibilizării sau interfa ării între aceste două moduri de reprezentare a numerelor. Cum cele două sisteme de numera ie sunt ponderale. Sistemul de numera ie binar folosit pentru reprezentarea informa iei în calculatoare este un sistem de numera ie ponderal. pe când reprezentarea proprie maşinilor de calcul este cea binară. întocmai ca sistemul de numera ie zecimal. Acest lucru ine de suportul fizic de manipulare. că un calculator numeric prelucrează numere binare. transport şi stocare a datelor interne. Acestor două valori li se asociază natural două valori logice: T (true.

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

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

1.8.4. Dacă formatul ale se limitează la 4 cifre zecimale...1} b = 7 ⇒ B = {0. Spre exemplu: b = 2 ⇒ B = {0. b − 1} .5. De altfel şi operatorul uman face aceleaşi conven ii de reprezentare.2. b − 2.1.6...1.6} b = 10 ⇒ B = {0. Spre exemplu ştim că numărul 1 nu poate fi exact 3 reprezentat ca un număr zecimal.. Prin conven ie.7. atunci vom scrie 1 ≅ 0. atunci mul imea cifrelor zecimale nu mai este suficientă pentru reprezentarea numerelor în acea bază. Mul imea B reprezintă mul imea cifrelor sau simbolurilor de reprezentare. deci fixăm un format de reprezentare. Spre exemplu să considerăm baza b = 16 care va folosi 16 cifre hexazecimale (sau mai simplu hexa).î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. În general. un număr întreg în baza b se poate reprezenta cu un număr predeterminat de cifre ci ∈ B = {0..9} Noi suntem obişnui i să folosim mul imea cifrelor zecimale. Dacă totuşi se foloseşte o bază de reprezentare mai mare decât 10. 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. 2.3. fie în orice altă bază.3.5.2.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.4.1.2.2.

....Forma generală de reprezentare externă a numerelor întregi este de forma:  N b = ±c n −1c n − 2 .. 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 + . 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.2. 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... Adunarea numerelor naturale binare se face întocmai ca la cele în reprezentare în baza 10. 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ă. + c1 ⋅ b1 + c 0 ⋅ b 0 ) = ± ∑ c k ⋅ b k k =0 n −1 În continuare vom studia următoarele probleme: .. b − 2.. a) Conversia din baza 10 în baza 2 şi invers Fie de exemplu numărul zecimal 37.1. 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 ..c 2 c1c 0  c k ∈ B = {0..cum se face conversia unui număr din baza b = 10 în baza b=2 cum se face conversia inversă.

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

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

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

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

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

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

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

problema reprezentării numărului negativ a fost rezolvată cu ajutorul bitului de semn dar problema reprezentării punctului binar va avea altă rezolvare. pentru a furniza rezultate corecte. ob inându-se 01110011101. 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). se adaugă bitul de semn. adică valoarea -1024+99 = -925.2510 Pentru înmul irea numerelor reale rămân valabile considerentele de la numere întregi. la final. Reprezentarea în complement fa ă de 2 se poate folosi şi pentru numerele reale negative. care se va înmul i cu numărul (fără semn) 100101.01 → 10011 .112 = −2 4 + 21 + 2 0 + 2 −1 + 2 − 2 = −16 + 3 + 0. 2. valoarea corectă.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. Mai departe.25 poate avea reprezentarea: 12.012 → 01100 .11 10011 .01 01100 . Astfel.2510 = 1100 . sunt reprezenta i intern în complement fa ă de 2. Ca o concluzie.75 = −12. se va reprezenta în complement fa ă de 2.01 = 10011 . modulul numărului (-25). deci. bitul de semn fiind MSB de la partea întreagă. -12.10 + 0. ob inând. To i întregii cu semn. Numerele binare întregi fără semn au aceeaşi reprezentare atât externă cât şi internă. aşa cum am arătat mai sus. valoarea 1110011101. Numerele întregi cu semn (care în reprezentare externă sunt prefixate cu ± ) au ca reprezentare internă un bit de semn. care au MSB=1. ob inându-se 10001100010+1=[1]0001100011. dar care se tratează deosebit de ceilal i bi i ai reprezentării. În cazul de mai sus. Acest ultim număr se va complementa fa ă de 2. Mai departe se va lucra cu 25. 35 . 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. 0 pentru numere pozitive.Se analizează separat bi ii de semn şi se ia decizia că rezultatul va fi negativ.3.

25: 12. Cum evolu ia lor s-a făcut trecându-se succesiv prin multipli de 8 bi i.01 = 0. 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. modul de interpretare al bi ilor ce compun reprezentarea şi gama de 36 . 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. Denumire Dimensiune Nr. Acest tratament nediferen iat provine de la reprezentarea ştiin ifică uzuală cu mantisă şi exponent. în cazul nostru numărul pozitiv 4. În cele ce urmează. a fost naturală gruparea a 8 bi i într-o entitate numită byte. Tipul unei date determină modul în care procesorul stochează şi prelucrează data respectivă. Deci. s-au impus şi alte entită i de reprezentare a informa iei.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. spre exemplu. 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). vom prezenta tipurile de bază pe care le pot avea datele în reprezentarea internă. ajungându-se în prezent la procesoare pe 64 de bi i. Acest lucru se întâmplă şi în realitate. pe care le vom prezenta sintetic în tabelul de mai jos. 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.2510 = 1100. byte Nr. reprezentarea binară a numărului 12. Fie. 1B = 8b (adică un byte reprezintă 8 bi i) Procesoarele au evoluat.

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

127]  min = −27 = −128  Dacă se declară tipul unsigned char. mai simplu. atunci nu se mai consideră (interpretează) bitul de semn şi data se consideră întreagă pozitivă. 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). Întregul alfabet al limbajului C se regăseşte în mul imea primelor 128 de caractere ASCII.se divide octetul în două grupe de câte 4 bi i 38 . Trebuie men ionat faptul că reprezentarea datelor în format hexazecimal este foarte răspândită în tehnica programării calculatoarelor. 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: . Coloana D semnifică valoarea zecimală (decimal) a octetului. Cum mul imea cifrelor hexa con ine 16 simboluri (0…9 şi A…F). pentru codificarea celor 16 cifre avem nevoie de 4 cifre binare ( 2 4 = 16 ). pornind de la reprezentarea binară a numărului. 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.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. 255]  min = 0  Tabelele de mai sus con in codurile ASCII ale primelor 128 de caractere. Reprezentarea unui număr natural în format hexazecimal se realizează cu metoda împăr irii succesive la 16 sau.

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

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

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

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

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

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

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

875 Se observă cum câmpurile exponent şi significand sunt procesate separat. 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. avem exponent _ biasmax = 255 ⇒ exponent _ realmax = 255 − 127 = 128 nmax = 2128 = 28 ⋅ 210 = 256 ⋅ (1024)12 ≅ 256 ⋅ 1036 = 2..0111111 ⇒ 5.7 ⋅10 308 46 .25 × 1.5 = [(.510 = 1.10101 × 5.25 × 1. 1.11 × 2  3+1 5. calculată fără a aproxima ca mai sus: 210 = 1024 ≅ 1000 = 103 este n max = 3.10101) × (. Astfel. în final corelându-se forma de reprezentare internă.11 −−−−− 10101 10101 −−−−− . exponentul este decisiv pentru gama de reprezentare.12 = .0111111× 2 = 111.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.6 ⋅ 10307 ( ) Valoarea maximă exactă este n max = 1. La tipul float.25 = 101.5 = .111 = 7.11)]× 2  4 .01 = .56 ⋅ 1038 Valoarea maximă exactă.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.10101× 23 10 2   1 .

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

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

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

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

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

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

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

3. Caracterele whitespace sunt utilizate pentru a face programele mai lizibile. constante caracter şi comentarii. Comentariile sunt de asemenea tratate ca whitespace. sau ca şiruri de caractere.drept componente de constante. Caracter Nume Caracter Nume . : ? ’ ” ( ) [ ] { } > < 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.3. 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. Aceste caractere au o semnifica ie specială pentru compilatorul de C. Caractere speciale şi de punctua ie Caracterele speciale şi de punctua ie din mul imea caracterelor C sunt folosite pentru mai multe scopuri. 3.4. . . Caracterele de punctua ie din setul de caractere reprezentabile C care nu apar în acest tabel pot fi utilizate numai în şiruri. Ele sunt în mod tipic utilizate pentru a specifica 54 .3.

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

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

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

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

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

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

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

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

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

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

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

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

0 0 0 . 0 0 0 . 0 0 0 . 1. disp_binary(i). . 0 0 0 .t<8. 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. . . /* prototipul functiei disp_binary() */ void main() { int i = 1. . . 0 0 0 .h> void disp_binary(). 2. 2.t++) { disp_binary(i). . se poate realiza o func ie care să efectueze această opera ie. for (t=0.} Programul produce următoarea ieşire: 0 0 . 0 1 0 .} printf (" \n"). 0 0 1 . 1 1 0 . 0 0 0 .}} void disp_binary(int i) /* se defineste functia disp_binary() */ /* care afiseaza bitii dintr-un byte */ {register int t.t=t/2) if (i&t) printf("1"). 0 0 0 . 0 0 0 . t.t<8. printf("\n"). else printf("0").x x x x x << << << >> >> 1. for (t=0. 0 0 0 . 0 0 1 . .t>0.t++) { i=i>>1. 0 0 0 . for (t=128. 1 Deşi limbajul C nu con ine un operator de rotire. 3. . i=i<<1. 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 . 0 0 0 .

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

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

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

Se evaluează expresia Expr1. Dacă Expr1 este falsă. valoarea lui y va fi 100. 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. else y = 200. iar valoarea acesteia devine valoarea întregii expresii: Exemplu: x = 10.sizeof(short int pe %d octeti". Acelaşi program scris cu if /else va fi: x = 10.sizeof(float)).sizeof(long int)). se evaluează Expr2. sizeof(long În urma execu iei acestui program. printf("\nTip printf("\nTip printf("\nTip printf("\nTip printf("\nTip double)). Dacă x ar fi mai mic decât 9.sizeof(char)). y = x > 9 ? 100 : 200.8. float pe %d octeti". long int pe %d octeti". Dacă este adevărată. care devine valoarea întregii expresii.sizeof(int)).2. se evaluează Expr3. y va primi valoarea 200. 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.} caracter pe %d octet".void main(){ printf("\nTip printf("\nTip int)). double pe %d octeti".sizeof(double)). Expr2 şi Expr3 sunt expresii. short int pe %d octeti". long double pe %d octeti\n". 71 . if (x > 9) y = 100. Cum 10 > 9.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.addr_info[t]. 107 . de supraînscriere a fişierului disc cu datele din memorie. } 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(). i < SIZE.t++) { if (*addr_info[t]. else if (feof(fp)) { fclose (fp). Func ia load() realizează opera iunea inversă.addr_info[t]. save(). for (t=0. i <= SIZE.fp) !=1) printf (" File write error \n "). fclose (fp). În plus. 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.t<SIZE.sizeof(struct addr).street). enter(). return. if ((fp = fopen("maillist".name) if(fwrite(&addr_info[i].} for (i = 0.name!='\0') { printf("%s\n". return.} else printf ("File read error\n"). load(). dacă dorim să adăugăm date la fişierul maillist care con ine deja date introduse anterior. fp) == 1). printf("%s\n". i++) if(fread(&addr_info[i].} for (i = 0. 1. Structura acestor rutine este următoarea: /* Functia save() */ void save() { register int i.city).} /* Functia load() */ void load() { register int i. "wb")) == NULL) { printf (" Cannot open file\n ").addr_info[t]."rb")) == NULL) { printf("Cannot open file\n "). if ((fp = fopen("maillist".name). exit(). ordinea de lansare a comenzilor va fi: init_list().sizeof(struct addr).1. i++) if(*addr_info[i].datele stocate în fişierul maillist. Spre exemplu. printf("%s\n".

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

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

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

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

struct sir_lung ord_sir(). propunem următorul program: .şirul rezultat se sortează în ordine crescătoare. sir_init=cit_sir().sir_ord. getchar(). void tip_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.void f1(struct struct_tip functia f1() */ {printf ("%d\n".se preia de la tastatura un al doilea şir de numere întregi . Toate aceste func ii comunică prin intermediul unei variabile globale de tip structură şi a mai multor variabile locale de tip structură.sir_impar).se concatenează cele două şiruri . int lung.sir_impar. /* se defineste structura sir+lungime si variabila globala sir */ struct sir_lung { int sir[80]. 112 . sir_par=par_sir(sir_init).h> // definim prototipurile functiilor utilizate struct sir_lung cit_sir(). sir_init=concat_sir(sir_init. sir_ord=cit_sir(). Se vor construi func ii care să realizeze citirea şirurilor de la tastatură.} sir.a). struct sir_lung concat_sir(). } param) /* se declara Pentru exemplificare.sir_ord). scrierea lor pe display. Programul în C este prezentat în continuare: # include <stdio. // programul principal void main(){ struct sir_lung sir_init.se preia de la tastatura un prim şir de numere întregi . struct sir_lung par_sir(). sir_impar=impar_sir(sir_init). struct sir_lung impar_sir().sir_par. respectiv să le ordoneze şi să le sorteze după paritate. sir_impar=ord_sir(sir_impar). sir_par=ord_sir(sir_par). Pentru a realiza acest program.param. sir_ord=concat_sir(sir_par. 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.

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

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

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

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

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

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

/*count_addr indica spre count. Instruc iunea j = *p. // 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". loca iile de memorie fiind acum ca cele din figură: Adresa i j p 1200 1202 1204 Memoria 123 123 1200 119 . 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. val). copiază un întreg scurt de la loca ia 1200 în j. p = &i. /*Se va tipari numarul 100 */ Spre exemplu. adică adresa variabilei i. /* val preia valoarea de la adresa count_addr. adresa care nu are nici o legatura cu valoarea lui count */ val = count_addr. p = &i. să considerăm por iunea de program: short i.count_addr = &count. } Să presupunem că zona de stocare a celor trei variabile arată astfel: Adresa i j p i = 123. j. j = *p. Aceasta instructiune plaseaza in count_addr adresa din memorie a variabilei count.

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

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

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

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

/*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

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

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

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

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

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

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

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

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

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

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

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

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

Programul va avea următoarele facilită i: 1. Exploatarea unei liste simplu înlăn uite în memorie: 2. acestea se lansează în execu ie. prin tastarea ini ialelor comenzilor. Pentru o mai bună proiectare a programului. 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. 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ă. Programul este prevăzut cu o intefa ă simplă prin care utilizatorul poate alege dintre op iunile pe care le are la dispozi ie. Citirea de pe disc în memorie a bazei de date salvate anterior 5. Programul poate fi extins ulterior pentru structuri dinamice mai complexe.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. Vom descrie pe rând func iile care îndeplinesc sarcinile enumerate mai sus. 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.2. înregistrare cu înregistrare. 2. Afişarea pe ecran. Zona alocată prin intermediul func iei malloc() se poate elibera folosind func ia free(). Salvarea din memorie pe disc a unei liste simplu înlăn uite 4. a bazei de date con inute în lista dinamică. care să folosească liste dublu înlăn uite sau circulare.1. Interfa a este sub forma unui meniu din care. 141 . Ştergerea unor înregistrări a) Ştergerea primului nod al listei b) Ştergerea unui nod intern al listei c) Ştergerea ultimului nod al listei 3. Crearea unei liste simplu înlăn uite în memorie (pentru prima oară).

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

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

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

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

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

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

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

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

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

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

x). float x. else return (b).x). Exemplu: Aceasta func ie tipăreşte un şir în ordine inversă: # include <string. float b) { if (a > b) return (a). 8. ♦ return poate fi folosită pentru a întoarce o valoare. int x. } Se observă că nu mai este nevoie de declararea explicită a parametrilor formali de tip întreg a şi b. else return (b). void main() { x = max(3.4). void main() { x = max(-3.float max(). 152 . } Tot corect va rula şi programul: # include <stdio. } int max(a. 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.2.h> void afis_invers(char s[]). printf("max= %d\n". 4.b) { if (a > b) return (a). } float max(float a. 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.2.h> int max().1). void main() { char s[10]. printf("max= %d\n".

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

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

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

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

} func1() /* Se defineste functia func1() */ { int temp. variabilele globale vor păstra valorile lor pe durata execu iei complete a programului. declararea parametrilor func iei func() de mai sus se poate face şi sub forma: func (char *s. deci se stochează în memoria statică. evident în afara oricărei func ii. } 8. Aceşti parametri formali pot fi utiliza i ca orice altă variabilă locală. . func1(). . mai exact înaintea func iei main().3.4. Exemplu: int count. De exemplu. else printf("Caracterul NU se afla in sir\n"). char c) { . . Variabilele globale pot fi plasate în orice parte a programului. } Func ia func() are doi parametri: s şi c. /* count este global */ void main (void) { count = 100. . variabilele globale se plasează la începutul unui program. printf ("count is %d". // Se va afisa 100 } func2() /* se defineste functia func2() */ { int count. De asemenea. şi înainte de prima lor utilizare.count). c)) printf("Caracterul se afla in sir\n"). . .if (func(s. 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. . De obicei. func2(). variabilele globale sunt cunoscute întregului modul program şi pot fi utilizate de orice parte a programului. 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. /* count este local */ 157 . Aceasta func ie întoarce 1 dacă caracterul c apar ine şirului şi 0 dacă c nu apar ine şirului. /* temp preia variabila globala count */ temp = count. Variabilele globale se creează prin declararea lor în afara oricărei func ii (inclusiv main()). Variabile globale Spre deosebire de variabilele locale. . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

de către toate Permanentă. 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. numai (registru) când se execută func ia în care este declarată Extern Global. Segment de memorie text Con ine instruc iunile programului. Domeniul de vizibilitate este în general determinant şi în stabilirea duratei de via ă a variabilei. cunoscute în mod tradi ional ca segment de memorie text. 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ă. 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. segment de memorie statică (sau de date) şi segment de memorie dinamică (sau stivă). Clase de memorare (specificatori sau atribute) Din punct de vedere al execu iei programelor C. memoria computerului este organizată în trei zone.8. Din punctul de vedere al duratei de via ă a variabilei. cât timp este in memorie programul executabil Vizibilitatea precizează domeniul sau locul în care o variabilă este vizibilă. 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ă. aceasta poate fi temporară (există numai pe perioada în care func ia care o 176 . 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ă.

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

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

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

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

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

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

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

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

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

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

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

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

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

Modularizarea externă constă în divizarea unui program foarte complex în mai multe subprograme. Ceea ce merită să subliniem este faptul că. În figura de mai sus se prezintă un ecran al Microsoft Visual C++ din MSDN 6. Prezentarea exhaustivă a organizării acestui mediu de dezvoltare a aplica iilor C/C++ este un demers în afara prezentei lucrări. Un workspace cuprinde unul sau mai multe Projects (proiecte) dintre care numai unul este principal şi restul sunt subordonate (subprojects). 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ă. un fişier sursă mai mare se poate diviza în două sau mai multe fişiere sursă mai mici. Astfel. acest proiect con ine mai ales fişiere sursă şi fişiere de tip header. anume un workspace care include un singur project. se numesc module 190 . Evident. Fiecare proiect este compus la rândul său din mai multe fişiere. dacă există. Modulul principal este fişierul care con ine func ia principală main(). Aceste fişiere se numesc module. în cadrul cel mai întâlnit. 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). Celelalte fişiere sursă.

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

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

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

Programatorul poate să interpreteze datele după cum doreşte Prin urmare.Capitolul X INTRĂRI/IEŞIRI 10.stdio. Într-o altă 194 . ele asigură o bună portabilitate a programelor. acestea sunt identice. 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. de exemplu UNIX. intrarea standard respectiv ieşirea standard sunt în mod implicit reprezentate de terminalul de la care s-a lansat programul. fiind implementate într-o formă compatibilă pe toate sistemele de operare. tipurile şi macrodefini iile din "stdio. În C un fişier reprezintă o sursă sau o destina ie de date. Func ii de intrare şi ieşire .h Limbajul C nu dispune de instruc iuni de intrare/ieşire. Un fişier de tip text este o succesiune de linii. prin scrierea/citirea datelor se scriu/citesc un număr de octe i fără o interpretare specifică. 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" reprezintă aproape o treime din bibliotecă. deşi în anumite sisteme. neexistând structuri de date specifice care să se aplice acestor fişiere. care poate fi asociată cu un disc sau cu alte periferice. 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. Suporturile cele mai des utilizate sunt cele magnetice (floppy sau harddiscuri).1. Biblioteca acceptă fişiere de tip text şi binar. În C. fiecare linie având zero sau mai multe caractere terminate cu ' \n '. 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. Aceste elemente se numesc înregistrări. De asemenea. Prin fişier în elegem o mul ime ordonată de elemente păstrate pe diferite suporturi. În limbajul C toate fişierele sunt tratate ca o înşiruire de octe i. Func iile de intrare/ieşire.

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

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

Opera ii cu fişiere În acest subcapitol vom detalia principalele opera ii efectuate asupra unor fişiere. Pentru o mai corectă în elegere a acestor func ii le vom structura după nivelul la care se utilizează: inferior sau superior. dar multe dintre ele pot depinde de implementare. adică cel cu numărul de ordine 3. 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. Principalele func ii sunt grupate în tabelul de mai jos: 197 . 10. conexiunea este întreruptă prin închidere. tastatura şi porturile seriale şi paralele) sunt deschise în mod text. Deschiderea unui fişier întoarce un pointer la un obiect de tip FILE. 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. sistemul de operare păstrează un indicator de fişier care indică pozi ia curentă în fişier. care con ine toate datele necesare pentru controlul fişierului. 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. De exemplu. În momentul începerii execu iei unui program. opera iile indicate mai sus pot fi realizate printr-un set de func ii aflate în biblioteca standard I/O a limbajului. Î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. la deschiderea unui fişier pentru citire indicatorul de fişier va indica la începutul fişierului. În timpul lucrului cu fişierele. Aceste func ii realizează ac iuni similare sub diferite sisteme de operare.2. pozi ie la care se va face următoarea opera ie de scriere sau citire. interfe ele standard (cu ecranul.

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

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

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

int pmode] ). Spre exemplu.C“.. Deoarece calea se include între ghilimele.pentru citire/scriere Deschiderea unui fişier nu reuşeşte dacă unul dintre parametri este eronat. Nu are nici un efect dacă fişierul este deja existent.O_RDWR). este defini ia generală a func iei _open.C din directorul JOC de pe dscheta A se deschide în citire/scriere. caracterul ‘\’ se dublează.. În acest caz func ia _open returnează valoarea (-1).pentru scriere 2 . D. B pentru floppy-disk şi C. În func ie de opera ia dorită. int oflag [. putem folosi o comandă de deschidere de forma: int d. Fişierul se prelucrează în mod binar Fişierul este de tip text. Crează şi deschide un nou fişier pentru scriere. caz în care fişierul BIO. pentru hard-disk) nume_i – este un nume de subdirector. mod poate avea valorile: 0 . 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 . 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. int _open( const char *filename.litera – defineşte discul (în general A. 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

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

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

h> #include <stdlib. definite în STDIO.h> 209 . 0l. 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.deplasamentul se socoteşte de la sfârşitul fişierului. 2) permite să se facă pozi ionarea la sfârşitul fişierului. origine – are una din valorile 0 . Ea poate fi apelată prin: v = _lseek(df. Defini ia func iei este: long _lseek( int handle.deplasamentul se socoteşte din pozi ia curentă a capului de citire/scriere. Argumentul origin trebuie să fie una dintre următoarele constante. 0l. int origin ). Pozi ionarea la începutul fişierului se face prin apelul: v = _lseek(df. 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.h> #include <fcntl.pozi iona oriunde în fişierul respectiv O astfel de pozi ionare este posibilă pe hard-uri şi floppy-uri prin func ia _lseek. 1 . 2 . deplasament. Opera ia următoare realizată prin apelul func iei _read sau _write se va realiza din această pozi ie.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. Următoarea opera ie de citire/scriere se va efectua de la noua loca ie. 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. 0) Exemplu: #include <io.deplasamentul se socoteşte de la începutul fişierului. apelul: v = _lseek(df. În continuare se pot adăuga articole folosind func ia _write. 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. Spre exemplu. long offset.

/* Muta pointerul fisier cu 10 octeti */ _read( fh. else printf( "Pozitia ultima este = %ld\n".} Î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. fh = _open( "write. /* Pozitionare pe ultima pozitie: */ pos = _lseek( fh. /* Pozitionare la inceputul fisierului: */ pos = _lseek( fh. Func ia _unlink şterge de pe disc fişierul specificat prin filename. pos ). if( pos == -1L ) perror( "_lseek inceput nu a reusit!" ). SEEK_END ). else printf( "Pozitia curenta = %ld\n". _close( fh ).3. /* Gaseste pozitia curenta: */ pos = _lseek( fh.h> void main( void ) { int fh. Exemplu: 210 . pos ). if( pos == -1L ) perror( "_lseek sfarsit nu a reusit!" ). 10 ). Defini ia func iei este: int _unlink( const char *filename ). buffer. long pos. 0L.o".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ă. SEEK_CUR ). /* Pozitia pointerului fisier */ char buffer[10]. 0L. if( pos == -1L ) perror( "_lseek pozitia curenta nu a reusit!" ). else printf("Pozitia pentru inceputul fisierului = %ld\n". 0L. _O_RDONLY ). SEEK_SET ). pos ). spf este specificatorul de fişier folosit la deschidere a fişierului.#include <stdio.

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

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

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

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

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

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. date_fis(df. Func iile printf şi scanf sunt proiectate pentru a lucra implicit cu fişierele stdout respectiv stdin.4. 10.1. 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ă.void main() { int df.mod) unde: pf .nume). deci cu monitorul şi tastatura.4. Func ia fopen() Func ia fopen se apelează printr-o expresie de atribuire de forma: pf = fopen(spf.nume). 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ă. df=creare_fis(nume). dar care sunt de nivel superior. df=deschid_fis_cit(nume). char *mode). 10. Dacă intrările şi ieşirile pentru perifericele standard le putem executa în formatul dorit cu ajutorul func iilor specializate scanf şi printf. Forma generală de declarare a func iei fopen() este: FILE *fopen(char *filename.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. 216 .} Atragem aten ia asupra modului în care lucrează func iile de intrare/ieşire pentru stdin şi stdout fa ă de cele pentru disc. list_fis(df. Există func ii specializate pentru scrierea/citirea pe disc cu format.nume). 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(). Bufferul este alocat automat şi gestionat de func ii C specializate. inchid_fis(df.

După adăugare comanda MS-DOS TYPE va tipări toate datele con inute în fiier. Men ionăm că stdin. 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. ieşirea standard şi ieşirea standard pentru erori. 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ă. Singura deosebire constă în 217 . ca în "rb" sau "w+b" se indică un fişier binar. la fel ca şi restul fişierelor. Numele fişierului con ine cel mult FILENAME_MAX caractere. comanda MS-DOS TYPE tipăreşte datele până la markerul original EOF şi nu până la ultima dată adăugată. 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. 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. După ce s-a făcut o adăugare. 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.

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

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

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

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

char line[81]. 0). dar se recomandă folosirea lor în special asupra fişierelor binare. 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().} /* se citeste un caracter de la pozitia 235 */ Observa ie: L modifică constanta 235 la tipul long int. 235L. atunci pointerul indică începutul fişierului. Exemplu: /* Acest program deschide fisierul FSEEK. Este preferată folosirea simultană a func iilor fseek şi ftell pentru a opera asupra fişierelor text. si muta 222 . Func ia returnează valoarea curentă a pointerului fişier. if ((fp = fopen("test".pozi ie curentă. Func ia ftell() este definită astfel: long ftell( FILE *stream ).h> void main( void ) { FILE *stream.OUT pointerul in diverse locuri din fisier */ #include <stdio. } fseek(fp. Func ia returnează 0 dacă se execută cu succes şi o valoare nenulă în caz de eroare. acest offset nu reflectă întotdeauna exact numărul de octe i datorită transla iei CR-LF. Pozi ia este exprimată prin offsetul fa ă de începutul fiierului. În cazul fişierelor deschise în mod text. se sugerează utilizarea acesteia numai pentru fişiere binare. SEEK_END . Dacă nu s-a efectuat nici o opera ie de I/O de la deschiderea fişierului în mod APPEND (adăugare). 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. Transla iile CR-LF efectuate în mod text pot cauza func ionarea defectoasă a func iei fseek. return getc(fp). Nu se recomanda utilizarea func iei fseek() pentru fişiere text. 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.SEEK_CUR . exit (1). "rb")) == NULL) { printf ("Cannot open file\n").

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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. ”C:\\BORLANDC\\BGI”). int far unde: gd şi gm sunt pointeri ce au aceeaşi semnifica ie ca şi în cazul func iei detectgraph. 241 .int far *gm. detectgraph(&gd. 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). De exemplu dacă BGI este subdirector al directorului BORLANDC. Apelul func iei detectgraph trebuie să fie urmat de apelul func iei initgraph. Cele mai utilizate adaptoare sunt cele de tip EGA şi VGA. 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. initgraph(&gd. cale este pointer spre şirul de caractere care defineşte calea subdirectorului BGI care con ine driverele. …………………………… Doar după apelul func iei initgraph pot fi utilizate şi alte func ii de gestionare a ecranului în mod grafic.&gm.gm.&gm). initgraph(int far *gd.

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

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

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

y defineşte pozi ia de pe ecran unde se face afişarea. 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. void far outtextxy(int x. .în centru: CENTER_TEXT. . afişează caracterele începând cu pozi ia curentă de pe ecran. vert – defineşte încadrarea pe verticală. Dimensiunile în pixeli ale unui şir de caractere se pot determina utilizând func iile textheight şi textwidth: 245 . astfel: . unde şir este un pointer spre zona de memorie în care se păstrează caracterele de afişat..marginea inferioară: BOTTOM_TEXT.char far* şir) . unde şir este un pointer spre zona de memorie în care se păstrează caracterele de afişat. charsize – defineşte dimensiunea caracterului în pixeli. 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) . 80*80 b) void far settextjustify(int oriz. int vert) – defineşte cadrajul textului. astfel: .în dreapta: RIGHT_TEXT. x. . .în centru: CENTER_TEXT.în stânga: LEFT_TEXT. . astfel: Valoarea parametrului 1 2 3 ….de la stânga la dreapta: HORIZ_DIR. oriz – defineşte încadrarea pe orizontală.marginea superioară: TOP_TEXT. 10 Matricea utilizată pentru afişarea caracterului (în pixeli) 8*8 16*16 24*24 …….int y.de jos în sus: VERT_DIR.

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

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

y). unde (x.. int ycentru. int yi. void far ellipse(int xcentru. void far line(int xi. int yf) – trasează un segment de dreaptă între punctele de coordonate (xi.y+dy). unghiurile fiind exprimate în grade sexagesimale. semiaxa mare definită de parametrul semiaxamare iar semiaxa mică definită de parametrul semiaxamică. int ycentru. unsigned şablon. int dr. unde: stil – este un întreg din intervalul [0. int sus.int raza) – trasează un arc de cerc.y+dy). cu (xcentru.yi) şi (xf. int dy) – mută cursorul în dreptul pixelului de coordonate (x+dx. int xf.2. void far setlinestyle(int stil. int unghistart. void far drawpoly(int nr. int unghistart. nr+1. void far linerel(int dx.….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 unghifin.y) reprezintă coordonatele pixelului curent.ycentru). int grosime) – defineşte stilul utilizat pentru trasarea liniilor. void far rectangle(int st. void far circle(int xcentru.ordonata_i unde i are valorile 1. int far* tabpct) – trasează o linie polignală. int y) – trasează un segment de dreaptă între punctul curent şi punctul de coordonate (x. void far lineto(int x.void far moverel(int dx. int unghifin. int dy) – trasează un segment de dreaptă între punctul curent şi punctul de coordonate (x+dx. int raza) – trasează un cerc.int semiaxamare. int ycentru. void far arc(int xcentru.yf). int semiaxamică) – trasează un arc de elipsă cu centrul în punctul de coordonate (xcentru. unde (x.ycentru) coordonatele centrului şi raza raza acestuia.y) sunt coordonatele punctului curent. int jos) – trasează un dreptunghi definit de col urile diagonal opuse. 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 pieslice(int xcentru. int sus. 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 semiaxamare. grosime – defineşte lă imea liniei în pixeli. int profunzime. astfel: culoare – defineşte culoarea utilizată pentru haşurare. void far bar3d(int st. void far setfillstyle(int haşura. int sus.int culoare) – este utilizată pentru a defini o haşură a utilizatorului. int ycentru. int far* tabpct) – desenează un poligon colorat.int raza) – desenează un sector de cerc colorat. nu se trasează partea de sus a prismei. astfel: 249 . astfel: NORM_WIDTH – valoarea 1 pixel şi THICK_WIDTH – valoarea 3 pixeli. int jos) – are aceeaşi semnifica ie cu func ia rectangle însă dreptunghiul este colorat. int unghistart. void far fillellipse(int xcentru. int semiaxamică) – desenează o elipsă colorată. pentru ind=0. void far bar(int st. int ycentru. int dr.şablon – defineşte stilul liniei şi are sens doar când parametrul stil are valoarea 4. int jos. void far getlinesettingstype(struct linesettingstype far* linieinfo) – este utilizată pentru a determina stilul curent. int culoare) – defineşte modul de colorare al figurilor. int ind) – func ia desenează o prismă colorată pentru ind diferit de zero. void far fillpoly(int nr. int unghifin. int dr.

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

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

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

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

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

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

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

memoria conven ională a unui PC este formată din primul 1Mb de RAM. cu primii 640Kb de memorie conven ională. 14. Programele DOS rulează. 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. 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). Sistemul de operare DOS dispune de capacită i de gestionare a memoriei ce pot maximiza performan ele calculatorului. Pe atunci această memorie era mai mult decât suficientă.Capitolul XIV ELEMENTE DE PROGRAMARE AVANSATĂ 14. în mod obişnuit. Sistemul de operare Windows utilizează modelul de memorie virtuală pentru a gestiona memoria.1. În programare memoria constituie un factor important ce influen ează viteza de lucru a programelor.1 Gestionarea memoriei Un calculator poate avea trei tipuri de memorie: conven ională. Fiecare tip de memorie are diferite viteze de acces. ceea ce afectează performan a programelor. PC-ul utilizează restul de 384Kb de memorie (numită memorie rezervată sau memorie superioară) pentru memoria video a calculatorului. Însă. extinsă şi expandată. Astăzi. driverele de dispozitive.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ă). Structura memoriei conven ionale a unui calculator personal este următoarea: 257 .

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

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

atunci când programele transmit parametri către o func ie. C plasează aceşti parametri în stivă. spa iul de memorie ocupat de stivă diferă. 14. Pentru a permite accesul la mai mult de 1Mb de memorie.sys. Valoarea minimă a stivei este 4Kb. 14.1. mod de lucru în care datele unui program nu pot fi scrise peste datele altui program ce rulează simultan cu acesta.1. Stiva este numită astfel deoarece ultimele valori depuse sunt primele extrase. Pentru a utiliza însă memoria extinsă este necesară trecerea la modul protejat de lucru al procesorului.exit(1). Programatorii au numit memoria de peste 1Mb memorie extinsă. 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. fie func ia fmalloc pentru a aloca memorie far.2 Memoria expandată În cazul programelor mari o memorie de numai 1Mb este insuficientă. De exemplu. 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. trebuie încărcat un driver de dispozitiv pentru memoria extinsă. În func ie de modelul de memorie utilizat. #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). numite pagini în care se încarcă paginile logice ale programului. 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.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. Intel şi Microsoft au creat o specifica ie pentru memoria expandată.1. În 260 .4 Stiva Stiva este o regiune de memorie în cadrul căreia programele păstrează temporar datele pe durata execu iei. care în DOS este de obicei himem. 14. Pentru a accesa memoria extinsă. Când func ia îşi încheie execu ia aceştia sunt scoşi din stivă.

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

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

func ia returnează valoarea 0. care este 0 pentru A. Parametru buffer este un pointer la bufferul din care sunt citite sau în care sunt scrise datele. int track. 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 nr_sector. 3) servicii de tastatură din BIOS Pentru accesul la serviciile de tastatură din BIOS. int head. sector şi nr_sector precizează sectoarele fizice ale disculuice trebie scris sau citit. şi aşa mai departe. 7 – dispozitivul nu este ocupat. valoarea returnată precizează eroarea. 1 pentru B. Parametrii head. 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. int sector. Dacă apare o eroare. track. void *buffer) unde parametrul unitate precizează numărul unită ii. C-ul pune la dispozi ie func ia _bios_keybrd ce are următoarea sintaxă: unsigned _bios_keybrd(unsigned comanda) 263 .6 – confirmare dispozitiv. int unitate.

Dacă valoarea returnată este 0xFFFF. inclusiv tastele speciale. Dacă valoarea returnată este 0xFFFF. Pentru aceasta se utilizează func ia _bios_equiplist care are următoarea sintaxă: 264 . cum ar fi tastele cu săge i Returnează starea tastelor de control. 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. înseamnă că nici o intrare de la tastatură nu este prezentă.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. înseamnă că nici o intrare de la tastatură nu este prezentă. Dacă func ia returnează 0. cum ar fi tastele cu săge i Determină dacă este prezent un caracter la bufferul tastaturii.

int port. bitul are valoarea 0 dacă există DMA şi 1 dacă nu există. 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). 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). 10-48Kb. 5:4 – modul video: 00-neutilizat. 13 – imprimanta serială. 01-32Kb. 10-mod video 80x25 color. 7:6 – numărul drieverelor de disc. 1 lui COM2 şi aşa mai departe. 1 – prezen a coprocesorului matematic. 12 – adaptorul de jocuri. Parametrul octet specifică fie octetul pentru ieşire. 8 – prezen a DMA (Direct Memory Acces). 3:2 – dimensiunea memorie RAM: 00-16Kb. unde 0 corespunde lui COM1. fie valorile de comunicare dorite. 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. 1164Kb. 11-mod video 80x25 mono. 11:10:9 – numărul de porturi seriale COM (de la 0 la 7). 01-mod video 40x25 mono.unsigned _bios_equiplist(void). Valoarea returnată de această func ie nu cuprinde memoria extinsă. 7) citirea cronometrului BIOS 265 . 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.char octet). 0 – prezen a unită ii de disc flexibile. expandată sau superioară.

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

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

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

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

editorul de legături examinează fişierele LIB pentru a rezolva referin ele la func ii. 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. Când sunt create func ii utile ce sunt necesare şi în alte programe.lib + functie. 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. adăugarea unuia sau mai multor fişiere obiect la bibliotecă.lib care să con ină acest fişier obiect se realizează cu următoarea linie de comandă: C:\>tlib biblioteca.obj ce con ine o serie de func ii pe care dorim să le păstrăm într-o bibliotecă. Crearea unei bilioteci biblioteca. înlocuirea unui fişier obiect cu altul. Presupunem că în urma compilării am creat fişierul obiect func ie.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.obj). 270 .3. ştergerea unuia sau mai multor fişiere obiect din bibliotecă. Atunci când este compilat şi link-editat un program. În func ie de compilator. Aceste fişiere con in biblioteci obiect.c func ie.14. se poate compila fişierul ce con ine func ia respectivă pentru a crea codul obiect (de exemplu din fişierul func ie.3. numele programului de bibliotecă şi op iunile liniei de comandă pe care programul le acceptă vor diferi. Func ia definită în acest fişier obiect poate fi reutilizată în alt program utilizând următoarea instruc iune: C:\>bc fisier_nou.3 Bibliotecile C Dacă se examinează fişierele ce înso esc un compilator C. 14.obj După ce fişierul bibliotecă a fost creat. 14.obj Totuşi. func iile acestuia sunt disponibile pentru compilarea şi legarea noilor programe. se pot construi biblioteci în care aceste func ii să fie păstrate.c prin compilare se ob ine fişierul obiect func ie. se remarcă multe fişiere cu extensia LIB.

el compilează codul din fişierul antet ca şi cum ar fi scris în fişierul sursă. * (extrage un modul din bibliotecă într-un fişier cu acelaşi nume. fără al elimina). Când compilatorul întâlneşte o instruc iune #include în program. -* (extrage şi elimină un modul din bibliotecă). -+ (înlocuieşte un modul din bibliotecă). Fişierele antet con in defini ii frecvent utilizate şi furnizează compilatorului informa ii referitoare la func iile sale. trebuie verificat subdirectorul care con ine fişierele antet. fişier unde: - - cale – este un şir de caractere care specifică calea până la bilioteca asupra căreia se efectuează opera ia. 14.Func ia de biliotecă TLIB a compilatorului Borland C are următoarea sintaxă: tlib cale comandă.(elimină un modul din bibliotecă). Dacă se găseşte fişierul respectiv.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. Dacă la compilarea programului se afişează un mesaj de eroare. 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. comandă – este formată dintr-un simbol şi numele unui fişier obiect. î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. . Simbol poate fi unul din caracterele: + (adaugă un modul la bibliotecă).

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