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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

un programator scrie declara ii într-un limbaj precum Pascal. gramatical. într-o primă etapă. fiecare instruc iune a microprocesorului fiind codificată de către constructor. un program executabil (executable program aflat pe disc cu extensia . 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. Acest cod maşină poate fi executat secven ial. care translatează instruc iunile de nivel înalt într-o serie de instruc iuni cod obiect. În urma acestei prelucrări se ob ine codul maşină. lansează un analizor sintactic. O instruc iune de nivel înalt se translatează într-una sau mai multe instruc iuni specifice microprocesorului pentru care a fost conceput compilatorul. tuturor adreselor simbolice existente în codul obiect. Aceste instruc iuni ale microprocesorului sunt înlocuite cu codurile lor binare. un compilator şi un editor de legături folosite pentru translatarea codului sursă în cod maşină. C sau MATLAB folosind un editor. numit în mod tradi ional cod obiect. Acest linker combină diferitele module (le leagă) şi dă valori reale. de către microprocesor. Cu alte cuvinte. efective.exe. În mod tipic. după ce compilatorul a produs codul obiect. 19 . este prelucrarea codului obiect de către un editor de legături (link-editor sau linker). Când este lansat în execu ie compilatorul acesta. numit parser. toate instruc iunile scrise în limbajul de nivel înalt. Se creează astfel un fişier numit fişier cod sursă ce con ine o colec ie de instruc iuni şi declara ii scrise în limbajul respectiv.La ora actuală un limbaj de programare este inclus într-un mediu de programare mai complex care include un editor de texte pentru introducerea instruc iunilor în limbajul de programare de nivel înalt. în ordinea în care au fost introduse. Codurile binare ale instruc iunilor microprocesorului împreună cu reprezentările interne ale datelor manipulate formează codul obiect. Acesta parcurge şi analizează sintactic. Este foarte important ca referiri la alte module de cod să fie corect reprezentate în acest cod obiect. Primul pas este prelucrarea codului sursă de către compilator. Pasul final în producerea programului executabil. instruc iune cu instruc iune. salvat într-un fişier cu extensia .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 urma lansării în execu ie a compilatorului. salvat pe disc sub numele nume.5 Procesul de elaborare a unui program executabil Procesul de ob inere a unui executabil este prezentat în figura de mai jos.obj în urma lansării în execu ie a editorului de legături. compilatorul (compiler) şi editorul de legături (linker). Blocurile tridimensionale reprezintă entită ile principale ale mediului de programare: editorul de texte.obj şi se leagă cu toate modulele necesare (inclusiv func ii de bibliotecă sau alte module externe).exe la care adresele nu mai sunt simbolice ci absolute relativ la adresa de început a programului. se preia fişierul cod obiect nume. semnalizându-se toate erorile fatale pentru program sau avertismente utile programatorului în procesul de depanare. La lansarea în execu ie a programului fluxul de informa ie este complet controlat de către microprocesor. În cazul în care compilarea se efectuează cu succes. Numai anumite limbaje 20 . ob inându-se un program executabil (cod maşină) cu numele nume.Fig. 1.c care se va salva pe disc. Dacă folosim limbajul de programare C spre exemplu. 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. 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”. acesta preia fişierul sursă şi îl prelucrează corespunzător. se ob ine un fişier cod obiect. se ob ine fişierul nume.

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

c p p ( lim b a j C + + ) n u m e .complexe decât cele de nivel înalt. e t c .e x e Fig. 1.o b j S e s a lv e a zã p e h a rd d is k fiş ie r u l n u m e .e d it a re ( le g a r e a tu tu r o r m o d u le lo r n e c e s a re ) S e o b in e fiş ie r u l c o d m a ş in ã ( e x e c u t a b il): n u m e .p a s ( lim b a j P a s c a l) n u m e .e x e S e s a lv e a zã p e h a r d d is k fiş ie r u l n u m e .c ( lim b a j C ) n u m e .o b j L in k . I n s tru c iu n ile în lim b a ju l d e n iv e l în a lt s e in tr o d u c d e la ta s ta tu r ã .e x e L a n s a r e a în e x e c u ie d e c ã tr e s is te m u l d e o p e r a r e a e x e c u ta b ilu lu i n u m e .6 Detalierea procesului de generare a unui executabil 22 . F iş ie ru l s u r s ã c u n u m e le “n u m e ” ş i e x te n s ia c o r e s p u n zã to a r e s e s a lv e a zã d in m e m o r ia R A M p e h a r d d is k C o m p ila t o r S e c o m p ile a zã fiş ie ru l s u r s ã S e o b in e fiş ie ru l c o d o b ie c t : n u m e . T o t c e s e in tr o d u c e d e la ta s ta tu r ã e s te v izib il p e m o n ito r E d it o r d e te x te ( e v e n tu a l in c o r p o r a t în m e d iu ) F iş ie r te x t ( f iş ie r s u rs ã ) c u e x te n s ia a d e c v a tã : n u m e .b a s ( lim b a j B A S I C ) . asamblorul face practic o convertire biunivocă între mnemonicele limbajului de asamblare şi codurile binare corespunzătoare acestor mnemonice (instruc iuni).

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

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

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

1.1.. cele 16 cifre hexazecimale vor fi: Cifra Simbol Cifra Simbol 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 8 9 A B C D E F 26 .2. Prin conven ie.2.4. Dacă formatul ale se limitează la 4 cifre zecimale. un număr întreg în baza b se poate reprezenta cu un număr predeterminat de cifre ci ∈ B = {0. Dacă totuşi se foloseşte o bază de reprezentare mai mare decât 10.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. b − 2..1} b = 7 ⇒ B = {0.4.9} Noi suntem obişnui i să folosim mul imea cifrelor zecimale. b − 1} .8..î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. De altfel şi operatorul uman face aceleaşi conven ii de reprezentare. Reprezentarea externă a numerelor întregi Numerele naturale se pot reprezenta fie în baza de numera ie 10.3..3.. Mul imea B reprezintă mul imea cifrelor sau simbolurilor de reprezentare. Spre exemplu să considerăm baza b = 16 care va folosi 16 cifre hexazecimale (sau mai simplu hexa).2. atunci mul imea cifrelor zecimale nu mai este suficientă pentru reprezentarea numerelor în acea bază. În general. Spre exemplu: b = 2 ⇒ B = {0.6. 2.6} b = 10 ⇒ B = {0.7.1. fie în orice altă bază. Spre exemplu ştim că numărul 1 nu poate fi exact 3 reprezentat ca un număr zecimal.5..1.5.2. deci fixăm un format de reprezentare. atunci vom scrie 1 ≅ 0.

cum se face conversia unui număr din baza b = 10 în baza b=2 cum se face conversia inversă. după regula: 0+0=0 27 .2.. b − 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... 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 + . + c1 ⋅ b1 + c 0 ⋅ b 0 ) = ± ∑ c k ⋅ b k k =0 n −1 În continuare vom studia următoarele probleme: .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 ..1. 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.... 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.. 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ă..

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

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

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

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

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

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

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

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

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

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

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

60 61 . dacă un număr are o reprezentare internă pe un număr de k octe i. atunci circuitele corespunzătoare transmit spre calculator semnale binare corespunzătoare codului 1010 0001. Sistemul de calcul manevrează codurile ASCII corespunzătoare literelor şi cifrelor pe care utilizatorul le poate interpreta. să presupunem că avem numărul 217. Cu alte cuvinte.10012 = D916 = 13 ⋅161 + 9 ⋅16 0 = 208 + 9 = 217 - În acest mod. 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 înlocuieşte fiecare grup de 4 bi i cu cifra hexazecimală pe care o codifică. se poate reprezenta simplu cu ajutorul a 2k cifre hexazecimale. D 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 H 0 1 2 3 4 5 6 7 8 9 a b c d e f Sym Null ☺ ☻ ♥ ♦ ♣ ♠ LF ♂ ♀ CR ♫ ☼ D 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 H 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f Sym ► ◄ ↕ ‼ ¶ § ▬ ↨ ↑ ↓ → ← ∟ ↔ ▲ ▼ D 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 H 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f Sym D 48 ! 49 " 50 # 51 $ 52 % 53 & 54 ' 55 ( 56 ) 57 * 58 + 59 . 62 / 63 H 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f Sym 0 1 2 3 4 5 6 7 8 9 : . De exemplu. adică 61H sau 97 în zecimal. În tabelele de mai jos se prezintă codificarea ASCII a caracterelor. dacă la tastatură se tastează simbolul “a”. Codurile corespunzătoare simbolurilor alfanumerice din tabel sunt exact semnalele binare care se transmit în reprezentarea internă. < = > ? 39 . 21710 = 110110012 = 1101.

3. Reprezentarea pe 4 octe i duce la posibilitatea măririi gamei de reprezentare astfel:  max = 2 31 − 1 3 3  . M SB S b30 O ctetul 1 O ctetul 2 O ctetul 3 b0 LS B O ctetul 4 Tipul int este identic cu signed int şi utilizează o reprezentare pe 4B a numerelor întregi cu semn. s-a trecut la modul de reprezentare a întregilor impus de noul procesor Intel 80386 dotat şi cu coprocesorul matematic Intel 80387. Odată cu standardizarea ANSI C din 1989.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.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  31  min = − 2  ( ) ( ) 40 .3.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Denum. Tipul Variab. variabilei

int x int x int x p q

globală locală locală pointer local pointer local

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

Acelaşi lucru se face pentru celelalte două variabile locale. Din interpretarea rezultatelor de mai sus putem trage următoarele concluzii. Spre exemplu,
printf(“%d”,x); este echivalentă cu printf(“%d”,*p); scanf(“%d”,&x); este echivalentă cu scanf(“%d”,p);

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

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

160

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

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

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

din

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

161

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

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

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

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

162

Un apel combinat, valoare-referin ă este prezentat în exemplul următor:
# include <stdio.h> void f(); void main (void) { int x = 1, y = 1; printf("x = %d, y = %d \n", x, y); f(x,&y);} void f(int val, int *ref) { val++; (*ref)++; printf("x = %d, y = %d \n",val,*ref); }

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

}

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

163

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

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

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

164

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

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

Rezultatul rulării programului va fi:
abcdefghijklmnoprstuvxyzw ABCDEFGHIJKLMNOPRSTUVXYZW ABCDEFGHIJKLMNOPRSTUVXYZW

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

165

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

202

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

203

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

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

Prin execu ia acestui program se vor ob ine următoarele mesaje pe display:
open failed on input file: No such file or directory Open succeeded on output file Press any key to continue

10.3.2. Scrierea într-un fişier
Scrierea într-un fişier se realizează folosind func ia _write. Se presupune că fişierul respectiv a fost în prealabil deschis prin func iile _open sau _creat. Ea este asemănătoare cu func ia _read, doar că se realizează transferul de date în sens invers şi anume din memorie pe suportul fiierului. Func ia _write, ca şi _read, se apelează printr-o atribuire de forma: nr = _read(df,zt,n) unde: nr – este o variabilă de tip întreg căreia i se atribuie numărul de octe i scrişi în fişier. df – este descriptorul de fişier returnat de func ia _open la deschiderea sau _creat la crearea fişierului. zt - este un pointer spre zona tampon definită de utilizator, zonă din care se face scrierea. n – este dimensiunea zonei tampon sau numărul de octe i care se doreşte să se scrie. Defini ia func iei este: int _write( int handle, const void *buffer, unsigned int count );

204

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

205

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

În urma execu iei programului, se va afişa mesajul:
Wrote 36 bytes to file Press any key to continue

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

206

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

&gm. cale este pointer spre şirul de caractere care defineşte calea subdirectorului BGI care con ine driverele. int far unde: gd şi gm sunt pointeri ce au aceeaşi semnifica ie ca şi în cazul func iei detectgraph. ”C:\\BORLANDC\\BGI”).Zona spre care pointează gm memorează una din valorile: Adaptor CGA Constantă simbolică Valoare CGAC0 – 0 CGAC1 – 1 CGAC2 – 2 CGAC3 – 3 CGAHI – 4 EGALO – 0 EGAHI – 1 VGALO – 0 VGAMED – 1 VGAHI – 2 Rezolu ie 320*200 320*200 320*200 320*200 640*200 640*200 640*350 640*200 640*350 640*480 EGA VGA Modul grafic se defineşte în aşa fel încât el să fie cel mai performant pentru adaptorul grafic curent.&gm).int far *gm. Cele mai utilizate adaptoare sunt cele de tip EGA şi VGA. initgraph(int far *gd. initgraph(&gd.gm. 241 . …………………………… Doar după apelul func iei initgraph pot fi utilizate şi alte func ii de gestionare a ecranului în mod grafic. detectgraph(&gd. De exemplu dacă BGI este subdirector al directorului BORLANDC. atunci se utilizează şirul de caractere: ”C:\\BORLANDC\\BGI” Exemplu: Pentru setarea în mod implicit a modului grafic se poate utiliza următoarea secven ă de instruc iuni: …………………………… int gd. Aceasta setează modul grafic în conformitate cu parametri stabili i de apelul prealabil al func iei detectgraph şi are următorul prototip: void far *cale). Apelul func iei detectgraph trebuie să fie urmat de apelul func iei initgraph.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Exemplul următor alocă o matrice de 400000 octe i: Exemplu: #include <stdio.matrice_uriasa[k]).k<100000L.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.sizeof (long int)))==NULL) printf(”Eroare la alocarea matricii”). int huge *matrice_uriasa. else{ printf(”Completeaza matricea\n”). for(k=0. hfree(matrice_uriasa). Majoritatea compilatoarelor predefinesc o constantă specifică pentru a determina modelul curent de memorie. o op iune în cadrul liniei de comandă a compilatorului. Pentru a stoca o astfel de matrice programul trebuie să utilizeze cuvântul cheie huge pentru a crea un pointer. 259 . astfel: int huge *matrice_uriaşă după care programul trebuie să utilizeze func ia halloc pentru alocarea memoriei şi func ia hfree pentru eliberarea acesteia.k++) matrice_uriasa[k]=k%32768.k++) printf(”%d ”. de regulă. În tabelul următor sunt prezentate aceste constante definite de compilatoarele Microsoft C şi Borland C: Model de memorie Small Medium Compact Large Microsoft C M_I86SM M_I86MM M_I86CM M_I86LM Borland C _SMALL_ _MEDIUM_ _COMPACT_ _LARGE_ Programul poate verifica modelul de memorie utilizat folosind următoarea secven ă de instruc iuni: #ifndef _MEDIUM_ printf(”Programul cere modelul de memorie medium\n”).h> #include <malloc.k<100000L. if ((matrice_uriasa=(int huge*) halloc(100000L. } } Pentru selectarea unui anumit model de memorie se include. El trebuie utilizat doar ca ultimă resursă. for(k=0. f) huge – este un model utilizat doar în cazul utilizării unor matrici mai mari de 64Kb.h> void main(void) { long int k.

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

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

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

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

Dacă func ia returnează 0. 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. Dacă func ia returnează 0. Pentru aceasta se utilizează func ia _bios_equiplist care are următoarea sintaxă: 264 . utilizatorul a apăsat CTRL C Returnează starea tastelor de control: Bit 7 – INS este activat Bit 6 – CAPSLOCK este activat Bit 5 – NUMLOCK este activat Bit 4 – SCRLLOCK este activat Bit 3 – ALT este apăsată Bit 2 – CTRL este apăsată Bit 1 – SHIFT stânga este apăsată Bit 0 – SHIFT dreapta este apăsată Indică func iei să citească un caracter de la tastatură. cum ar fi tastele cu săge i Determină dacă este prezent un caracter la bufferul tastaturii. inclusiv tastele speciale.unde parametrul comanda specifică opera ia dorită şi poate avea una din următoarele valori: _KEYBRD_READ _KEYBRD_READY _KEYBRD_SHIFTSTATUS _NKEYBRD_READ _NKEYBRD_READY _NKEYBRD_SHIFTSTATUS Indică func iei să citească un caracter de la tastatură Determină dacă este prezent un caracter la bufferul tastaturii. Dacă valoarea returnată este 0xFFFF. înseamnă că nici o intrare de la tastatură nu este prezentă. cum ar fi tastele cu săge i Returnează starea tastelor de control. Dacă valoarea returnată este 0xFFFF. utilizatorul a apăsat CTRL C Func ia acceptă inclusiv tastele speciale. înseamnă că nici o intrare de la tastatură nu este prezentă.

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful