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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

un număr întreg în baza b se poate reprezenta cu un număr predeterminat de cifre ci ∈ B = {0.9} Noi suntem obişnui i să folosim mul imea cifrelor zecimale. De altfel şi operatorul uman face aceleaşi conven ii de reprezentare.1.6} b = 10 ⇒ B = {0..2. Spre exemplu: b = 2 ⇒ B = {0.3.3333 3 Limbajul C are o serie de func ii de reprezentare cu format a datelor numerice sau alfanumerice prin care programatorul poate impune un format extern cu care se manipulează datele.2. b − 1} .3.5.2. atunci mul imea cifrelor zecimale nu mai este suficientă pentru reprezentarea numerelor în acea bază..6. atunci vom scrie 1 ≅ 0. Mul imea B reprezintă mul imea cifrelor sau simbolurilor de reprezentare. 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 .4.1} b = 7 ⇒ B = {0.4. Reprezentarea externă a numerelor întregi Numerele naturale se pot reprezenta fie în baza de numera ie 10. fie în orice altă bază. Spre exemplu să considerăm baza b = 16 care va folosi 16 cifre hexazecimale (sau mai simplu hexa). 2.1.7.8. În general. Prin conven ie. Dacă totuşi se foloseşte o bază de reprezentare mai mare decât 10.î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.5. deci fixăm un format de reprezentare..1. Dacă formatul ale se limitează la 4 cifre zecimale.. b − 2.2...1. Spre exemplu ştim că numărul 1 nu poate fi exact 3 reprezentat ca un număr zecimal.

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 + ... se împarte succesiv numărul la 2 şi se utilizează resturile la aceste împăr iri în ordinea inversă de cum au fost ob inute. Adunarea numerelor naturale binare se face întocmai ca la cele în reprezentare în baza 10. a) Conversia din baza 10 în baza 2 şi invers Fie de exemplu numărul zecimal 37. + c1 ⋅ b1 + c 0 ⋅ b 0 ) = ± ∑ c k ⋅ b k k =0 n −1 În continuare vom studia următoarele probleme: . b − 2.1.cum se face conversia unui număr din baza b = 10 în baza b=2 cum se face conversia inversă. din baza 2 în baza 10 este simplă şi utilizează ponderea 2: 1001012 25 24 23 22 21 20 = 1 0 0 1 0 1 = 1x25 + 1x22 + 1x20 =37 Cu aceste numere naturale putem face o serie de opera ii aritmetice..2.Forma generală de reprezentare externă a numerelor întregi este de forma:  N b = ±c n −1c n − 2 ....... 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ă.. după regula: 0+0=0 27 .. din baza b = 2 în baza b = 10 cum se face conversia dintr-o bază oarecare b1 în altă bază b2 Pentru a reprezenta un număr natural din baza 10 în baza 2...c 2 c1c 0  c k ∈ B = {0.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

01 = .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.11 × 2  3+1 5. Astfel.10101× 23 10 2   1 .0111111 ⇒ 5.25 = 101.5 = .56 ⋅ 1038 Valoarea maximă exactă.0111111× 2 = 111. exponentul este decisiv pentru gama de reprezentare. avem exponent _ biasmax = 255 ⇒ exponent _ realmax = 255 − 127 = 128 nmax = 2128 = 28 ⋅ 210 = 256 ⋅ (1024)12 ≅ 256 ⋅ 1036 = 2.. Game de reprezentare pentru numerele reale Gama de reprezentare pentru fiecare din tipurile reale prezentate mai sus se calculează luând în considerare cel mai mare număr şi cel mai mic număr posibil a fi scris în respectiva reprezentare. 1. calculată fără a aproxima ca mai sus: 210 = 1024 ≅ 1000 = 103 este n max = 3.25 × 1.25 × 1.7 ⋅10 308 46 .5 = [(. în final corelându-se forma de reprezentare internă.10101) × (.111 = 7.11 −−−−− 10101 10101 −−−−− .6 ⋅ 10307 ( ) Valoarea maximă exactă este n max = 1.875 Se observă cum câmpurile exponent şi significand sunt procesate separat.10101 × 5.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.12 = .510 = 1.11)]× 2  4 . La tipul float.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Deoarece o etichetă este locală în corpul unei func ii rezultă că ea este nedefinită în afara corpului func iei respective. repeti ia cu test ini ial. se realizează un salt la instruc iunea prefixată de eticheta aflată după instruc iunea goto.1. saltul necondi ionat. Instruc iunile pot fi clasificate în: instruc iuni etichetate. 5. singura utilizare a sa fiind ca destina ie a unei instruc iuni goto. Exemplu: Următorul program utilizează instruc iunea goto pentru a afişa numerele de la 1 la 100: 76 . instruc iuni de salt. instruc iuni expresie. 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. ieşirea prematură dintr-un ciclu. repeti ia cu număr cunoscut de paşi. instruc iuni repetitive. instruc iuni de selec ie. Etichetele sunt locale în corpul func iei în care sunt definite. deci. o instruc iune goto nu poate face salt la o instruc iune din afara corpului func iei în care este definită. instruc iuni compuse. decizia şi selec ia. repeti ia cu test final. Nu este recomandată utilizarea abuzivă a acestei instruc iuni deoarece programul devine mai pu in lizibil şi pot apare erori logice în program foarte greu de detectat. Instruc iunea goto se utilizează în special pentru ieşirea din mai multe cicluri imbricate. 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. Etichetele nu pot fi redeclarate.Capitolul V INSTRUC IUNI Limbajul C posedă un set variat de instruc iuni.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

140 . info next NULL info next previous Listă dublu legată info NULL previous Pointerul next indică spre următorul nod. 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. }. una care pointează spre primul nod al listei iar cealaltă spre ultimul nod al listei. una fiind imaginea în oglindă a celeilalte. char street[30]. unsigned int zip. 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]. char state[10]. Pentru aceasta vom utiliza două variabile globale de tip pointer. Particular struct addr { char name[20]. Particularizările se vor face pe exemplul bazei de date construite anterior. struct addr *next. Vom prezenta în continuare modul în care putem proiecta func iile principale care ac ionează asupra unei structuri dinamice. }. info next info next Listă circulară info next Listele dublu legate sunt utile în rezolvarea unor probleme care necesită parcurgeri frecvente ale structurilor dinamice în ambele sensuri.singura deosebire că ultimul element nu are adresa de pointer vid (NULL) ci adresa primului element.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Denum. Tipul Variab. variabilei

int x int x int x p q

globală locală locală pointer local pointer local

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

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

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

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

160

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

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

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

din

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

161

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

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

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

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

162

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

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

}

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

163

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

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

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

164

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

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

Rezultatul rulării programului va fi:
abcdefghijklmnoprstuvxyzw ABCDEFGHIJKLMNOPRSTUVXYZW ABCDEFGHIJKLMNOPRSTUVXYZW

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

165

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

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

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

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

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

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

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

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

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

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

de către toate Permanentă. 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ă. Din punctul de vedere al duratei de via ă a variabilei. Domeniul de vizibilitate este în general determinant şi în stabilirea duratei de via ă a variabilei.13.8. cât timp este in memorie programul executabil Vizibilitatea precizează domeniul sau locul în care o variabilă este vizibilă. numai (registru) când se execută func ia în care este declarată Extern Global. segment de memorie statică (sau de date) şi segment de memorie dinamică (sau stivă). cunoscute în mod tradi ional ca segment de memorie text. pe func iile dintr-un fişier parcursul rulării sursă sau din mai multe programului fişiere sursă executabil Static Local sau global Permanentă. parametrii func iilor şi apelurile şi retururile dinamică (de tip stivă) de/din func ii În tabelul următor se prezintă caracteristicile claselor de memorie. aceasta poate fi temporară (există numai pe perioada în care func ia care o 176 . Segment de memorie text Con ine instruc iunile programului. 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. memoria computerului este organizată în trei zone. 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ă. Clase de memorare (specificatori sau atribute) Din punct de vedere al execu iei programelor C.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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.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ă. După adăugare comanda MS-DOS TYPE va tipări toate datele con inute în fiier. 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 . După ce s-a făcut o adăugare. La un moment dat pot fi deschise cel mult FOPEN_MAX fişiere. ieşirea standard şi ieşirea standard pentru erori. Men ionăm că stdin. 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. Dacă modul "mode" include "b" după litera ini ială. ca în "rb" sau "w+b" se indică un fişier binar. 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. 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

unde culoare este un întreg în intervalul [0. int top. . – afişează un şir de caractere în mod similar func iei puts.15] a cărui semnifica ie este explicată de tabelul următor: Culoare Negru Albastru Verde Turcoaz Roşu Purpuriu Maro Gri deschis Gri închis Albastru deschis Verde deschis Turcoaz deschis Roşu deschis Purpuriu deschis Galben Alb Clipire Constantă simbolică BLACK BLUE GREEN CYAN RED MANGETA BROWN LIGHTGRAY DARKGRAY LIGHTBLUE LIGHTGREEN LIGHTCYAN LIGHTRED LIGHTMANGETA YELLOW WHITE BLINK Valoare 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 128 11. – afişează date sub controlul formatelor în mod similar func iei printf. – afişează un singur caracter. . . .int putch (int c). bottom) – dreapta jos la adresa de memorie indicată de pointerul destination. .copiază textul cuprins în dreptunghiul definit de coordonatele (left. .void clreol (void).int gettext (int left. . int right. 236 .şterge sfârşitul liniei începând cu pozi ia cursorului. Func ii pentru gestiunea textelor Pentru afişarea caracterelor se pot folosi func iile: . top) – stânga sus şi (right. .şterge toată linia pe care este pozi ionat cursorul.Culoarea caracterelor se setează cu ajutorul func iei textcolor ce are următorul prototip: void textcolor(int culoare).int cputs (const char *str). void *destination).int cprintf (const char *format).void delline (void).6. int bottom.

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

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

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

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

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

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

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

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

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

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

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

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

int jos.int raza) – desenează un sector de cerc colorat. int ind) – func ia desenează o prismă colorată pentru ind diferit de zero. astfel: NORM_WIDTH – valoarea 1 pixel şi THICK_WIDTH – valoarea 3 pixeli. void far setfillstyle(int haşura. astfel: 249 . int unghifin. void far pieslice(int xcentru. int dr. int dr. pentru ind=0. int sus. int unghistart.int culoare) – este utilizată pentru a defini o haşură a utilizatorului.şablon – defineşte stilul liniei şi are sens doar când parametrul stil are valoarea 4. void far getlinesettingstype(struct linesettingstype far* linieinfo) – este utilizată pentru a determina stilul curent. grosime – defineşte lă imea liniei în pixeli. void far bar3d(int st. int ycentru. int ycentru. int sus. int far* tabpct) – desenează un poligon colorat. int semiaxamare. nu se trasează partea de sus a prismei. int semiaxamică) – desenează o elipsă colorată. haşura – defineşte haşura utilizată pentru colorare conform tabelului: Constantă simbolică EMPTY_FILL SOLID_FILL LINE_FILL LTSLASH_FILL SLASH_FILL BKSLASH_FILL LTBKSLASH_FILL HATCH_FILL XHATCH_FILL INTERLEAVE_FILL WIDE_DOT_FILL CLOSE_DOT_FILL USER_FILL Valoare 0 1 2 3 4 5 6 7 8 9 10 11 12 void far setfillpattern(char far *h_utilizator. int culoare) – defineşte modul de colorare al figurilor. void far fillellipse(int xcentru. astfel: culoare – defineşte culoarea utilizată pentru haşurare. int jos) – are aceeaşi semnifica ie cu func ia rectangle însă dreptunghiul este colorat. void far fillpoly(int nr. void far bar(int st. int profunzime.

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

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

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

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

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

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

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

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

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

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

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

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

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

sector şi nr_sector precizează sectoarele fizice ale disculuice trebie scris sau citit. int head. int nr_sector. int unitate. valoarea returnată precizează eroarea. Parametru operatie specifică func ia dorită astfel: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Ini ializează sistemul de disc Returnează starea ultimei opera ii pe disc Citeşte numărul precizat de sectoare Scrie numărul precizat de sectoare Verifică numărul precizat de sectoare Formatează pista specificată Formatează pista specificată şi marchează sectoarele defecte Formatează unitatea începând cu pista specificată Returnează parametrii unită ii de disc Ini ializează unitatea de disc Execută o citire lungă – 512 octe i de sector plus patru suplimentari Execută o scriere lungă – 512 octe i de sector plus patru suplimentari Execută o pozi ionare pe disc Ini ializarea alternativă a discului Citeşte bufferul sectorului Scrie bufferul sectorului Testează dacă unitatea este pregătită Recalibrează unitatea Execută diagnosticarea unită ii de RAM Execută diagnosticarea unită ii Execută diagnosticarea internă a controlerului Dacă se execută cu succes. 2) opera ii intrare/ieşire Opera iile intrare/ieşire de nivel jos pot fi realizate utilizând func ia biodisk ce are următoarea sintaxă: int biodisk(int operatie. int track. int sector. Dacă apare o eroare. 3) servicii de tastatură din BIOS Pentru accesul la serviciile de tastatură din BIOS.6 – confirmare dispozitiv. 1 pentru B. Parametrii head. track. şi aşa mai departe. func ia returnează valoarea 0. care este 0 pentru A. void *buffer) unde parametrul unitate precizează numărul unită ii. 7 – dispozitivul nu este ocupat. C-ul pune la dispozi ie func ia _bios_keybrd ce are următoarea sintaxă: unsigned _bios_keybrd(unsigned comanda) 263 . Parametru buffer este un pointer la bufferul din care sunt citite sau în care sunt scrise datele.

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

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

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

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

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

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

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

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

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

Sign up to vote on this title
UsefulNot useful