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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

F iş ie r u 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 r u l s u r s ã S e o b in e fiş ie r u l c o d o b ie c t : n u m e .6 Detalierea procesului de generare a unui executabil 22 .c ( lim b a j C ) n u m e . asamblorul face practic o convertire biunivocă între mnemonicele limbajului de asamblare şi codurile binare corespunzătoare acestor mnemonice (instruc iuni). e x e Fig.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 r e ) 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 . 1. I n s tr u c iu n ile în lim b a ju l d e n iv e l în a lt s e in tr o d u c d e la ta s ta tu r ã .e x e 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 .o b j S e s a lv e a zã p e h a r d d is k fiş ie r u l n u m e .e x e L a n s a r e a în e x e c u ie d e c ã tr e s is te m u l d e o p e r a r e a e x e c u ta b ilu lu i n u m e .b a s ( lim b a j B A S I C ) .o b j L in k .p a s ( lim b a j P a s c a l) n u m e . e t c . 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 t e x t e ( 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 .complexe decât cele de nivel înalt.c p p ( lim b a j C + + ) n u m e .

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

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

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

3..1} b = 7 ⇒ B = {0.3..8.4.1. Spre exemplu ştim că numărul 1 nu poate fi exact 3 reprezentat ca un număr zecimal.2. atunci vom scrie 1 ≅ 0. b − 1} . În general.1.6. Dacă formatul ale se limitează la 4 cifre zecimale. 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. Spre exemplu: b = 2 ⇒ B = {0.2. atunci mul imea cifrelor zecimale nu mai este suficientă pentru reprezentarea numerelor în acea bază. Prin conven ie. Dacă totuşi se foloseşte o bază de reprezentare mai mare decât 10. fie în orice altă bază.4.. un număr întreg în baza b se poate reprezenta cu un număr predeterminat de cifre ci ∈ B = {0.5. De altfel şi operatorul uman face aceleaşi conven ii de reprezentare.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.1. Reprezentarea externă a numerelor întregi Numerele naturale se pot reprezenta fie în baza de numera ie 10.2. 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 . 2.7.5.6} b = 10 ⇒ B = {0.9} Noi suntem obişnui i să folosim mul imea cifrelor zecimale.. b − 2.2.1. Mul imea B reprezintă mul imea cifrelor sau simbolurilor de reprezentare.

. + c1 ⋅ b1 + c 0 ⋅ b 0 = ± - ( ) ∑ ck ⋅ b k k =0 n −1 În continuare vom studia următoarele probleme: cum se face conversia unui număr din baza b = 10 în baza b=2 cum se face conversia inversă.2.. după regula: 0+0=0 27 ... 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...c 2 c1c 0  c k ∈ B = {0.....Forma generală de reprezentare externă a numerelor întregi este de forma:  N b = ±c n −1c n − 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. 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. a) Conversia din baza 10 în baza 2 şi invers Fie de exemplu numărul zecimal 37. Reprezentarea sa binară va fi ob inută astfel: 3710 = 1001012 37 36 1 2 18 18 0 2 9 2 8 4 2 1 4 2 2 0 2 1 0 Conversia inversă.. b − 2...1.

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

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

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

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

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

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

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

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

modul de interpretare al bi ilor ce compun reprezentarea şi gama de 36 . spre exemplu. s-au impus şi alte entită i de reprezentare a informa iei. În cele ce urmează. Acest lucru se întâmplă şi în realitate. 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.2510 = 1100. biti 8b 16 b 32 b 64 b 80 b octet cuvânt Cuvânt dublu Cuvânt cvadruplu B W DW QW TW 1B 2B 4B 8B 10B A determina reprezentarea internă înseamnă să determinăm lungimea reprezentării (de obicei în multipli de octe i).25: 12. byte Byte Word Double_Words Quad_Words Ten_Words Nr.Numerele reale se pot reprezenta identic cu cele întregi cu semn. Tipul unei date determină modul în care procesorul stochează şi prelucrează data respectivă. reprezentarea binară a numărului 12.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. a fost naturală gruparea a 8 bi i într-o entitate numită byte. 1B = 8b (adică un byte reprezintă 8 bi i) Procesoarele au evoluat.01 = 0. vom prezenta tipurile de bază pe care le pot avea datele în reprezentarea internă. Cum evolu ia lor s-a făcut trecându-se succesiv prin multipli de 8 bi i. ajungându-se în prezent la procesoare pe 64 de bi i. Deci. Fie. 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. în cazul nostru numărul pozitiv 4. Acest tratament nediferen iat provine de la reprezentarea ştiin ifică uzuală cu mantisă şi exponent. Denumire Dimensiune Denumire echivalentă Nota ie Nr. pe care le vom prezenta sintetic în tabelul de mai jos. 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.

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

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

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

2 Tipul int Acest tip se foloseşte pentru reprezentarea numerelor întregi cu sau fără semn. 2 31 = 2 ⋅ 2 30 = 2 ⋅ 210 ≅ 2 ⋅ 10 3 ≅ 2 ⋅ 10 9   min = − 2 31  ( ) ( ) 40 . Reprezentarea pe 4 octe i duce la posibilitatea măririi gamei de reprezentare astfel:  max = 2 31 − 1 3 3  . MSB 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.3. s-a trecut la modul de reprezentare a întregilor impus de noul procesor Intel 80386 dotat şi cu coprocesorul matematic Intel 80387.D 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 H 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f Sym D @ 80 A 81 B 82 C 83 D 84 E 85 F 86 G 87 H 88 I 89 J 90 K 91 L 92 M 93 N 94 O 95 H 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f Sym P Q R S T U V W X Y Z [ \ ] ^ _ D 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 H 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f Sym ` a b c d e f g h i j k L M n o D 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 H 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f Sym p q r s t u v w x y z { | } ~ ⌂ 2.3. Odată cu standardizarea ANSI C din 1989.

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

long double. double word) . cu reprezentare pe 8 octe i (64 bi i.exponent (exponent) Folosind formatul specific I80386. cu reprezentare pe 10 octe i (80 bi i.float . ten word) M SB b3 1 b3 0 b0 LSB S 31 30 S Exponent biased 23 22 Exponent = 8b Bias = 7FH=127 Significand 0 Significand = 23b float 63 62 52 51 Exponent = 11b S Bias = 3FFH=1023 79 78 S 64 63 0 Significand = 52b double 0 Significand = 52b long double Exponent = 15b Bias = 3FFFH=16383 Tipurile float şi double sunt formate pentru numere reale ce există numai în memorie. cu reprezentare pe 4 octe i (32 bi i. quad word) .Se observă cum stocarea în calculator a unei date floating-point necesită trei păr i: .bitul de semn (sign) . în limbajul C se disting trei tipuri de date reale: . Când un astfel de număr este încărcat de procesor în stiva pentru numere reale (flotante) pentru prelucrare sau 42 .mantisa. frac ia (significand) .double.

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

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

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

exponentul este decisiv pentru gama de reprezentare.7 ⋅10 308 46 .6 ⋅ 10307 ( ) Valoarea maximă exactă este n max = 1.0111111 ⇒ 5.5 = .111 = 7.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. calculată fără a aproxima ca mai sus: 210 = 1024 ≅ 1000 = 103 este n max = 3. Astfel.5.11 × 2  3+1 5.875 Se observă cum câmpurile exponent şi significand sunt procesate separat.12 = .0111111× 24 = 111.56 ⋅1038 Valoarea maximă exactă.510 = 1.4028 ⋅ 10 38 exponent_ biasmin = 0 ⇒ exponent_ realmin = 0 − 127 = −127 −12 −12 −13 nmin = 2−127 = 2−7 ⋅ 210 = 23 ⋅ 2−10 ⋅ 210 = 8 ⋅ 210 ≅ 8 ⋅ 10−39 ( )12 ( ) ( ) ( ) Valoarea pozitivă minimă exactă este n min = 5. 1.11 −−−−− 10101 10101 −−−−− . în final corelându-se forma de reprezentare internă.25 × 1.5 = [(. avem exponent _ biasmax = 255 ⇒ exponent _ realmax = 255 − 127 = 128 nmax = 2128 = 28 ⋅ 210 = 256 ⋅ (1024)12 ≅ 256 ⋅1036 = 2. 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.10101 × .25 × 1.11)]× 2  .10101× 23   1 .10101) × (.2510 = 101.012 = . La tipul float.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

. switch (choice) { case 'c' : create_list(). break. break. char state[10]. case 'd' : display_list(). char street[30]. case 'q' : exit(0).h> # include <stdlib.}}} Fişierul header local. break. break.h> typedef struct addr { char name[20].prima înregistrare .înregistrare intermediară Exemplu: Programul principal bd_main. case 'l' : loadf_list(). case 'e' : erase(). break.c # include "local. case 'i' : insert(). break. char city[15].h> # include <string. break.h" void main() { char choice.ultima înregistrare . 142 .Bază de date cu listă simplu înlăn uită Afişare pe ecran a înregistrărilor din baza de date Interfa a cu utilizatorul Comenzi pentru citire/scriere pe disc Comenzi pentru procesarea listei dinamice Citire de pe disc în lista Salvarea pe disc a listei Inserare dinamică dinamice din memorie Ştergere . case 's' : savef_list().) { choice = menu(). for (.h> # include <ctype.h este: # include <stdio.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

initgraph(&gd.&gm). int far unde: gd şi gm sunt pointeri ce au aceeaşi semnifica ie ca şi în cazul func iei detectgraph. cale este pointer spre şirul de caractere care defineşte calea subdirectorului BGI care con ine driverele. detectgraph(&gd.Zona spre care pointează gm memorează una din valorile: Adaptor Constantă simbolică Valoare Rezolu ie CGA EGA VGA CGAC0 – 0 CGAC1 – 1 CGAC2 – 2 CGAC3 – 3 CGAHI – 4 EGALO – 0 EGAHI – 1 VGALO – 0 VGAMED – 1 VGAHI – 2 320*200 320*200 320*200 320*200 640*200 640*200 640*350 640*200 640*350 640*480 Modul grafic se defineşte în aşa fel încât el să fie cel mai performant pentru adaptorul grafic curent. ”C:\\BORLANDC\\BGI”). 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. 241 . Apelul func iei detectgraph trebuie să fie urmat de apelul func iei initgraph. initgraph(int far *gd.gm. 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. Cele mai utilizate adaptoare sunt cele de tip EGA şi VGA.&gm. …………………………… Doar după apelul func iei initgraph pot fi utilizate şi alte func ii de gestionare a ecranului în mod grafic.int far *gm.

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

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

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

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

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

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

int ycentru. int dy) – mută cursorul în dreptul pixelului de coordonate (x+dx. int grosime) – defineşte stilul utilizat pentru trasarea liniilor.4] care defineşte stilul liniei conform următorului tabel: Constantă simbolică SOLID_LINE DOTTED_LINE CENTER_LINE Valoare 0 1 2 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 DASHED_LINE USERBIT_LINE 3 4 248 .y+dy). int xf. int unghistart.y).ycentru) coordonatele centrului şi raza raza acestuia. int unghifin. int y) – trasează un segment de dreaptă între punctul curent şi punctul de coordonate (x.int semiaxamare. int sus. void far lineto(int x.y) reprezintă coordonatele pixelului curent.void far moverel(int dx. unde (x. semiaxa mare definită de parametrul semiaxamare iar semiaxa mică definită de parametrul semiaxamică.yi) şi (xf. void far rectangle(int st. void far linerel(int dx. void far line(int xi. 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. int dy) – trasează un segment de dreaptă între punctul curent şi punctul de coordonate (x+dx. int yi. int raza) – trasează un cerc. cu (xcentru. void far ellipse(int xcentru.ycentru).y) sunt coordonatele punctului curent. void far arc(int xcentru. void far drawpoly(int nr.2. unde: stil – este un întreg din intervalul [0.int raza) – trasează un arc de cerc. int unghistart. int semiaxamică) – trasează un arc de elipsă cu centrul în punctul de coordonate (xcentru.yf). void far circle(int xcentru. void far setlinestyle(int stil. int unghifin. int dr. unsigned şablon..ordonata_i unde i are valorile 1.….y+dy). int yf) – trasează un segment de dreaptă între punctele de coordonate (xi. int ycentru. unghiurile fiind exprimate în grade sexagesimale. nr+1. int ycentru. int far* tabpct) – trasează o linie polignală. unde (x. int jos) – trasează un dreptunghi definit de col urile diagonal opuse.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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