Professional Documents
Culture Documents
CUPRINS
CUPRINS _______________________________________________________________________ i Cap 1 INTRODUCERE N LIMBAJUL C _____________________________________________4 1.1 Scurt istoric_________________________________________________________________4 1.2 Forma unui program C _______________________________________________________8 1.3 Compilarea unui program C___________________________________________________9 Cap 2 Tipuri, operatori, expresii_____________________________________________________11 2.1. Tipuri simple ______________________________________________________________11 2.2. Literali ___________________________________________________________________12 2.3. Declararea unei variabile i a unei constante ____________________________________13 2.4. Operatori _________________________________________________________________14 2.4.1. Operatori aritmetici ______________________________________________________15 2.4.2. Operatorii relaionali (de comparaie) ________________________________________15 2.4.3. Operatori logici _________________________________________________________16 2.4.4. Operatori la nivel de bit ___________________________________________________16 2.4.5. Operatorul de asignare ____________________________________________________16 2.4.6. Operatorul condiional ____________________________________________________17 2.4.7. Operatorul virgul _______________________________________________________17 2.4.8. Precedena operatorilor ___________________________________________________18 Cap 3 FUNCTII _________________________________________________________________19 3.1. Definirea unei funcii _______________________________________________________19 3.2. Returnarea unui apel _______________________________________________________20 3.3. Funcii cu un numr variabil de parametri _____________________________________20 3.4. Sfritul execuiei unui program ______________________________________________20 3.5. Apelul i transmiterea parametrilor ___________________________________________21 3.6. Funcia principal main _____________________________________________________22 Cap 4 FUNCII DE INTRARE IEIRE ______________________________________________24 4.1.Fluxuri i fiiere ____________________________________________________________24 Semnificaia __________________________________________________________________26 4.2. Funcii de intrare/ieire pentru caractere_______________________________________27 4.3. Scrierea cu format__________________________________________________________28 4.5. Citirea cu format ___________________________________________________________32 Cap 5 INSTRUCIUNI DE CONTROL ______________________________________________35 5.1. Instruciunea if ____________________________________________________________36 5.3. Instruciunea while_________________________________________________________38 5.4. Instruc]iunea do ... while ___________________________________________________39
i
Gheorghe GRIGORA
5.5. Instruciunea for ___________________________________________________________39 5.6. Instruciunea break ________________________________________________________41 5.7. Instruciunea continue _____________________________________________________42 5.8. Instruciunea go to_________________________________________________________42 5.9. Exerciii __________________________________________________________________43 1. Calculul factorialului. _____________________________________________________43 2. Conversie _________________________________________________________________43 3. Numrarea biilor 1 ntr-un numr ______________________________________________43 4. Prezentare de date __________________________________________________________43 5.10. Soluii la exerciii _________________________________________________________43 1. Calculul factorialului ________________________________________________________43 2. Conversie _________________________________________________________________44 3. Numrarea biilor 1 ntr-un numr ______________________________________________45 4. Prezentare de date __________________________________________________________45 Cap 6 TABLOURI I POINTERI ___________________________________________________47 6.1. Pointeri ___________________________________________________________________47 6.2. Tablouri cu o dimensiune ____________________________________________________48 6.2.1. Referenierea unui element al tabloului _______________________________________48 6.2.2. Iniializarea unui tablou ___________________________________________________48 6.3. Relaia ntre pointeri i tablouri ______________________________________________50 6.4. Operaii aritmetice cu pointeri _______________________________________________51 6.5. iruri de caractere _________________________________________________________51 6.6. Tablouri cu mai multe dimensiuni ____________________________________________51 6.7. Tablouri de pointeri ________________________________________________________53 6.8. Pointeri i alocarea memoriei_________________________________________________54 6.9. Operatorul sizeof ________________________________________________________55 6.10. Pointeri ctre funcii _______________________________________________________55 6.11. Exerciii _________________________________________________________________57 6.12. Soluii ___________________________________________________________________57 Cap 7 CONSTRUIREA DE NOI TIPURI _____________________________________________59 7.1. Redenumirea unui tip _______________________________________________________59 7.2. Tipul structur ____________________________________________________________60 7.3. Accesul i iniializarea cmpurilor unei structuri ________________________________61 7.4. Structuri autorefereniate ___________________________________________________61 7.5. Tipul enumerare ___________________________________________________________63 7.6. Uniuni ____________________________________________________________________63 7.7. Exerciii __________________________________________________________________65 7.8. Soluii ____________________________________________________________________66 Cap 8 GESTIUNEA MEMORIEI ___________________________________________________70
ii
Programare_ Limbajul_C
8.1. Precizarea unui mecanism de memorare _______________________________________71 8.2. Gestiunea automat a memoriei ______________________________________________71 8.3. Gestionarea register a memoriei ____________________________________________71 8.4. Gestionarea static a memoriei _______________________________________________72 8.5. Variabile globale ___________________________________________________________72 8.6. Declararea variabilelor externe _______________________________________________72 8.7. Gestiunea dinamic a memoriei_______________________________________________73 8.8. Exerciii __________________________________________________________________73 8.9. Soluii ____________________________________________________________________74 Cap 9 CONVERSIA DE TIP _______________________________________________________76 9.1. Mecanisme de conversie ____________________________________________________76 9.1.1 Conversia la asignare _____________________________________________________77 9.1.2 Evaluarea expresiilor aritmetice _____________________________________________77 9.1.3. Evaluarea expresiilor logice________________________________________________78 9.1.4 Conversii explicite _______________________________________________________78 9.1.5 Conversie de pointeri. Pointerul universal _____________________________________79 Cap 10 PREPROCESORUL ________________________________________________________80 10.1 Directive preprocesor ______________________________________________________80 10.1.1 Constante simbolice. Directiva #define ______________________________________81 10.1.2 Includerea fiierelor _____________________________________________________81 10.1.3 Compilare condiionat ___________________________________________________82 10.1.4 Directiva #error ______________________________________________________82 10.2 Macroinstruiuni __________________________________________________________83 10.2.1 Macroinstruciuni predefinite ______________________________________________83 10.2.2 Macroinstruciuni tip funcie_______________________________________________83 Cap 11 EXERCIII_______________________________________________________________84 BIBLIOGRAFIE_________________________________________________________________86 ANEXA ________________________________________________________________________87 Biblioteca standard C __________________________________________________________87 Codul ASCII __________________________________________________________________90
iii
1.1 Scurt istoric 1.2 Forma unui program C 1.3 Compilarea unui program C
Programare_ Limbajul_C
Cristopher Strachey, cel care a condus echipa ce a proiectat CPL-ul. Limbajul a fost proiectat i implementat de ctre Dennis Ritchie la Bell Laboratories n 1972. n mare parte ( construciile for i switch, tratarea pointerilor etc.) limbajul a fost influienat i de Algol 68. Genealogia limbajului C este prezentat n Figura 1.1. Ritchie a lucrat pe un calculator DEC PDP-11 sub sistemul de operare Unix. Din acest motiv varianta clasic a limbajului C a fost asociat sistemului de operare Unix - versiunea 5 i mult vreme a constituit un standard (neoficial de altfel) al limbajului [KeR 78]. Odat cu cretere popularitii calculatoarelor personale, au aprut numeroase alte implementri ale limbajului C i astfel a crescut i popularitatea acestuia. Un fapt cu totul inedit a fost acela c, mare parte din implementrile C -ului s-au dovedit de un mare grad de compatibilitate: un program scris pentru una din variante putea fi compilat cu succes folosind o alt variant. Se nelege c au existat i neconcordane n multitudinea de dialecte ale limbajului. Pentru eliminarea acestora n 1983 a fost creat o comisie care s stabileasc un standard al acestui limbaj. Standardul ANSI ( American National Standard Institute) a fost redactat abia n decembrie 1989 i a fost pus la dispoziia celor interesai la nceputul lui 1990. Firma Borland a folosit acest standard n faza de proiect i a realizat implementrile Turbo C care se bucur de un succes remarcabil. De altfel, n prezent , toate compilatoarele C sunt compatibile cu standardul ANSI C; unele din acestea au adugate o serie de faciliti sau extensii proprii care nu mpiedic compilarea unui program standard.
C (1972) C++ (1984) Figura 1.1. Limbajul C este privit ca un limbaj de nivel mediu ntruct combin cele mai reuite elemente ale limbajelor de nivel nalt ( Pas-cal, Fortran, Algol, Modula, Ada) cu gradul de control i flexibilitatea oferite de ctre limbajul de asamblare. C permite manevrarea biilor, a octeilor i a adreselor de memorie, adic a elementelor de baz n funcionarea unui calculator. n ciuda acestui fapt , programele scrise n C
Gheorghe GRIGORA
au un grad mare de portabilitate: uurina de a adapta programele scrise pentru o anumit categorie de calculatoare sau sisteme de operare la alte calculatoare sau sisteme de operare. Limbajul C are structuri de control adecvate precum i faciliti de structurare a datelor care l fac s fie folosit ntr-o diversitate de domenii. Are de asemenea o mulime bogat de operatori care-i dau un nalt grad de expresivitate. Unul din motivele pentru care limbajul C este n acelai timp agreat de mare parte din programatori i dezagreat de alt mare parte din programatori, este lipsa total a verificrii tipului (type checking). De exemplu, o variabil de un tip scalar oarecare poate apare n aceeai expresie cu o variabil de un alt tip scalar. Cei care agreaz limbajul C i apreciaz flexibilitatea sa, cei care nu-l agreaz l consider un limbaj lipsit de siguran. Ceea ce este sigur, este c datorit faptului c face parte din sistemul de operare Unix, limbajul a cptat o mare popularitate n lumea academic i nu numai. Compilatoarele oferite mpreun cu sistemul Unix sunt relativ ieftine (uneori chiar gratuite) i pot fi folosite pe o diversitate de maini. O nou versiune a limbajului C, numit C++, a aprut n 1984. Originea acestui limbaj STR 94 trebuie cutat n proiectul care a nceput n Aprilie 1979 la Computing Research Center of Bell Laboratories n Murray Hill, New Jersey, USA, proiect ce trebuia s realizeze distribuirea nucleului UNIX ntr-o reea de computere conectate LAN. Pentru realizarea acestui proiect, Bjarne Stroustrup a realizat n Octombrie 1979 un preprocesor, numit Cpre, care a adugat la limbajul C conceptul de clas folosit pentru prima dat de limbajul Simula ( dezvoltat n anii 1962-1967 de norvegienii Kristen Nyagaard i Ole-Johan Dahl). n martie 1980 preprocesorul mbuntit era implementat pe 16 sisteme iar limbajul acceptat de acesta a fost numit C cu Clase. Motivele pentru care a fost ales limbajul C (n faa altor limbaje ale vremii: Modula-2, Ada, Smalltalk, Mesa, Clu) pentru acest proiect sunt expuse chiar de creatorul noului limbaj: C este un limbaj flexibil: se poate folosi n orice domeniu i permite utilizarea oricror tehnici de programare; C este un limbaj eficient: semantica sa este low level ceea ce nseamn c pot fi folosite cu eficien resursele hardware pentru programele C. Aa cum vom vedea pe parcursul prezentrii limbajului, acesta oglindete conceptele fundamentale ale unui computer tradiional; C este un limbaj disponibil: un limbaj pentru care se poate gsi cu uurin un compilator, indiferent de ce calculator dispunem; C este un limbaj portabil: chiar dac un program C nu este automat portabil de la o main la alta, acest lucru este posibil n general fr un efort deosebit. Cele mai mari influiene asupra noului limbaj le-au avut: Simula care a dat clasele, Algol 68 care a dat suprancrcarea operatorilor, referinele i abilitatea de a declara variabile oriunde n cadrul unui bloc i BCPL de unde a fost luat forma de prezentare a comentariilor cu //. Dup numele C cu clase limbajul a fost botezat C84. Asta pentru scurt timp ns deoarece comitetul ANSI pentru standardizarea C-ului a atras atenia c se poate crea o confuzie cu aceast denumire; era vorba totui de un nou limbaj, chiar dac era cldit pe C. Numele C++ a fost sugerat de Rick Mascitti, un membru al echipei lui Stroustrup, i a fost folosit pentru prima dat n Decembrie 1983.Iat ce spune Stroustrup despre aceast alegere: <<I picked C++ because it wos short, had a nice interpretation, and wasnt of the form adjective C. In C++, ++ can, depending on context, be read as next, succesor or increment , tough it is always pronounced plus plus.>> n figura 1.2. este prezentat genealogia limbajului C++ iar figura 1.3. prezint etapele importante din proiectarea limbajului. Datele care apar n parantez se refer la prima implementare disponibil pentru limbajul respectiv(dup [STR 94]). C++arm reprezint The Annotated C++ Reference Manual iar C++std este standardul ANSI C++.
Programare_ Limbajul_C
Fortran(1956) Algol 60(1960) CPL(1963) BCPL(1967) Simula 67(1969) C Algol 68(1977) C cu Clase (1980)
Ada (1983) C++ (1984) ANSI C (1986) ML(1976) Clu(1978) C++arm(1990) C++std (1995) Figura 1.2. 1979 1980 1982 1983 1984 1985 1986 1987 Mai Octombrie Aprilie Ianuarie August Decembrie Ianuarie Februarie Octombrie Septembrie Noiembrie nceputul proiectului C cu clase Prima implementare a lui C cu clase Primul raport intern la Bell Labs asupra lui C cu clase Primul articol extern asupra lui C cu clase Prima implementare a limbajului C++ Limbajul a fost botezat C++ Primul manual C++ Prima lansare extern C++ Manualul The C++ Programming Language Prima conferin OOPSLA (centrat pe Smalltalk) Prima conferin USENIX C++
7
Gheorghe GRIGORA
1988 1990
1991 1992
1994
Decembrie Ianuarie Iunie Martie Mai Mai Iunie Februarie Martie Mai August
Prima lansare GNU C++ Prima implementare Oregon Software C++ Prima implementare Zortech C++ Prima reuniune tehnic ANSI X3J16 pentru C++ Prima implementare Borland C++ C++arm: The Annotated C++ Reference Manual Prima reuniune ISO WG21 Prima lansare DEC C++ Prima lansare Microsoft C++ Prima lansare IBM C++ A fost nregistrat Raportul Comitetului ANSI/ISO Figura 1.3.
Programare_ Limbajul_C
S trecem n revist structura programului prezentat. Primele linii din program constituie comentarii. Aceste comentarii sunt incluse ntre "parantezele" /* i */. Urmeaz o linie ce ncepe cu #. Pe aceast linie este o directiv care este gestionat de precompilator (preprocesor) i permite includerea definiiilor funciilor intrare/ieire furnizate de sistem i care se gsesc n fiierul stdio.h. Programul pe care l-am prezentat este constituit din dou funcii: main i cmmdc. Trebuie reinut faptul c orice program C conine definiia unei funcii cu numele main care ine locul programului principal: punctul de intrare n execuia unui program C este funcia main. Corpul unei funcii este delimitat de acolade: { ine locul lui begin (dinalte limbaje) iar } al lui end. n corpul funciei main n Programul 1 avem apeluri la alte funcii: printf, scanf, cmmdc. n limbajul C nu exist instruciuni de intrare/ieire; operaiile de intrare/ieire se realizeaz prin apel la funciile unei biblioteci standard furnizate de sistem. Aceste funcii vor fi prezentate mai trziu ntr-un capitol special. n exemplul de aici avem: printf cu un parametru aflat ntre ghilimele: acest text va fi tiprit pe ecran scanf cu trei parametri: "%d%d", &a i %b. Acesta este un apel la o operaie de citire a unor date pe care le introducem de la tastatur. Parametrul "%d%d" specific formatul de citire a dou numere ntregi (d de la digit) iar parametrii &a, &b corespund adreselor (faptul c & apare n fa) unde vor fi memorate cele dou valori ntregi: &a adresa variabilei a, iar &b adresa variabilei b. printf ("Cmmdc al celor doua numere este: %dn" cmmdc (a,b)) este un apel la o operaie de scriere (pe ecranul monitorului) a textului: cuprins ntre " i " n afar de %dn care reprezint formatul cu care se va tipri valoarea returnat de apelul la funcia cmmdc (a,b): va fi scris un numr ntreg (%d) dup care se trece la linie nou (caracterul n). Definiiile acestor funcii se afl n fiierul sistemului stdio.h. Construcia "do {..} while ( .. )" care apare i n funcia main i n funcia cmmdc este una din structurile iterative de care dispune limbajul C: grupul de instruciuni cuprins ntre acoladele de dup do se execut atta timp ct condiia scris dup while este ndeplinit (de precizat c acel grup de instruciuni se execut mcar odat). Structurile de control ale limbajului C se vor prezenta ntr-un capitol special.
Gheorghe GRIGORA
Toate programele n C constau dintr-una sau mai multe funcii; singura funcie care trebuie s fie prezent n orice program se numete main( ). Aceasta este prima funcie apelat la nceputul executrii programului. ntr-un program C corect conceput, funcia main( ) conine, n esen, o schem a ceea ce face programul, schem compus din apeluri ale altor funcii aflate n biblioteca standard a limbajului C sau definite de programator. Standardul ANSI C precizeaz setul minimal de funcii care va fi inclus n biblioteca standard; compilatoarele ce exist pe pia conin cu siguran i alte funcii. Procesul de legare (linking) este acela n care programul de legare(linker) combin codul scris de programator cu codul obiect al funciilor utilizate i care se gsete n bibliteca standard. Unele compilatoare au programul propriu de legare, altele folosesc programul de legare al sistemului de operare. Un program C compilat creeaz i folosete patru zone de memorie disjuncte, care ndeplinesc funcii distincte (figura 1.4.). Prima regiune este aceea care stocheaz codul programului.Urmeaz o zon de memorie n care sunt stocate variabilele globale. Celelalte dou zone reprezint aa zisa stiv a programului i zona de manevr. Stiva este folosit n timpul execuiei programului n scopurile: memoreaz adresele de revenire ale apelurilor la funcii; memoreaz argumentele funciilor; memoreaz variabilele locale; memoreaz starea curent a unitii centrale de prelucrare. STIVA
ZONA DE MANEVR VARIABILE GLOBALE CODUL PROGRAMULUI Figura 1.4. Zona de manevr (heap) este o regiune de memorie liber pe care programul , prin intermediul funciilor de alocare dinamic ale limbajului C, le folosete pentru stocarea unor articole de genul listelor nlnuite i arborilor. S mai precizm , n ncheierea acestei scurte introduceri , c n general, compilatoarele existente pe pia, sunt compilatoare C++. Aa cum vom vedea n partea a doua a acestei cri, C++ este o versiune extins a limbajului C, care a fost conceput pentru programarea orientat pe obiecte. De aceea C++ accept limbajul C: orice program C va putea fi compilat de un compilator C++.Dup unii autori, programarea n limbajul C va dinui mult vreme de acum ncolo, aa cum dinuie de cnd a fost creat limbajul i, un programator care nu tie C nu va putea programa n C++.
10
Programare_ Limbajul_C
2.1. Tipuri simple 2.2. Literali 2.3. Declararea unei variabile 2.4. Operatori
O expresie n limbajul C ca n orice limbaj de programare este o secven de operatori i operanzi care descrie algoritmul de calcul al unei valori. Operanzii ntr-o expresie sunt nume de variabile, nume de constante (literali), apeluri la funcii. Aceti operanzi, fiecare dintre ei, au un anume tip iar n funcie de aceste tipuri se vor aplica algoritmi specifici pentru determinarea valorii expresiei.
11
Gheorghe GRIGORA
Tipuri reale: float, double Aceste tipuri sunt folosite pentru a reprezenta numere reale. Ele se disting prin precizia de reprezentare: float numere reale n simpl precizie aproximativ 6 cifre semnificative - iar double pentru numere reale n dubl precizie aproximativ 15 cifre semnificative.. Tipul vid: void. Este un tip folosit pentru reprezentri de dimensiune nul. Pentru tipurile ntregi se pot folosi specificrile signed i unsigned. Specificarea signed tip este echi-valent cu tip iar unsigned tip are semnificaia din Tabela 2.
2.2. Literali
Literalii (sau constantele literale) sunt valori constante; ele reprezint valori care nu pot fi modificate pe parcursul execuiei unui program. n limbajul C se folosesc diferite reguli sintactice pentru reprezentarea constantelor. Tabela 3 sintetizeaz unele din aceste reguli.
Exist i caractere speciale (de control) care sunt compuse din caracterul \ i din alt caracter. Acestea sunt: \n - interlinie (linie nou); \t - tabulare; \\ - caracterul \ (backslash); \f - retur de linie; \o - caracterul NULL; \ - caracterul \ - caracterul ; \r - retur de car; \u - tabulare vertical; \b - backspace; \ddd - caracter de cod octal ddd; \xhh - caracter de cod hexazecimal hh.
12
Programare_ Limbajul_C
Gheorghe GRIGORA
centrale i nu n memoria RAM. Variabilele din clasele auto i register nu i pstreaz valoarea de la un apel la altul al funciei n care sunt definite. Variabilele din clasa static difer de cele din clasa auto sau register prin aceea c sunt memorate n locaii fixe de memorie (au o adres fix, permanent). Dac se definesc n interiorul unei funcii trebuie s aib neaprat n fa cuvntul rezervat static (se mai spune c sunt n clasa static internal). Variabilele definite n exteriorul tuturor funciilor fr vreun specificator de clas sunt n clasa extern i au alocate adrese fixe. Tabelul 5 sintetizeaz clasele de alocare.
Static n modulul n care-i DA DA external definit External n toate modulele DA (n funcie) aplicaiei Static n toate modulele DA External aplicaiei (n funcie) n capitolul al optulea sunt tratate mai n amnunt problemele legate de gestiunea memoriei.
2.4. Operatori
Operatorii se folosesc la constituirea de expresii pornind de la constante i variabile. Evaluarea expresiilor se face nlocuind operanzii prin valorile sale i efectund operaiile conform cu specificaiile acestora. Am vzut c operanzii pot fi variabile, constante sau apeluri la funcii (despre care se va vorbi mai trziu). Fiecare dintre acetia au un tip declarat. Dac tipul operanzilor este acelai ntr-o expresie, evaluarea se face n mod natural. Spre exemplu, dac se declar: float alpha = 20.45; float beta = 25; atunci evaluarea expresiei: alpha + sqrt (beta),se face n ordinea indicat mai jos: alpha + sqrt (25) alpha + 5.0 20.45 + 5.0 25.45 Dac operanzii au tipuri diferite , se aplic conversii implicite de tip: unul din operanzi se convertete la tipul celuilalt iar tipul rezultatului va fi tipul comun. Operatorii ce pot apare ntr-un program C sunt de patru feluri: operatori primari de parantetizare; acetia delimiteaz subexpresii i pot schimba asociativitatea operatorilor. De pild expresiile x * y +z i x * (y + z) vor fi evaluate diferit pentru aceleai valori ale variabilelor, ca i perechea: x+y*z ; (x + y) * z.
14
Programare_ Limbajul_C
operatori binari cu notaie infixat adic: operand operator operand. Exemplu: x+y x % y. operatori unari cu notaie prefixat sau postfixat: -x, ++x, y--, x++. operatorul ternar condiional operand 1 ? operand 2 : operand 3.
Gheorghe GRIGORA
Programare_ Limbajul_C
Pe lng acest tip de asignare, limbajul C permite compunerea asignrii (#) cu un operator binar (op) obinndu-se operatori de asignare compus: e1 op = e2; unde e1 i e2 sunt expresii.
Aceast asignare este echivalent cu : e1 = (e1) op (e2); Se obin asignri compuse cu: Operatori aritmetici: +=, -=, *=, /=, %= Operatori logici la nivel de bit: &=, |=, ^= Operatori de decalare: <<=, >>= Faptul c atribuirea este privit ca un operator binar, permite ca acetia s formeze expresii care au atribuite valori. Astfel variabil = expresie; este o expresie ce are ca valoare, valoarea expresiei din dreapta semnului # . Acest lucru permite atribuirea n lanpreciznd c asociativitatea operatorului de asignare este de la dreapta la stnga: var1 = var2 = ... = varn = expresie; prin care cele n variabile capt valoarea expresiei din partea dreapt. Spre exemplu i = j = k = l = 1; este o construcie valid n C; prin aceasta cele patru variabile i, j, k, l capt valoarea 1.
17
Gheorghe GRIGORA
Expresiile sunt evaluate de la stnga la dreapta iar valoarea ntregii expresii este valoarea (i tipul, se nelege) expresiei ultime ce apare n acest ir. O asemenea construcie apare frecvent n instruciuni for pentru realizarea mai multor aciuni: for(i = 0,j = 0, i < 100; i++, j++) for(i = 0,j = n-1, i < j; i++, j--)
Tabelul nr.6.
Operator :: :: ,. [] () sizeof -> , . ~ ! +, * & () new,delete ->* , .* *, /, % +,>> , << <,<=, >, >= ==, != & ^ | && || ?: =,*=,/=, %= +=, -=, <<=, >>= &=, ^=, |= ,
18
Funcia (semnificaia) Acces explicit la o variabil global (unar) Acces explicit la o clas (binar) Selectarea unui membru Indexarea unui tablou Apel la o funcie Lungimea n bii Incrementare, decrementare Negare pe bii Negare logic Plus, minus unari Acces la o variabil Adresa unei variabile Conversie de tip (cast) Operatori de gestiune a memoriei Selectarea unui membru Operatori multiplicativi Operatori aritmetici Operatori de decalare Operatori relaionali Egalitate, inegalitate Conjuncie pe bit Sau exclusiv pe bit Sau (disjunctiv) pe bit Conjuncie logic Disjuncie logic Afectare condiional Operatori de asignare compus Operatorul virgul
Descrierea
Cap 3 FUNCTII
3.1. Definirea unei funcii 3.2. Returnarea unui apel 3.3. Funcii cu un numr variabil de parametri 3.4. Sfritul execuiei unui program 3.5. Apelul i transmiterea parametrilor 3.6. Funcia principal main
n limbajul C - spre deosebire de Pascal sau alte limbaje - nu exist noiunile distincte de procedur i de funcie. n C exist doar funcii care returneaz o valoare. Este posibil definirea de funcii care s retuneze valoarea vid: n acest caz tipul valorii returnate este void. n general, utilizarea unei funcii permite: - descompunerea unei secvene lungi de procesare a informaiei ntr-o mulime de secvene mici de transformri de baz ale informaiei; - furnizarea ctre ali programatori a unor elemente de baz de nivel nalt; detaliile acestor elemente rmn ascunse pentru utilizator. Aceste elemente de nivel nalt ofer o claritate n plus programului global i permite - relativ uor - modificri ulterioare ale acestuia.
Gheorghe GRIGORA
funcii definete att tipul su (tipul valorii returnabile) ct i numrul i tipul argumentelor sale. Exist dou moduri de a declara o funcie: - implicit: n definiia funciei, linia de nceput conine tipul su i argumentele sale (numr i tip); - explicit: printr-o instruciune ce descrie signatura sa n cazul n care se face apel la o funcie descris ntr-un fiier separat sau n cazul n care apelul la funcia respectiv precede declaraia funciei n acelai fiier. Iat cteva instruciuni de declarare a unei funcii: long int cod (const char *, int); char* decod (long int); void schimbare (int , int); int strlen (const char* s); int funct ( ); n ultima linie avem un exemplu de funcie fr parametri (numrul parametrilor este zero).
Programare_ Limbajul_C
transmis la apel. Aceast valoare poate fi util atunci cnd programul este executat n interiorul unei linii de comand a interpretorului de comenzi. Prin convenie, valoarea nul (0) semnific faptul c programul se termin normal iar o valoare nenul (1 sau -1) semnific faptul c s-a produs o eroare. n absena unei instruciuni exit, valoarea implicit a codului returnat la sfritul execuiei unui program este 0.
Gheorghe GRIGORA
diferen este c valoarea transmis prin parametrii actuali &a,&b este adresa lui a respectiv b i prin funcia schimba2 se modific valorile refereniate. Aadar, pentru schimbarea coninutului unor variabile printr-o funcie, parametrul corespunztor trebuie s fie de tip pointer. Argumentele de tip pointer vor fi utilizate de asemenea pentru transmiterea ca parametru a unei variabile de tip structur (vezi Cap.7) precum i pentru limitarea numrului de valori ce trebuiesc puse n stiva de apel a execuiei unui program. n capitolul 6 vom discuta mai pe larg modul de transmitere al parametrilor de tip tablou ca argumente ale unei funcii.
nume - program p1 pn 0
Programul 2
main (int argc, char* argv[ ] ){ if (argc<2) printf ("Nu exista argumente in aceasta executie ! \n"); else { printf ("Argumentele executiei sunt: \n"); for (i 1; i < argc; i++)
22
Programare_ Limbajul_C
printf ("%s", argv [i] ); } } Dac acest program are numele prg2 atunci linia de comand: prg2 produce scrierea mesajului: Nu exista argumente in aceasta executie ! iar linia de comand: prg2 Iata niste argumente !
23
Gheorghe GRIGORA
4.1 Fluxuri i fiiere 4.2 Accesul la fiiere. Tipul FILE 4.3 Funcii de intrare /ieire pentru caractere 4.4 Scrierea cu format 4.5 Citirea cu format
4.1.Fluxuri i fiiere
Sistemul de fiiere al limbajului C este conceput pentru a lucra cu o mare varietate de dispozitive, inclusiv cu terminale, uniti de disc sau de band etc.. Dei fiecare dintre acestea este foarte diferit de celelalte, sistemul de fiiere le transform pe fiecare ntr-un dispozitiv logic numit flux. Fluxurile sunt independente de dispozitivele iniiale , nct aceeai funcie care scrie date pe un fiier de pe disc poate fi folosit pentru a scrie date pe o alt categorie de dispozitive: consola, imprimanta. Aadar dispozitivul fizic pe care se scriu datele se numete fiier iar abstractizarea acestuia se numete flux (sau stream). Exist dou tipuri de fluxuri: Fluxul text este o scven de caractere. Standardul ANSI C permite organizarea unui flux text n linii care se ncheie cu un caracter linie nou(n). Acesta din urm este opional pe ultima linie i este determinat de modul de implementare. Datorit posibilelor conversii s-ar putea ca numrul de caractere scrise/citite s difere de numrul de caractere existente pe dispozitivul extern. Fluxul binar este o secven de octei cu coresponden biunivoc fa de octeii existeni pe dispozitivul extern: numrul de octei scrii/citii este acelai cu numrul acestora de pe dispozitivul extern. Un fiier n limbajul C poate fi un terminal, o imprimant sau o zon (fiier) pe disc. Un flux se poate asocia unui fiier executnd o operaie de deschidere: odat deschis, pot avea loc transferuri de informaii ntre fiier i programul curent n execuie. Unificarea sistemului de intrare/ieire n C se face prin pointerii de fiier. Un pointer de fiier este un pointer ctre informaia care definete diverse caracteristici ale unui fiier(nume, stare, poziie curent etc). Pointerul de fiier identific un anumit fiier i este folosit de ctre fluxul asociat pentru a conduce operaia de intrare/ieire. Pentru a putea utiliza ntr-un program funciile standard de intrare/ieire trebuie scris la nceputul acestui program o directiv "include" care are ca argument numele fiierului antet ce conine definiiile acestor funcii: stdio.h . # include <stdio.h> Orice program C ncepe execuia prin deschiderea unitilor de intrare/ieire standard: stdin stdout stderr stdprn : unitatea standard de intrare : unitatea standard de ie[ire : unitatea de eroare : unitatea standard de ie[ire prn (imprimant\)
24
Programare_ Limbajul_C
Aceste uniti standard sunt asociate, n lipsa altor indicaii, tastaturii calculatorului (intrare) i ecranului (ieire i eroare). Sistemul de fiiere ANSI C este compus din mai multe funcii legate una de alta. Cele mai frecvent folosite dintre acestea sunt date n Tabelul 5 i vor fi prezentate n continuare.
Tabelul 5
NUME fopen( ) fclose( ) putc( ) fputc( ) puts( ) fputs( ) getc( ) fgetc( ) gets( ) fgets( ) fseek( ) printf( ) fprintf( ) scanf( ) fscanf( ) feof( ) ferror( ) rewind( ) remove( ) fflush( ) OPERA}IE Deschide un fiier nchide un fiier Scrie un caracter ntr-un fiier Analog lui putc() Scrie un ir pe ecran Scrie un ir pe fiier Citete un caracter dintr-un fiier Analog lui getc( ) Citete un ir de la tastatur Citete un ir din fiier Caut un anumit octet ntr-un fiier Scrie la ieirea standard Analogul pentru fiiere al funciei printf( ) Citete din intrarea standard Analogul pentru fiiere al funciei scanf( ) Returneaz adevrat dac se atinge sfritul fiierului Returneaz adevrat dac survine o eroare Iniializeaz indicatorul de poziie al fiierului la nceput terge un fiier Golete coninutul unui fiier
4.1. Accesul la fiiere. Tipul FILE Conceptul de baz pentru intrri/ieiri standard este cel de pointer la fiier. Bibliotecile standard C ofer o serie de funcii pentru operaii de intrare/ieire orientate pe fiiere. Dispozitivele standard de intrare (tastatura) i de ieire (ecranul) sunt vzute tot ca fiiere cu pointerii stdin i stdout. Aceste fiiere (stdin, stdout) pot fi orientate i ctre alte dispozitive sau fiiere. Declararea unei variabile de tip "pointer la fiier" se face folosind tipul FILE definit n fiierul antet stdio.h i are forma: FILE* pointer_la_fi[ier unde pointer_la_fiier este un identificator (numele pointerului). Deschiderea unui fiier se face prin funcia fopen care returneaz ca rezultat un pointer la un descriptor de fiier. Acest dispozitiv este gestionat de sistem pentru a asigura intrrile i ieirile efectuate n program.
FILE* fopen(const char* fi[ier,const char* mod)
Aici, fiier este un ir de caractere (identificator) ce desemneaz numele extern al fiierului iar mod este un ir de caractere ce descrie modul de deschidere al fiierului. Modurile posibile de deschidere (deci irurile de caractere ce constituie parametrul lui fopen) sunt date n Tabelul 6.
25
Gheorghe GRIGORA
Tabelul 6.
mod "r" "w" "a" "r+" "w+" "a+" Semnificaia Citire Creare (schimbare) pentru scriere Adugare la sfritul fiierului (existent) Actualizare (citire/scriere) Schimbare pentru actualizare Actualizare cu scriere la sfritul fiierului (existent)
Facem precizarea c, deschiderea unui fiier existent n modurile "w" sau "w+" produce pierderea coninutului su de pn atunci. Dac funcia fopen returneaz o valoare nenul, aceasta indic faptul c deschiderea s-a efectuat corect. Valoarea returnat este un pointer la un descriptor de fiier valid, valoare ce va fi utilizat n apelul la funciile de intrare/ieire. Dac deschiderea nu s-a efectuat corect valoarea returnat este NULL(pointer la informaia nul) . Acest lucru poate fi folosit pentru tratarea erorilor la deschiderea fiierului. nchiderea unui fiier se face prin apelul la funcia fclose: int fclose (FILE* pointer_la_fi[ier) Funcia returneaz EOF n caz de eroare i 0 n caz normal. Prin nchidere se elibereaz descriptorul de fiier, se actualizeaz fiierul pe disc i nceteaz conexiunea logic ntre pointer i fiier. Apelul la fclose se poate face prin:
int cod; FILE* pointer_la_fi[ier cod = fclose (pointer_la_fi[ier)
Apelul la aceast funcie nchide fiierul cu pointerul "pf", deschide fiierul cu numele nume_fiier n modul de acces "mod", atribuind pointerul "pf" la acest fiier. Aceasta permite redireciona-rea dispozitivelor periferice prin program. Programul urmtor ilustreaz acest fapt.
Programul 3
# include <stdio.h> main ( ){ printf ("Mesaj scris pe monitor \n"); if(freopen("iesire.dat","w+",stdout)==NULL) { fprintf (stderr, "Eroare la reasignare \n"); exit (1); } printf ("Mesaj scris in fisierul iesire.dat \n"); fclose (stdout); if (freopen ("CON", "wt", stdout) == NULL) { fprintf (stderr, "eroare la reasignarea lui stdout la CON\n"); exit (1);
26
Programare_ Limbajul_C
} printf ("Mesaj scris din nou pe monitor \n"); } Limbajul C dispune i de posibilitatea: tergerii de fiiere prin funcia remove: int remove(const char* nume_fi[ier); redenumirii unui fiier:
int rename (const char* nume_nou, const char* nume_vechi );
27
Gheorghe GRIGORA
ntreg n notaie zecimal cu semn; ntreg n notaie octal fr semn; ntreg n notaie hexa fr semn; pentru x sau p se folosesc literele a, b, c, d, e, f, iar pentru X literele A, B, C, D, E, F; ntreg n notaie zecimal fr semn real n notaie zecimal sub forma [-]mmm.dddddd sau numrul de d este dat de argumente de precizie (implicit acesta este 6);
28
Programare_ Limbajul_C
e sau E
g sau G c s p n %
real n notaie zecimal sub forma: [-]m.dddddde xx sau [-]m.ddddddE xx. Numrul de d este dat de argumente de precizie (implicit este 6); afiare n acelai stil cu e dac exponentul este -4 sau superior preciziei; n stil f n caz contrar; afiarea unui singur caracter dup conversie n unsigned char; afiarea unui ir de caractere pn la ntlnirea caracterului '@0' (sfrit de ir) sau pn cnd numrul de caractere este egal cu numrul de precizie; afieaz argumentul n notaia pointer; nu exist nici conversie nici afiare n acest caz: este vorba de referine la un pointer la un ntreg n care se va stoca numrul de caractere afiate pn n prezent; Permite afiarea caracterului %
Exemple
Urmtoarele programe ilustreaz folosirea formatelor de afiare la apelul funciei printf rezultatele sunt ilustrate la sfritul fiecrui program.
Programul 4
# include <stdio.h> int main ( ) { int i = 123; long int l = 123456789L; unsigned int ui = 45678U; unsigned long int ul = 987654321UL; double d = 123.45; printf(" Forma zecimala:\n"); printf(" i = %d\n", i); printf(" l = %ld\n", l); printf(" ui = %u\n", ui); printf(" ul = %lu\n\n", ul); printf(" Forma octala:\n"); printf(" ui = %o\n", ui); printf(" ul = %d\n", i); printf(" i = %lo\n\n",ul); In urma execuiei se va afia: Forma zecimala: i = 123 l = 123456789 ui = 45678 ul = 987654321
29
Gheorghe GRIGORA
printf("Scrierea hexazecimala:\n"); printf(" ui = %X\n", ui); printf(" ul = %lX\n\n", ul); printf("Afisarea semnului:\n"); printf("|% d| |%+d| |%d|\n",-123,-123,-123); printf("|% d| |%+d| |%d|\n", 123, 123, 123); printf("Afisare in octal si hexazecimal:\n"); printf("|%x| |%#x|\n", 123, 123); printf("|%X| |%#X|\n", 123, 123); printf("|%o| |%#o|\n", 123, 123); printf("\n"); n urma execuiei se va afia: Scrierea hexazecimala: ui = B26E ul = 3ADE68B1 Afisarea semnului: |-123| |-123| |-123| | 123| |+123| |123| Afisare in octal si hexazecimal: |76| |0x7b| |7B| |0X7B| |173| |0173|
/*Specificarea lungimii campului si a **numarului de cifre*/ printf("|%.4d|\n", 123); printf("|%+.4d|\n", 123); printf("|%.4X|\n", 123); printf("\n"); printf("|%5d| |%-5d|\n", 123, 123); printf("|%+5d| |%+-5d|\n",123, 123); printf("|%#5d| |%#-5d|\n",123, 123); printf("\n"); printf("|%+6.4d|\n", printf("|%+6.4o|\n", printf("|%+6.4X|\n", printf("\n"); 123); 123); 123);
30
Programare_ Limbajul_C
Iat ce se va afia: |0123| |+0123| |0X007B| | 123| |123 | | +123| |+123 | | 0X7B| |0X7B | | +0123| | 0173| |0X007B| /* Afisarea numerelor in virgula flotanta*/ printf("|%f|\n" 3.14157); printf("|%.3f|\n" 3.14157); printf("|%.0f|\n" 3.14157); printf("|%#.0f|\n" 3.14157); printf("|%E|\n" 3.14157e123); printf("|%E|\n" 3.14157e123); printf("|%.3E|\n" 3.14157e123); printf("|%.0E|\n" 3.14157e123); printf("|%#.0E|\n" 3.14157e123); printf("|%f|\t|%G|\n", 3.1, 3.1); printf("|%E|\t|%G|\n", 3.1e10, 3.110); printf("|%G|\t|%G|\n", -0.0001, 0.00001); printf("|%f|\t|%G|\n", 3.1e5, 3.1e7); printf("|%.11G|\t|%.11G|\n", 3.1e6, 3.1e11); return0; } Iat ce se va afia de aceast dat: |3.141570| |3.142| |3| |3.| |3.141570E+123| |1.142E+123| |3E+123| |3.E+123| |3.100000| |3.1| |3.100000E+10| |3.1E+10| |-1.0001| |1E-05| |310000| |3.1E+07| |3100000| |3.1 E+11|
31
Gheorghe GRIGORA
CARACTER d i o u x c
s e, f, g p n
FORMAT DE INTRARE ntreg n notaie zecimal; ntreg n notaie octal(primul caracter 'o') sau hexa (primul caracter 'Ox' sau 'OX'); ntreg n notaie octal; ntreg fr semn n notaie zecimal; ntreg n notaie hexa; caracter. Implicit se citete un singur caracter. Dac este indicat un numr q (%qC) atunci sunt citite cel mult q caractere, citindu-se i spaiile care, de obicei sunt ignorate. Pentru citirea urmtorului caracter diferit de spaiu se folosete %ls. ir de caractere: se citete pn la ntlnirea unui spaiu sau pn la atingerea numrului de caractere indicat; real n notaie virgul flotant; valoarea unei adrese; scrie n argument numrul de caractere citite pn acum. Nu se face nici o operaie de citire;
32
Programare_ Limbajul_C
[...]
[...] %
citete caracterele din intrare att timp ct acestea aparin muli-mii indicate n [ ]. Adaug la argument caracterul '@0' (sfrit de ir); citete caracterele din intrare att ct acestea nu sunt n mulimea indicat n [ ] dup care adaug '@0'; permite indicarea caracterului literal '%'.
char*
char*
Programul 5
#include <stdio.h> int main(){ int i1, i2, i3; char c1, c2, c3; char s1[10],s2[10],s3[10]; char s4[5], s5[5], s6[5]; printf("Introduceti trei numere intregi: "); scanf("%d%d%d", &i1, &i2, &i3); printf("Ati introdus:%d\t%d\t%d\n",i1,i2,i3); printf("Introduceti trei numere intregi: "); scanf("%4d%3d%4d", &i1, &i2, &i3); printf("Ati introdus:%d\t%d\t%d\n",i1,i2,i3); printf("Introduceti un text: "); scanf("%s%s%s, s1, s2, s3"); printf("s1=|%s|\ts2=|%s|\ts3=|%s|\n",s1,s2,s3); printf("Introduceti un text: "); scanf("%4s %4s %4s", s1, s2, s3); printf("s1=|%s|\ts2=|%s|\ts3=|%s|\n",s1,s2,s3); scanf("%c%c%c", &c1,&c2,&c3); printf("c1= |%c|\tc2=|%c|\tc3=|%c|\n"c1,c2,c3); printf("Introduceti un text: "); scanf("%4c%4c%4c", s4,s5,s6); s4[4] =s5[4] =s6[4] ="\0"; printf("s4=|%4s|\ts5=|%4s|\ts6=|%4s|\n",s4,s5,s6)return 0; } Rezultatul unei execuii a acestui program este: Introduceti trei numere intregi: 5454 -9988 0 A-ti introdus numerele: 5454 -9988 0 Introduceti trei numere intregi: 5454998 390 A-ti introdus numerele: 5454 998 390 Introduceti un text: Iata un exemplu! s1=|Iata| s2=|un| s3=|exemplu!|
33
Gheorghe GRIGORA
Introduceti un text: Iata un exemplu! s1=|Iata| s2=|un| s3=|exem| c1=|p| c2=|l| c3=|u| Introduceti un text: Iata un exemplu!: s4=|! Ia| s5=|ta u| s6=|n ex|
34
5.1. 5.2. 5.3. 5.4. 5.5. 5.6. 5.7. 5.8. 5.9. 5.10.
Instruc]iunile if Instruc]iunea switch Instruc]iunea while Instruc]iunea do...while Instruc]iunea for Instruc]iunea break Instruc]iunea continue Instruc]iunea go to Exerci]ii Solu]ii la exerci]ii
Limbajul C ofer\ urm\toarele tipuri de instruc]iuni: declara]ii; expresii; instruc]iuni de control; blocuri de instruc]iuni; defini]ii de func]ii. ~n capitolul 2 am v\zut cum se construiesc expresiile `n C. O expresie urmat\ de caracterul ';' este o instruc]iune `n C. ~n general o instruc]iune se termin\ prin ';'. Urm\toarele construc]ii sunt instruc]iuni C de tip expresii: i = 1; j ++; ++ k; cod = printf ("%d\n", alpha); Un bloc este un [ir de instruc]iuni `ncadrat `n acolade: { [i } (care joac\ rolul de begin ... end din alte limbaje). Un bloc este echivalent cu o instruc]iune. Iat\ un bloc `n C: { i = 1; j = 2; s [i] = t [j]; i ++; j --; s [i] = t [j]; } Instruc]iunile de declarare a variabilelor (declara]iile) sunt de forma(vezi [i paragraful 2.3) : tip identificator; tip identificator = valoare; Vom trece `n revist\ `n continuare instruc]iunile de control ale limbajului C.
Gheorghe GRIGORA
5.1. Instruciunea if
if (expresie) if ( expresie ) instruc]iune1 instruc]iune1 else instruc]iune2 Ca [i `n alte limbaje de programare aceast\ instruc]iune permite alegerea `ntre dou\ alternative. Tipul expresiei de dup\ cuv=ntul rezervat if poate fi `ntreg (char, short, int sau long), real (float sau double) sau tip pointer. Expresia se pune obligatoriu `ntre paranteze iar partea "else instruc]iune2;" este facultativ\. Semantica acestei instruc]iuni este uzual\: dac\ valoarea expresiei "expresie" este nenul\ (ceea ce `nseamn\ "true") atunci se execut\ instruc]iune1 iar dac\ aceast\ valoare este nul\ ("false") se execut\ instruc]iune2 (dac\ aceasta exist\).
Exemple:
if (x>y) x = x-y; else y = y-x; if ((c = getchar ( ) ) ! = EOF) putchar (c); if (x>y) printf("%d este cel mai mare\n", x); else printf("%d este cel mai mare\n", y); if (x>y) printf("primul numar este cel mai mare\n"); else if (x==y) printf("numerele sunt egale\n"); else printf("al doilea numar este mai mare\n"); if ( a < b ) { a += 2; b += 1; }; else a -=b; Aten]ie! ~n acest ultim exemplu este o eroare de sintax\: alternativa else nu poate fi asociat\ cu if pentru c\ `naintea sa ( dup\ } ) apare caracterul ; care reprezint\ instruc]iunea vid\! Apare [i problema ambiguit\]ii (a[a zisa problem\ dangling else): if ( a == 1 ) if ( b == 2 ) printf ( b este egal cu doi\n); else printf ( b nu este egal cu doi\n); C\rui if `i este ata[at else? Limbajul C rezolv\ problema aceasta prin ata[area lui else celui mai apropiat if, deci exemplul este interpretat astfel: if ( a == 1 ) if ( b == 2 ) printf ( b este egal cu doi\n);
36
Programare_ Limbajul_C
5.2. Instruc]iunea switch switch(expresie){ case const1: instruc]iune1 case const2: instruc]iune2 case constn: instruc]iunen default: instruc]iune } Instruc]iunea switch este o structur\ de control cu alegeri multiple ce `nlocuie[te o succesiune de alternative de tip if. Aceasta permite execu]ia unei anumite ramuri `n func]ie de valoarea expresiei "expresie" din parantez\, care poate fi de tip `ntreg, caracter sau enumerare. Dac\ expresia are valoarea const1 atunci se execut\ instruc]iune1 [i toate cele ce urmeaz\ dup\ aceasta p=n\ la `nt=lnirea unei instruc]iuni "break", dac\ valoarea sa este const2 atunci se execut\ instruc]iune2 [i urm\toarele p=n\ la "break" etc. Cazul default este facultativ; dac\ expresia nu are nici una din valorile indicate prin cons1, cons2, ..., se execut\ instruc]iunile desemnate de ramura "default", dac\ aceasta exist\.
Exemplu:
switch (getchar ( ) ) { case 'a': printf ("s-a citit litera a\n"); break; case 'b': printf ("s-a citit litera b\n"); break; case 'c': printf ("s-a citit litera c\n"); break; default: printf ("s-a citit altceva decat a, b sau c\n"); break; } ~n acest exemplu, pe fiecare ramur\ s-a folosit instruc]iunea break. Se `n]elege c\, `n cazul `n care exist\ pe una din ramuri alte instruc]iuni de control, break poate lipsi (vezi exemplul de la insrtuc]iunea for).
37
Gheorghe GRIGORA
Exemplu:
int c, n = 0; while ((c = getchar( )) != EOF){ putchar (c); n++ } int x, y; while (x!=y) { if (x>y) x = x-y; else y = y-x; } Programul urm\tor ilustreaz\ utilizarea instruc]iunii while [i a instruc]iunii switch. Acest program analizeaz\ un text [i num\r\ vocalele, spa]iile goale [i restul caracterelor din acesta.
Programul 6
# include <stdio.h> int main (void){ int nvocale, naltele, nspatii; char c; nvocale = naltele = nspatii = 0; printf ("Introduceti un text\n -->"); while ((c = getchar( )) != '\n') { switch (c) { case 'a': case 'A': case 'e': case 'E': case 'i': case 'I': case 'o': case 'O': case 'u': case 'U': nvocale++; break; case ' ': case '\t': nspatii++; break; default: naltele++; } } printf("In text sunt %d vocale, %d spatii si %d alte caractere.\n",nvocale,nspatii,naltele);
38
Programare_ Limbajul_C
return 0; } Rezultatul unei execu]ii: Introduceti un text: --> Iata un text pentru test ! In text sunt 8 vocale,5 spatii si 13 alte caractere.
Exemplu:
int n = 0, c; do { c = getchar ( ); n++; } while (c!= '\n'); printf ("Linia contine %d caractere \n", n-1); int n = 1; c; c = getchar ( ) while (c!= '\n') { c = getchar ( ); n++ }; printf ("Linia contine %d caractere\n", n-1); Se constat\ u[or c\ cele dou\ secven]e de program sunt echivalente.
Gheorghe GRIGORA
2. Instruc]iunea este echivalent\ cu secven]a: expr1; while (expr2){ instruc]iune expr3; } Instruc]iunea poate fi folosit\ [i prin suprimarea uneia dintre expr1, expr2 sau expr3 sau prin suprimarea corpului ("instruc]iune"): Suprimarea ini]ializ\rii; for( ; expr2 ; expr3) instruc]iune Aceasta este echivalent\ cu secven]a dat\ mai sus (2.) din care lipse[te prima instruc]iune (expr1;)
Exemplu:
i = 0; for(;(c = getchar( )) != '\0'; i++) putchar (c); printf("numarul de caractere:%d\n", i); S\ observ\m c\ ini]ializarea lui i s-a f\cut `n afara instruc]iunii for ! Suprimarea expresiei de control: for( expr ; ; expr3) instruc]iune Aceasta este o bucl\ infinit\. Singura modalitate de a ie[i din aceasta este utilizarea instruc]iunii break.
Exemplu:
for (i = 0; ; c = getchar ( ), i++) if (c == '\n') break; Suprimarea expresiei finale: for( expr1 ; expr2 ; ) instruc]iune
Exemplu:
for (i = 0; (c = getchar( )) != '\0';){ putchar (c) i++ } printf ("numarul de caractere: %d\n", i);
40
Programare_ Limbajul_C
Exemplu:
i=0 for (; (c = getchar( )) != '\0'; { putchar (c) i++ } printf ("numarul de caractere: %d\n", i); Suprimarea corpului buclei: for( expr1 ; expr2 ; expr3 ) ;
Exemplu:
for (i = 0; getchar( )!= '\n'; i++);
Gheorghe GRIGORA
5.8. Instruciunea go to
Instruc]iunea go to provoac\ saltul necondi]ionat la o etichet\, `n cuprinsul acelea[i func]ii. go to eticheta ; Etichetele sunt nume ce se pun `n fa]a instruc]iunilor ]int\ urmate de ":". Este recomandat s\ se evite folosirea instruc]iunii go to (a[a cer principiile program\rii structurate) dar uneori este util\. ~n exemplul urm\tor se ilustreaz\ folosirea instruc]iunii go to [i modul cum ea poate fi evitat\: for (i = 0; i < n; i++) for (j=0; j < m; j++) if (a [i] == b [j] ) go to gasit; printf("Tablourile nu au elemente comune\ n"); . . . gasit: printf (" a[%d]=b[%d]=%d\n",i,j,a [i]); int gasit = 0; for (i = 0; i < n && ! gasit; i++) for (j = 0; j < m &&! gasit; j++) gasit = (a [i] == b [j]); if (gasit) printf (" a[%d]= b[%d]= %d\n",i,j,a[i]); else printf ("Tablourile nu au elemente comune \n");
42
Programare_ Limbajul_C
5.9. Exerciii
jgjj
1. Calculul factorialului.
Scrie]i un program care s\ realizeze urm\toarele: - citirea unui num\r natural (dat de utilizator); - calculul factorialului acestui num\r (iterativ); - afi[area rezultaului. 2. Conversie Scrie]i un program care cite[te un num\r hexazecimal (ca o succesiune de caractere), verific\ validitatea sa [i efectueaz\ conversia `n zecimal. Se va utiliza o itera]ie pentru citirea caracter cu caracter a num\rului, itera]ia se opre[te dac\ se cite[te un caracter ce nu este cifr\ hexazecimal\ [i, pe m\sur\ ce se citesc cifrele hexazecimale se face conversia `n zecimal. Scrie]i dou\ variante ale programului: a) programul con]ine doar func]ia principal\ main ( ) b) func]ia principal\ main face apel la o func]ie de conversie a unui caracter reprezent=nd o cifr\ hexazecimal\ `n valoarea sa zecimal\: int conversie (char x);
4. Prezentare de date
Scrie]i un program care cite[te n valori `ntregi [i le afi[eaz\ c=te l pe fiecare linie, ad\ug=nd la sf=r[itul liniei suma lor. Ultima linie va con]ine suma sumelor, aliniat\ la sf=r[it de linie. Numerele n [i l se definesc ca valori constante.
Gheorghe GRIGORA
printf (" %d ! = %d\", n, fact); return 0; } 2. Conversie Vom construi o bucl\ while care se execut\ at=ta timp c=t ultimul caracter citit este o cifr\ hexazecimal\: 0-9 sau a-f sau A-F. Dac\ este o astfel de cifr\, se converte[te `n valoare `ntreag\, p\str=nd valoarea cumulat\ a num\rului zecimal ob]inut. Prima solu]ie face conversie `n func]ia main ( ) iar a doua solu]ie folose[te o func]ie de conversie. a) /*Citirea unui numar hexa si conversia sa in zecimal*/ # include <stdio.h> int main (void){ int numar = 0; char c; printf (" Introduceti un numar hexa ->"); c = getchar ( ) ; while (( c>= '0') && (c<='9')|| (c>= 'a') && (c<='f')|| (c>= 'A') && (c<='F') { if ((c>= '0') && (c<='9')) numar = numar*16 + (c - '0'); else if ((c>= 'a') && (c<='f')) numar = numar*16 + (c - 'a' + 10); else numar = numar*16 + (c - 'A' + 10); c = getchar ( ); } printf("Conversia zecimala :%d\n", numar); return 0; }
b) /*Citirea unui numar hexa si conversia sa in zecimal*/ # include <stdio.h> int conversie (char x){ return (x>= '0') && ( x<= '9') ? x - '0': ((x>='a')&&(x<='f')?x-'a':x-'A')+10; } int main (void){ int numar = 0; char c; printf (" Introduceti un numar hexa ->"); c = getchar ( ) ; while (( c>= '0') && (c<='9')||
44
Programare_ Limbajul_C
(c>= 'a') && (c<='f ')|| (c>= 'A') && (c<='F') { numar = numar*16 + conversie (c); c = getchar ( ) ; } printf ("Conversia zecimala: %d\n", numar); return 0; }
4. Prezentare de date
/* Se citesc n intregi si se afiseaza cate l pe **linie impreuna cu suma acestora la sfarsit de **linie.Ultima linie contine suma sumelor */ # include <stdio.h> const n = 10; const l = 3; int main (void){
45
Gheorghe GRIGORA
int val, /*ultima valoare intreaga citita */ sumap=0, /*suma din linia curenta*/ nl=0,/*numarul intregilor pe linie */ suma=0, /* suma sumelor par]iale */ nt; /*numarul total de intregi */ printf ("Introduceti%d numere intregi", n); for (nt = 0; nt < n, nt++) { scanf ("%d", &val); ++nl; suma p+= val; printf ("%6d", val); if (nl == l ) { printf (" %6d\n", sumap); nl = 0; suma += sumap; sumap = 0; } } if (nl! = 0) { /*ultima linie este incompleta: se **completeaza cu spatii*/ int i; for (i = nl; i < l; i++) printf (" "); suma += sumap; printf ("%6d\n", sumap); } /* alinierea sumei totale pe ultima linie */ int i; for (i = 0; i < l; i++) printf (" "); printf ("%d\n", suma); return 0 ; }
46
6.1. 6.2. 6.3. 6.4. 6.5. 6.6. 6.7. 6.8. 6.9. 6.10. 6.11. 6.12.
Pointeri Tablouri cu o dimensiune Rela]ia `ntre pointeri [i tablouri Opera]ii aritmetice cu pointeri {iruri de caractere Tablouri cu mai multe dimensiuni Tablouri de pointeri Pointeri [i alocarea memoriei Operatorul sizeof Pointeri c\tre func]ii Exerci]ii Solu]ii
6.1. Pointeri
tip *identificator;
Un pointer este o variabil\ ce con]ine adresa unei alte variabile. Pointerii au tip : aceasta `nseamn\ c\ un pointer nu poate fi folosit dec=t pentru a con]ine adrese de variabile de un singur tip. De exemplu, prin declara]iile: int *pi; char *pc; se specific\ faptul c\ pi este un pointer la o variabil\ de tip `ntreg iar pc un pointer la o variabil\ de tip "char". Operatorul unar "&" numit operator de referen]iere sau adresare, aplicat unei variabile, permite ob]inerea adresei acesteia: int i; pi = &i; Operatorul unar "*", dereferen]iere, indirectare, permite derefen]ierea unui pointer, furniz=nd valoarea variabilei pointate de acesta: int x; x = *pi x = i; Utilizarea operatorului "*" este destul de delicat\, chiar periculoas\ am spune: acesta permite citirea sau scrierea `n orice zon\ de memorie. Utilizarea unui pointer care este ini]ializat gre[it este o eroare des `nt=lnit\ `n programare [i din p\cate consecin]ele sunt imprevizibile.
Gheorghe GRIGORA
O aplica]ie important\ privind utilizarea pointerilor este transferul prin referin]\ al parametrilor unei func]ii. A[a cum s-a precizat la capitolul "Func]ii", `n limbajul C transferul implicit al parametrilor este prin valoare. Dac\ se dore[te modificarea unui parametru formal, trebuie transmis\ func]iei adresa variabilei iar `n corpul func]iei s\ se foloseasc\ operatorul de dereferen]iere. Exemplul clasic pentru acest procedeu este acela al func]iei de interschimbare a dou\ variabile. Forma "clasic\" a func]iei, incorect\ (semantic) `n limbaj C, este: void schimbare (int a, int b) { int temp = a; a = b; b = temp; } Un apel al acestei func]ii (prin schimbare (x, y);) nu are nici un efect asupra variabilelor x,y (actuale) pentru c\ func]ia lucreaz\ cu variabilele ei locale a [i b. Varianta corect\ este: void schimbare_corecta (int *p, int *q){ int temp; temp = *p; *p = *q; *q = temp; } Apelul corespunz\tor este: int x = 1, y = 2; schimbare_corecta (&x, &y);
Un tablou este o colec]ie de elemente(date) de acela[i tip, fiecare din acestea putnd fi accesibil. Declaraia int vector[10]; define[te un tablou cu numele "vector" care are 10 elemente de acela[i tip, `ntreg. A[adar, o declara]ie de tablou cu o dimensiune con]ine tipul tabloului (tip), numele tabloului (nume) - care este un identificator precum [i dimensiunea sa, dimensiune. Dimensiunea este o constant\ `ntreag\ [i specific\ num\rul elementelor tabloului.
48
Programare_ Limbajul_C
Exemple:
1. int a[4] = {1, 2, 3, 4}; char s[4] = {'a', 'b', 'c', '\o'}; 2. # define marime 5; char tab[marime]; tab[0] = 'a'; tab[1] = tab[2] = 'b'; tab[3] = tab[4] = 'd';
Tabloul va avea structura: 'a' 'b' 'b' 'd' 'd' tab[marime - 1] = 'e'; tab[marime/2] = 'c'; Dupa aceste transformari structura sa va fi: 'a' 'b'
'c'
'd'
'e'
Dac\ lista de valori ini]iale (dat\ dup\ declara]ie) cuprinde mai pu]ine elemente dec=t dimensiunea declarat\ a tabloului, atunci celelalte valori se ini]ializeaz\ cu zero. Spre exemplu int a[10] = {5, 2, 3, 1}; are ca efect crearea tabloului: 5 2 3 1 0 0 0 0 0 0 ~n cazul unui tablou care se ini]ializeaz\, poate fi omis\ dimensiunea; aceasta se determin\ de c\tre compilator: float x[ ] = {2, 0, 5.2, 3, 4, 0}; 2 0 5.2 3 4 0 ~n cazul tablourilor de caractere, ini]ializarea se poate face [i cu [iruri de caractere incluse `ntre ghilimele; declara]ia: char s[6] = "abcdef" ; este echivalent\ cu: char s[6] = {'a', 'b', 'c', 'd', 'e', 'f'}; iar declara]ia: char s[ ] = "abcdef" ; este echivalent\ cu: char s[ ] = {'a', 'b', 'c', 'd', 'e', 'f', '\0'}; De notat c\ `n cazul sirurilor de caractere ce se memoreaz\ `ntr-un tablou, ultimul element al tabloului este caracterul \0; acest caracter noteaz\ sf=r[itul [irului( vezi paragraful 6.5). Ini]ializarea unui tablou se poate face [i prin citirea succesiv\ a tuturor elementelor sale: int i;
49
Gheorghe GRIGORA
float x [20]; for (i=0; i < 20; i++) { scanf (" %f", &x[i]); } Este de remarcat faptul c\ nu se face nici o verificare a limitelor unui tablou atunci c=nd se acceseaz\ un element al s\u. Spre exemplu dac\ se declar\ int a[3], b[3], c[3]; [i alocarea se face astfel: a[0] a[1] a[2] b[0] b[1] b[2] c[0] c[1] c[2] atunci expresiile a[4], b[1], c[-2], sunt echivalente, `n sensul c\ ele reprezint\ adresa aceleia[i zone de memorie.
~n limbajul C, numele unui tablou poate fi utilizat ca pointer (constant) la adresa de `nceput a tabloului. De aceea, cu declara]iile de mai sus, este perfect valid\ asignarea pa = tab; care este echivalent\ cu "pa=&tab[0];". Nu sunt valide (e lesne de dedus) expresiile: tab = pa; sau tab ++; A[adar se poate scrie [i: for ( i = 0, pa = tab; i < 10; i++, pa++) *pa = 100; ~n rezumat, echivalen]a `ntre elementele unui tablou [i numele s\u se exprim\ prin:
tablou[i] *(tablou + i) &tablou[i] tablou + i
50
Programare_ Limbajul_C
~n aceast\ declara]ie, dim_1, dim_2, ..., dim_n trebuie s\ fie constante. Un tablou declarat `n acest mod se compune din dim_1*dim_2*...*dim_n elemente de acela[i tip (declarat) stocate `ntr-o zon\ contigu\ de memorie.
51
Gheorghe GRIGORA
Pentru a accesa un element dintr-un tablou exist\ mai multe moduri de notare, `n concordan]\ cu modul de stocare a elementelor tabloului `n memorie. Iat\ de exemplu o declara]ie a unui tablou cu dou\ dimensiuni de valori 3 respectiv 4 (a[adar o matrice cu 3 linii [i 4 coloane). int t[3][4]; O prim\ viziune a reprezent\rii acestuia `n memorie este cea natural\ a unei matrice: t [0][0] t [0][1] t [0][2] t [0][3] t [1][0] t [1][1] t [1][2] t [1][3] t [2][0] t [2][1] t [2][2] t [2][3] Elementeul ha[urat din acest tablou se referen]iaz\ prin t[1][2] (este elementul de pe linia 2 [i coloana 3 din matrice, datorit\ numerot\rii indicilor de la 0). O alt\ viziune (acceptat\ de C) a acestui tablou este aceea a unui tablou cu o singur\ dimensiune `n care fiecare element este la r=ndul s\u un tablou cu o dimensiune: t t[0] t[1] t[2] t [0][0] t [1][0] t [2][0] t [0][1] t [1][1] t [2][1] t [0][2] t [1][2] t [2][2] t [0][3] t [1][3] t [2][3]
Elementul t[1] reprezint\ un tablou cu o dimensiune iar elementul ha[urat este referen]iat prin *(t[1]+3). ~n sf=r[it a treia viziune a tabloului este aceea a unei succesiuni de elemente dispuse `ntr-o zon\ contigu\: t [0][0] t [0][1] t [0][2] t [0][3] t [1][0] t [1][1] t [1][2] t [1][3]
t [2][0] t [2][1] t [2][2] t [2][3] Elementul ha[urat (acela[i ca [i `n cazurile anterioare) se referen]iaz\ prin: *( *t + 6) sau prin nota]ia echivalent\: *( &( t[0][0] + 6)) S\ not\m c\ `n acest caz t este de tip pointer de pointer de `ntregi (dubl\ adresare indirect\). O problem\ important\ relativ la tablourile cu mai multe dimensiuni este cea a ini]ializ\rii acestora. A[a cum referen]ierea unui element al tabloului poate fi f\cut\ `n mai multe moduri, [i ini]ializarea unui tablou se poate face `n moduri diferite (sintactic vorbind). S\ exemplific\m aceste moduri cu referiri la acela[i tablou pe care l-am reprezentat mai sus, t[3][4]: ini]ializarea prin enumerarea tuturor elementelor tabloului ( `n ordinea liniilor dac\-i vorba de matrice): int t[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; ini]ializarea fiec\rei dimensiuni de tablou, separat: int t[3][4] ={{1, 2, 3, 4},{5, 6, 7, 8},
52
Programare_ Limbajul_C
{9, 10, 11, 12} }; ini]ializarea numai a unor elemente din tablou: int t[3][4] = {{1},{5, 6}}; Astfel t [0][0] va fi ini]ializat cu 1, t [1][0] cu 5 iar t [1][1] cu 6; restul elementelor sunt ini]ializate cu zero.
S\ consider\m un tablou de pointeri de tip char, cu numele tabp. O func]ie care s\ afi[eze toate [irurile de caractere (pointate de tabp) la un singur apel ar putea ar\ta astfel: void scrie(char *tabp [ ], int n) { int i; for (i = 0; i < n; i++) if (tabp [i] != NULL) printf ( "%s\n", tabp[i] ); } Apelul acestei func]ii pentru scrierea zilelor s\pt\m=nii este: scrie (zi, 7); S\ mai d\m un exemplu de func]ie care sorteaz\ lexicografic (alfabetic) [irurile pointate de tabp: void sortare (char* tabp[ ], int n){ int i, sortat = 0; char *temp; while (! sortat) { sortat = 1; for (i = 0; i < n-1; i++) if (strcmp (tabp [i], tabp [i+1]) > 0) {
53
Gheorghe GRIGORA
temp =tabp [i]; tabp [i] = tabp [i+1]; tabp [i+1] = temp; sortat = 0; } } } Apelul pentru sortarea alfabetic\ a zilelor s\pt\m=nii este sortare(zi, 7); ~n corpul func]iei sortare s-a folosit un apel la func]ia strcmp care compar\ 2 [iruri de caractere s1 [i s2; rezultatul este negativ dac\ s1 < s2, pozitiv dac\ s1 > s2 [i zero dac\ s1 # s2.
54
Programare_ Limbajul_C
Operatorul sizeof ofer\ dimensiunea `n octe]i a tipului expresiei considerate(tipului respectiv). Dac\ se aplic\ unui tablou, operatorul sizeof ofer\ dimensiunea `n octe]i a memoriei ocupat\ de `ntreg tabloul. Pentru a ob]ine num\rul elementelor tabloului t se utilizeaz\ expresia sizeof t sau sizeof t[0]. S\ not\m `n acela[i timp c\, atunci c=nd este parametrul unei func]ii, un tablou este considerat ca [i un pointer; prin urmare operatorul sizeof nu ofer\ dimensiunea memoriei ocupat\ de tablou ci dimensiunea tipului pointer. Acest lucru se ilustreaz\ `n exemplul urm\tor: double tab [100]; void f(double[ ]); /*declaratia este echivalenta cu void f(double*)*/ int main( ){ int i = sizeof tab; /* i este initializat cu 100 * sizeof(double)*/ f(tab); ... } void f(double t[ ]){ int i = sizeof t; /* i este initializat cu sizeof(double)*/ } Pentru a ob]ine dimensiunea memoriei ocupat\ de un tip anume se folose[te: sizeof(tip); De aceast\ dat\ parantezele sunt obligatorii. Apelul sizeof(long); ofer\ num\rul de octe]i utiliza]i pentru a memora un `ntreg long (este incorect a scrie sizeof long).
tip: este tipul returnat de func]ia pointat\; nume: este identificatorul pointerului la func]ie; lista_arg : este lista argumentelor func]iei pointate. De exemplu declara]ia: char *(*p)(char*,const char*); precizeaz\ c\ p este un pointer la o func]ie de tip char* (pointer la tipul char), are doi parametri, unul de tip char*, cel\lalt de tipul const char*. Este important a se pune *nume `n parantez\ deoarece declara]ia tip *nume (lista_parametri) precizeaaz\ c\ nume este o func]ie de tip tip*.
55
Gheorghe GRIGORA
Un exemplu de utilizare a tipului pointer la o func]ie este func]ia de comparare cmp ce apare ca argument la func]ia de sortare qsort (vezi anexa ): void qsort(const void* baza, size_t n, size_n t, int(*cmp)(const void* val1, const void* val2)) Func]ia qsort este o func]ie de sortare general\ care permite sortarea unui tablou de elemente de tip oarecare(baza) prin specificarea unei func]ii de comparare. Iat\ un program ce utilizeaz\ func]ia qsort : # include <stdio.h> # include <stdlib.h> # include <string.h> enum {n_max = 10}; int tab_int[n_max]; /* tab_int este tabloul ce contine lungimile liniilor **ce trebuie sortat */ char* tab_nume[n_max] = { "Ionescu Ion", "Constantin Vasile", "Ion Gheorghe", "Constantin Mircea", "Constantinescu Ioana", "Vasiliu Catalina", "Vasilescu Maria", "Constantiniu Calin", "Vasile Radu", "Vasilescu Cristi" }; /* tab_nume este tabloul ce trebuie ordonat **lexicografic */ /* se definesc functiile de comparatie */ int comp_int (const void* n1, const void* n2) { return(*(int*)n1- *(int*)n2); } int comp_nume (const void* p1, const void* p2) { return(strcmp(*(char**)p1,*(char**)p2)); } int main(void) { int i; printf("Lista initiala de nume : \n"); for(i = 0; i < n_max; i++) printf("%2d: %s\n",i,tab_nume[i]); for (i = 0, i < n_max; i++) tab_int[i] = strlen(tab_nume[i]); qsort(tab_int, n_max, sizeof(int), comp_int); qsort(tab_nume,n_max,sizeof(char*),comp_nume); printf("Lungimile numelor sortate:\n"); for(i = 0; i < n_max ; i++) printf("%2d: %d\n",i,tab_int[i]); printf("Nume ordonate lexicografic: \n"); for(i = 0; i < n_max; i++) printf("%2d: %s\n",i,tab_nume[i]); return 0; }
56
Programare_ Limbajul_C
6.11. Exerciii
1. A[a cum s-a mai precizat, `n C, [irurile de caractere sunt memorate `n tablouri `n care ultimul element este un delimitator de sf=r[it de [ir: caracterul NULL(\0). ~n anex\ sunt date func]iile din biblioteca standard care se aplic\ [irurilor de caractere. S\ se scrie programe C care s\ implementeze func]iile: a) int strlen(const char*s) : func]ie ce returneaz\ dimensiunea [irului s; b) char* strcpy(char* dest,const char* sursa) : func]ie ce copie [irul sursa `n [irul dest [i returneaz\ dest; c) char* strcat(char* dest,const char* sursa): func]ie ce concateneaz\ [irul sursa la sf=r[itul [irului dest [i returneaz\ valoarea lui dest; d) int strcmp(const char* s1, const char* s2) : func]ie ce compar\ [irurile s1 [i s2 conform ordinii lexicografice. Dac\ are loc rela]ia s1 < s2, se returneaz\ o valoare negativ\, dac\ s1 = s2 se returneaz\ 0 [i dac\ s1 > s2 se returneaz\ o valoare pozitiv\.
6.12. Soluii
1. Implementarea func]iilor de la a) la d) se poate face folosind nota]ia tablou sau aritmetica pointerilor. int strlen(const char *s){ int i = 0; while(s[i++] != '\0') ; return -- i ; } char *strcpy(char* dest, const char *sursa){ int i = 0; while(sursa[i] != '\0') { dest[i] = sursa[i]; i++ } dest[i] = '\0'; return dest; } char *strcat(char *dest, const char *sursa){ while(*dest != '\0') dest++; while(*scr != '\0') *dest++ = *src++; *dest = '\0'; return dest; } char *strcmp(const char *s1, const char *s2){ int i = 0; while((s1[i] == s2[i] && (s1[i] != '\0')) i++; return s1[i] - s2[i]; }
57
Gheorghe GRIGORA
Iat\ [i un program care testeaz\ aceste func]ii. Se folose[te func]ia assert (anexa ) care are ca parametru o expresie logic\ ce descrie o proprietate ce trebuie verificat\. Dac\ evaluarea acestui parametru este valoarea fals (0) atunci assert returneaz\ un mesaj de eroare. # include <stdlib.h> # include <assert.h> int strlen(const char *s) char* strcopy(char *dest, const char *sursa); char* strcat(char *dest, const char *sursa); int strcmp(const char *s1, const char *s2); int main(){ char *inceput = "Iat\"; char *mijloc = "un mic"; char *sfarsit = "exemplu ! "; char *mesaj; int lungime; lungime = strlen(inceput)+ strlen(mijloc) +strlen(sfarsit); assert(lungime==strlen("Iata un mic exemplu ! ")); mesaj = malloc(lung + 1); strcat(strcat(strcopy(mesaj,inceput),mijloc), sfarsit); assert(strcmp(mesaj, "Iata un mic exemplu !")== 0); assert(strcmp(mesaj,inceput) > 0); assert(strcmp(mesaj, "Iat un micut exemplu!") < 0); return 0; }
58
Redenumirea unui tip Tipul structur\ Accesul [i ini]ializarea c=mpurilor unei structuri Structuri autoreferen]iate Tipul enumerare Uniuni Exerci]ii Solu]ii
~n capitolul al doilea au fost introduse tipurile de baz\ ale limbajului C. ~n anumite programe este util s\ regrup\m diferite variabile pentru a exprima o anume rela]ie ce le caracterizeaz\. De pild\ un tablou permite a regrupa o mul]ime de elemente de acela[i tip. ~n acest capitol vom vedea c\ `n C putem construi structuri sau uniuni care permit regruparea elementelor de tipuri diferite `n scopul de a defini un nou tip. De asemenea exist\ posibilitatea de a renumi (redenumi) un anume tip.
Gheorghe GRIGORA
60
Programare_ Limbajul_C
Utilizarea structurilor permite construirea de structuri de date evoluate ca: fi[iere, liste, arbori etc.. De pild\, `ntr-un arbore binar, fiecare nod este compus dintr-o informa]ie (valoarea nodului) [i doi pointeri, unul la subarborele st=ng altul la cel drept. Iat\ cum poate fi definit\ o astfel de structur\: typedef struct { ... } valoare;
61
Gheorghe GRIGORA
/* s-a definit tipul "valoare" pentru **informatia din nodurile arborelui*/ typedef struct Arbore{ valoare val; struct Arbore *stang; struct Arbore *drept; } Arbore; S\ observ\m c\ structura Arbore se autoreferen]iaz\: printre c=mpurile structurii arbore sunt dou\ care sunt de acela]i tip struct Arbore. Din acest motiv structura are un nume: Arbore. Programul urm\tor ilustreaz\ utilizarea unei structuri autoreferen]iate: se construiesc doi arbori binari folosind func]ia adauga [i se afi[eaz\ un arbore `n nota]ia infix (func]ia afisare). Programul Arbori binari. # include <stdio.h> # include <stlib.h> typedef int valoare; typedef struct Arbore{ valoare val; struct Arbore* stang; struct Arbore* drept; } Arbore; Arbore* adauga(valoare v, Arbore* s, Arbore* d); void afisare (Arbore* a); int main( ){ Arbore* a1; Arbore* a2; a1=adauga(1, adauga(2, adauga(3, NULL, NULL), NULL), adauga(4, NULL, NULL)); printf("Primul arbore este: \n"); afisare(a1); a2=adauga(10, a1, adauga(20, NULL, NULL)); printf("\n"); printf("Al doilea arbore este: \n"); afisare(a2); printf("\n"); return 0; } Arbore* adauga(valoare v, Arbore* s, Arbore* d){ Arbore* c; c=malloc (sizeof (Arbore)); c->val = v; c->stang = s; c->drept = d; return c; } void afisare (Arbore* a){ if (a! = NULL){ printf ("(%d", a->val , " "); afisare (a->stang); printf (" "); afisare (a->drept);
62
Programare_ Limbajul_C
printf (")"); } else printf ("nil"); } Execu]ia programului are ca rezultat afi[area urm\toarelor informa]ii: Primul arbore este: (1 (2 (3 nil nil) nil) (4 nil nil)) Al doilea arbore este: (10(1 (2 (3 nil nil) nil) (4 nil nil)) (20 nil nil))
7.6. Uniuni
Uniunile sunt structuri care pot con]ine (la momente de timp diferite), obiecte de tipuri diferite. De fapt, este vorba despre zone de memorie care, la un moment dat con]in un anume tip de variabil\ iar la alt moment acest tip se poate schimba. Sintaxa este similar\ cu cea care define[te o structur\: union nume { declara]ii }; Lista de declara]ii, spre deosebire de cea de la structur\, care definea c=mpurile acesteia, define[te o list\ de alegeri posibile. Uniunea trebuie privit\ ca o structur\ `n care membrii ocup\ aceea[i zon\ de
63
Gheorghe GRIGORA
memorie; dimensiunea memoriei alocat\ pentru o variabil\ de tip uniune este dimensiunea celui mai mare membru al uniunii. S\ not\m c\, la un moment dat doar un membru al uniunii poate ocupa memoria. ~n exemplul urm\tor se specific\ faptul c\ un punct `n plan poate fi definit fie prin coordonatele sale carteziene fie prin cele polare. Typedef union { struct { long abscisa; long ordonata; } cart; struct { float ro; float teta; } pol; } Coordonate; float pi=3.1415926; Coordonate p1, p2; ... printf("%d%d\n",p1.cart.abscisa,p1.cart.ordonata); printf("%f%f\n",p2.pol.ro,p2.pol.teta); O variabil\ de tip Coordonate, cum sunt de pild\ p1 [i p2, poate con]ine fie valori `ntregi reprezent=nd coordonatele carteziene fie valori flotante reprezent\nd coordonatele polare. Desigur c\ `n acest caz se presupune c\ prin program se afl\ c\ p1 este de tip cart iar p2 este de tip pol. Declara]iile precedente pot fi modificate astfel (pentru a memora `n variabil\ `ns\[i tipul de reprezentare ales) : typedef enum {cart=1,pol=2,nec=0} TipCoord; typedef union { TipCoord tip; struct { TipCoord tip; long abscisa; long ordonata; } cart; struct { TidCoord tip; float ro; float teta; } pol; } Coordonate; C=mpul tip este prezent `n toate alternativele uniunii [i el poate fi consultat prin referin]a p1.tip sau p1.cart.tip sau p1.pol.tip. Se mai poate elimina [i aceast\ redondan]\ prin urm\toarea declara]ie: typedef struct { TipCoord tip; union { struct { long abscisa; long ordonata;
64
Programare_ Limbajul_C
} cart; struct { float ro; float teta; } pol; } val; } Coordonate; Dezavantajul este c\ numirea coordonatelor se face printr-o cascad\ mai mare : p1.val.cart.abscisa p1.val.cart.ordonata , dac\ p1.tip=1 p2.val.pol.ro p2.val.pol.teta , dac\ p2.tip=2
7.7. Exerciii
1. Scrie]i un program pentru consultarea unei agende telefonice la nivelul personalului unei societ\]i. Pentru aceasta va trebui s\ se realizeze urm\toarele: 1.1 crearea agendei telefonice sub forma unui fi[ier text. Acest fi[ier va con]ine o mul]ime de linii, fiecare av=nd c=te dou\ c=mpuri: un [ir de caractere ce `nseamn\ identitatea abonatului [i un num\r care corespunde num\rului s\u de telefon. Iat\ un exemplu: andrei 1427 anca 2130 ana 2131 barbu 2140 bogdan 1430 [tefan 2143 victor 2114 zeno 1442 1.2 crearea programului care realizeaz\ urm\toarele: a) ini]ializarea unui tablou de elemente structurate prin citirea fi[ierului ce reprezint\ agenda telefonic\; b) consultarea agendei: la furnizarea unui nume programul ofer\ num\rul de telefon corespunz\tor (dac\ acesta exist\). De exemplu: anca tel.: 2130 anuta persoan\ necunoscut\ [tefan tel.: 2143 quit 2. S\ se scrie un program care s\ realizeze: a) alc\tuirea unei liste de adrese care folose[te un tablou de structur\ pentru stocarea informa]iilor legate de adrese (nume, strada, ora[, codul po[tal); b) completarea tabloului de structuri cu noi adrese; c) afi[area pe ecran a listei de adrese.
65
Gheorghe GRIGORA
7.8. Soluii
1. Vom construi dou\ func]ii pentru c\utarea `n agenda telefonic\: caut\_secv pentru c\utarea secven]ial\ [i caut\_dih pentru c\utarea dihotomic\(binar\). # include <stdio.h> # include <string.h> # define dim_agenda 100 # define lung_nume 20 typedef char sir[lung_nume] typedef struct { sir nume; int tel; } persoana; /* s-a definit o structura de tip persoana*/ persoana agenda[dim_agenda]; /* s-a definit agenda ca un tablou de persoane */ int n=0; /* numarul persoanelor din agenda */ /* urmeaza functia de cautare secventiala */ int cauta_secv(char cineva[ ]) { int compar=1; int i=0; while ((compar>0) && (i<n)) { compar=strcmp(cineva, agenda[i].nume); i++; } return (compar==0)? i-1:-1; } /* urmeaza functia de cautare dihotomica */ int cauta_dih(char cineva[ ]){ int compar=1; int i; int a=0; int b=n-1; while ((compar!=0) && (a<=b)) { i=(a+b)/2; compar=strcmp(cineva,agenda[i].nume); if (compar<0) b=i-1;/* se cauta in prima parte */ else if (compar>0) a=i+1;/* se cauta in a doua parte */ } return (compar==0)? i:-1; } int main(void){ FILE* fisier; sir cineva; int i, fin=0; /* initializarea agendei telefonice */ fisier=fopen("telefon.txt","r"); do {
66
Programare_ Limbajul_C
fscanf(fisier,"%s%d",&(agenda[n].nume), &(agenda[n].tel)); n++; } while (!feof(fisier)); printf("Consultare agenda telefonica\n"); printf("\t Dati prenumele celui cautat \n"); printf("\t sau "quit" pentru sfarsit \n"); while (!fin) { printf(">"); scanf("%s", & cineva); fin=(strcmp(cineva, "quit")==0); if (!fin) { i=cauta_secv(cineva); /* sau i=cauta_dih(cineva); */ if (i==-1) printf("tel.%s:necunoscut!\n",cineva); else printf("tel.%s:%d\n",cineva,agenda[i].tel); } } return 0; } 2. Programul con]ine func]iile: init_lista -ini]ializarea listei; select_meniu -afi[area op]iunilor; introducere -ad\ugarea unui nou nume `n urm\toarea structur\ liber\; gaseste_liber -exploreaz\ tabloul de structuri `n c\utarea unui element nefolosit; sterge -[tergerea unei adrese; afiseaz\ -afi[area listei de adrese.
/* Program pentru listarea unor adrese folosind un **tablou de structuri*/ # include <stdio.h> # include <stdlib.h> # define MAX 100 struct adresa { char nume[30]; char strada[40]; unsigned long int numar; char oras[20]; unsigned long int cod_postal; } adresa_info[MAX]; void init_lista(void), introducere(void); void sterge(void), afiseaza(void); int select_meniu(void), gaseste_liber(void); void main(void){ char optiune; init_lista();
67
Gheorghe GRIGORA
for(;;) { optiune=select_meniu(); switch(optiune) { case 1: introducere(); break; case 2: sterge(); break; case 3: afiseaza(); break; case 4: exit(0); } } } void init_lista(void){ register int t; for(t=0;t<MAX;++t)adresa_info[t].nume[0]= \0; } int select_meniu(void){ char s[80]; int c; printf("1. Introduceti un nume \n"); printf("2. Stergeti un nume \n"); printf("3. Afisati fisierul \n"); printf("4. Iesire din program \n"); do { printf("\n Introduceti optiunea dvs.: "); gets(s); c=atoi(s); } while (c<0 || c>4); return c; } void introducere(void){ int liber; char s[80]; liber=gaseste_liber(); if (liber==-1) { printf("\n Lista este completa. "); return; } printf("Introduceti numele: "); gets(adresa_info[liber].nume); printf("Introduceti numele strazii: "); gets(adresa_info[liber].strada); printf("Introduceti numarul: "); gets(s); adresa_info[liber].numar=strtoul(s, \0,10); printf("Introduceti numele orasului: "); gets(adresa_info[liber].oras); printf("Introduceti codul postal: "); gets(s); adresa_info[liber].cod_postal= strtoul(s, \0,10);
68
Programare_ Limbajul_C
} gaseste_liber(void){ register int t; for(t=0;adresa_info[t].nume[0]&&t<MAX;++t); if (t==MAX) return -1; /* Nu mai este loc in lista*/ return t; } void sterge(void){ register int liber; char s[80]; printf("Introduceti nr. inregistrarii: "); gets(s); liber=atoi(s); if (liber>=0 & liber<MAX) adresa_info[liber].nume[0]= \0; } void afiseaza(void){ register int t; for (t=0; t<MAX; ++t) { if (adresa_info[t].nume[0]) { printf("%s\n",adresa_info[t].nume); printf("%s\n",adresa_info[t].strada); printf("%lu\n",adresa_info[t].numar); printf("%s\n",adresa_info[t].oras); printf("%lu\n", adresa_info[t].cod_postal); } } printf("\n\n"); }
69
Precizarea unui mecanism de memorare Gestiunea automat\ a memoriei Gestionarea register a memoriei Gestionarea static\ a memoriei Variabile globale Declararea variabilelor externe Gestiunea dinamic\ a memoriei Exerci]ii Solu]ii
Limbajul C ofer\ programatorului posibilitatea de a preciza mecanismul pe care-l dore[te pentru memorarea unei variabile. Acest mecanism se refer\ la gestionarea memoriei `n cursul execu]iei programului. Memorarea poate fi: static\, `n segmentul datelor programului; automat\, `n stiva de execu]ie; dinamic\, `n memoria disponibil\; `ntr-un registru al calculatorului. ~n general programatorul nu trebuie s\ se g=ndeasc\ explicit la un mecanism anume de memorare. Compilatorul asociaz\ `n mod automat unei variabile un tip de memorare care corespunde locului unde sa f\cut declararea. ~n acela[i timp `ns\, nu este lipsit de interes ca programatorul s\ cunoasc\ particularit\]ile fiec\rui tip de mecanism de memorare [i de asemenea s\ cunoasc\ modul `n care compilatorul va administra variabilele pe care le declar\ `n program.
Programare_ Limbajul_C
71
Gheorghe GRIGORA
v\zut\ ca o directiv\ de optimizare dat\ compilatorului; acesta ]ine cont de ea at=ta timp c=t exist\ regi[tri disponibili `n ma[in\. Declara]ia register trebuie s\ fie plasat\ `n interiorul unui bloc. Ea este efectiv\ doar pentru variabilele care pot fi codificate pe un cuv=nt-ma[in\: caractere, `ntregi, pointeri. ~n plus, este imposibil a se ob]ine adresa unei astfel de variabile. Valoarea ini]ial\ a unei variabile registru este, implicit, nedefinit\. Exemple de utilizare a unor astfel de variabile sunt `n solu]ia exerci]iului 2 din capitolul precedent.
Variabilele statice sunt alocate `n segmentul de date al programului; durata lor de via]\ este timpul de execu]ie al programului `nsu[i. Implicit sunt ini]ializate cu 0 la `nc\rcarea programului, `nainte de execu]ia func]iei main. ~n cazul tablourilor sau structurilor, ansamblul c=mpurilor va fi ini]ializat cu zero. Vizibilitatea unei astfel de variabile statice depinde de locul `n care a fost declarat\: `n interiorul unui bloc: va fi vizibil\ doar `n blocul acela (la fel ca variabilele auto); `n exteriorul unui bloc: va fi vizibil\ `ncep=nd cu definirea sa p=n\ la sf=r[itul fi[ierului.
72
Programare_ Limbajul_C
} ~n cazul declar\rii func]iilor ce se definesc `n alt modul (fi[ier), cuv=ntul extern este facultativ. Declara]ia: extern void functie(char); este echivalent\ cu: void functie (char); ~n cazul func]iilor important este s\ se descrie signatura sa, adic\ tipul rezultatului [i al parametrilor de apel.
8.8. Exerciii
1. S\ se construiasc\ un program, format din trei module, care s\ implementeze func]iile uzuale aplicate unei stive de caractere: push, pop, top, stiva_vid\, stiva_plin\. Modulele vor fi: -stiva.h, care con]ine declara]iile func]iilor din modulul stiva.c; -stiva.c, care con]ine implementarea func]iilor; -prog.c, care con]ine modulul principal. 2. Defini]i un modul pentru gestionarea unei stive de [iruri de caractere. Caracteristicile modulului sunt definite `n fi[ierul header stiva.h care are con]inutul: # define dimensiune_sir 80 # define dimensiune_stiva 10 typedef char sir[dimensiune_sir]; void push(const sir ch); void pop(sir ch); int stiva_vida(void); int stiva_plina(void);
73
Gheorghe GRIGORA
Utiliza]i func]iile de manipulare a [irurilor de caractere (Anexa ) pentru definirea func]iilor acestui modul. Pornind de la acest modul scrie]i un program test care realizeaz\: a) citirea unui [ir de [iruri de caractere care se vor `mpinge `n stiv\ (se vor stivui); citirea se va face p=n\ la `nt=lnirea sf=r[itului de fi[ier sau p=n\ la umplerea stivei; b) scoate [irurile din stiv\ [i le afi[eaz\ (evident `n ordinea invers\ stivuirii).
8.9. Soluii
1. /* stiva.h : header ce contine declaratiile **functiilor din modulul stiva.c */ void push(char); void pop(void); char top_stiva(void); int stiva_vida(void); int stiva_plina(void); /* stiva.c : modul de gestionare a stivei de **caractere */ # define dim 100; static char stiva[dim]; static int index=0; void push(char c){stiva[index++]=c;} void pop(){--index;} char top_stiva(){return(stiva[index-1]);} int stiva_vida(){return(index==0);} int stiva_plina(){return(index==dim);} /* prog.c : modulul principal */ # include "stiva.h" # include "stiva.c" # include <stdlib.h> # include <stdio.h> int main() { char element; printf("Incarcati stiva: \n>"); while(!stiva_plina()&&scanf("%c",&element)!=EOF){ push(element); printf(">"); } printf("\n"); printf("Iata continutul stivei: \n"); while (!stiva_vida()) { element=top_stiva(); printf("<%c\n",element); pop(); } return 0; } 2. /*stiva.h*/ # define dimensiune_sir 80 # define dimensiune_stiva 10 typedef char sir[dimensiune_sir]; void push(const sir ch); void pop(sir ch); int stiva_vida(void); int stiva_plina(void); /*modul.c
74
Programare_ Limbajul_C
** Gestionarea unei stive de siruri de caractere. */ # include <stdio.h> # include <assert.h> # include <string.h> # include "stiva.h" /* ** variabile cu clasa de memorare *static*, **nevizibile in alte fisiere. */ static int index=0; /* indicile top-ului stivei */ static sir stiva[dimensiune_stiva]; void push(const sir c){ /* Se copie c in stiva dupa care se incrementeaza **index. Inainte de aceasta se apeleaza functia **assert care impune o conditie pentru a continua **executia: stiva sa nu fie plina. */ assert(!stiva_plina()); strcpy(stiva[index],c); index++; } void pop(sir c){ /* se decrementeaza index apoi se copie top-ul **stivei in c */ assert(!stiva_vida()); --index; strcpy(c,stiva[index]); } int stiva_vida(void){ return(index==0);} int stiva_plina(void){ return(index==dimensiune_stiva); } /* test.c : programul de test ce consta din doua **iteratii: prima pentru citirea din fisierul de **intrare si stivuirea informatiei, a doua pentru **afisarea rezultatului destivuirii. */ # include <stdio.h> # include "stiva.h" int main(void){ sir x; printf("Se incarca stiva: \n>"); while(!stiva_plina()&&scanf("%s",x)!=EOF) { push(x); printf(">"); } printf("\n"); printf("Iata continutul stivei: \n"); while (!stiva_goala()) { pop(x); printf("<%s\n",x); } return 0; }
75
9.1 Mecanisme de conversie 9.1.1. Conversia la asignare 9.1.2. Evaluarea expresiilor aritmetice 9.1.3. Evaluarea expresiilor logice 9.1.4. Conversii explicite 9.1.5. Conversii de pointeri. Pointerul universal.
Conversia de tip `n limbajul C ofer\ un mecanism simplu pentru evaluarea expresiilor aritmetice [i pentru transmiterea parametrilor. Trebuie s\ facem distinc]ia `ntre dou\ maniere de conversie: aceea care are drept scop schimbarea interpret\rii con]inutului unei variabile [i conversia care are drept consecin]\ modificarea con]inutului unei variabile. ~n primul caz de pild\, se poate interpreta con]inutul unei variabile de tip caracter ca fiind o valoare `ntreag\ [i drept urmare posibilitatea utiliz\rii acesteia `ntr-o expresie aritmetic\. ~n al doilea caz se poate converti o valoare real\ la o valoare `ntreag\; ca o consecin]\ se pierde valoarea frac]ionar\ a variabilei reale.
Programare_ Limbajul_C
float f2=14/5; /* f2=2.0 */ apelul unei funcii: printf(%d\n,z); /*caracterul z este convertit la un intreg */ ~n cazul `n care se utilizeaz\ operatori de conversie, compilatorul este cel care gireaz\ aceste conversii. Conversiile `ntre tipurile de baz\ ale limbajului sunt considerate permise iar tratamentul la care sunt supuse datele de c\tre compilator este bine stabilit de regulile limbajului. Alte conversii `n care intervin tipuri definite de programator sunt considerate ilicite [i sunt respinse de compilator. Vom detalia `n continuare aceste mecanisme.
77
Gheorghe GRIGORA
Dac\ un operand este: double unsigned long long unsigned Exemple: i (int) int int i (int) Faza 1 Faza 2 Rezultat Faza 1 Faza 2 Rezultat +
Cel\lalt operand se converte[te la: double unsigned long long unsigned + c (char) int int + b) (float) double double
Programare_ Limbajul_C
pentru transmiterea valorilor cu tipurile cerute ca parametri `n func]ii; schimbarea tipului valorii returnate de o func]ie. Exemple: int a=21; b=4; float f; double r; f=a/b; /* f5.0; */ f=(float)a/b; /* f=5.25; */ f=a/(float)b; /* f=5.25; */ .... r=sqrt(double(a)); .... a=(int)ceil(r); /*functia ceil returneaza o valoare de tip intreg*/
79
Cap 10 PREPROCESORUL
10.1.4 Directiva #error 10.2 Macroinstruc]iuni 10.2.1 Macroinstruc]iuni predefinite 10.2.2 Macroinstruc]iuni tip func]ie
~n codul surs\ al unui program C se pot include instruc]iuni destinate compilatorului; acestea se numesc directive pentru preprocesor. Preprocesorul realizeaz\ o faz\ de precompilare care are ca obiect: includerea unor fi[iere (specificate) `n program; compil\ri condi]ionate conform unor parametri (valori) transmise `n comanda de compilare; definirea unor constante simbolice; definirea macroinstruc]iunilor. Programatorul asociaz\ o secven]\ de instruc]iuni unui identificator. La apari]ia acestui identificator `n program, preprocesorul substituie secven]a corespunz\toare de instruc]iuni `n locul acestui identificator. Precompilarea este o faz\ de prelucrare independent\ de compilarea propriu-zis\. ~n acela[i timp `ns\, ea face parte integrant\ din procesul de compilare: apelul compilatorului pentru un program execut\ mai `nt=i `nc\rcarea preprocesorului. Rezultatul activit\]ii preprocesorului este un fi[ier surs\ `n care sunt tratate toate directivele de precompilare [i care este transmis compilatorului.
Programare_ Limbajul_C
Gheorghe GRIGORA
# include <math.h> .... val=pow(M_PI, (double)n); /* se calculeaza puterea a n-a a lui */ ~n fi[ierul math.h se afl\ defini]ia constantei M_PI [i a func]iei pow( ).
Programare_ Limbajul_C
10.2 Macroinstruiuni
Prin acest mecanism programatorul asociaz\ o secven]\ de instruc]iuni unui identificator.
Aceste macroinstruc]iuni utilizator se definesc cu directiva #define: # define nume_macro secven]\_caractere Aici, nume_macro poate fi un identificator ce reprezint\ numele macroinstruc]iunii. Acesta, utilizat `ntr-un text surs\, va fi `nlocuit cu secven]\_caractere care poate `nsemna o secven]\ de instruc]iuni C (vezi [i 10.1.1). Dar nume_macro poate fi [i numele unei func]ii urmat\ de parametri: prin aceasta se define[te o macroinstruc]iune de tip func]ie care poate fi apelat\ `n orice punct al programului `n care a fost definit\.
Exemplu:
# define <stdio.h> # define ABS(a) (a)<0?-(a):(a) void main(void){ printf(Valoarea absoluta a lui -1 si 1: %d%d,ABS(-1),ABS(1)); } Folosirea func]iilor definite ca macroinstruc]iuni are, pe de o parte, un avantaj major: m\rirea vitezei de execu]ie a codului fiindc\ nu mai apare nici o `nc\rcare suplimentar\ legat\ de apelarea func]iei. Pe de alt\ parte `ns\, dac\ dimensiunea macroinstruc]iunii tip func]ie este mare, acest supliment de vitez\ este anihilat de cre[terea dimensiunii programului, datorit\ codului care apare de mai multe ori.
83
Cap 11 EXERCIII
1. S\ se scrie un program numit maxmin.c care s\ execute : a. citirea unui intreg pozitiv n; b. citirea a n numere reale; c. determinarea valorilor maxim [i minim dintre cele n numere citite; 2. S\ se scrie un program numit sumedia.c care s\ citeasc\ niste numere reale [i, pentru fiecare s\ scrie un r=nd `ntr-un tabel ce are forma: Nr.crt. Num\r Min . Max . Suma . Media .
unde Min(Max) `nseamn\ cel mai mic(mare) dintre numerele citite p=n\ `n acel moment, Suma [i Media fiind suma (media) numerelor citite p=n\ `n acel moment
3. S\ se scrie un program care s\ numere literele mici, literele mari , cifrele [i celelalte caractere(la un loc) dintr-un text(fi[ier). 4. S\ se scrie func]ii recursive pentru calculul factorialului unui num\r dat n, a puterilor 1,2 , k a unui num\r n, a sumei numerelor naturale m [i n. S\ se foloseasc\ aceste func[ii `ntr-un program principal. 5. S\ se scrie o func]ie int este_prim(int n) care s\z returneze 1 dac\ num\rul n este prim [i zero `n caz contrar. S\ se scrie o func]ie fibonacci ( int n) care s\ returneze al n-ulea termen al [irului lui Fibonacci. S\ se foloseasc\ cele dou\ func]ii pentru a verifica dac\ al nulea termen din [irul lui Fibonacci este num\r prim ( n = 1, , 12). 6. Folosind func]ia este_prim( ) de la exerci]iul precedent, scrie]i un program ce s\ verifice conjectura lui Goldbach pentru toate numerele pare dintr-un interval dat [a,b] : Orice num\r par n mai mare ca 2 este suma a dou\ numere prime. De exemplu: 700 = 17 + 683 , 702 = 11 + 691, 704 = 3 + 701, etc. 7. Urm\toarea func]ie calculeaz\, recursiv, cel mai mare divizor comun a dou\ numere `ntregi pozitive p [i q: int cmmdc_r(int p, int q) { int r; if (( r = p % q ) == 0 ) return q; else return cmmdc_r(q, r); } Scrie]i un program pentru testarea acestei func]ii precum [i pentru calculul celui mai mare divizor comun a n numere date. Scrie]i apoi [i testa]i varianta iterativ\ a func]iei, cu numele : int cmmdc_i (int p, int q) 8. Descrie]i [i implementa]i trei algoritmi de sortare. 9. Construi]i o func]ie int numar_cuvinte(const char *s) care s\ returneze numarul cuvintelor din [irul s ( cuvintele sunt desp\r]ite prin spa]iu/spa]ii). S\ se testeze aceast\ func]ie pentru un [ir anume(fi[ier).
Programare_ Limbajul_C
10. S\ se scrie [i apoi s\ se testeze func]ii pentru adunarea a doi vectori [i pentru adunarea a dou\ matrice. 11. S\ se scrie un program `n care s\ se foloseasc\ func]ia sistemului : void qsort(void *array, size_t n_els,size_t el_size, int compare(const void *, const void *)) pentru a sorta o list\ de numere reale `n ordinea cresc\toare relativ la p\r]ile frac]ionare a acestor numere. De exemplu lista: 2.43, 45.01, 23.90, 123.04, 0.98 este sortat\ astfel: 45.01, 123.04, 2.43, 23.90, 0.98 12. S\ se scrie func]iile care implementeaz\ o stiv\ `n C(push, pop, top, etc). S\ se foloseasc\ acestea pentru a verific\ dac\ un [ir de caractere format numai din literele a, b, c este palindrom cu centrul c: [irurile: aabacabaa , bababbcbbabab, c, aca, bbcbb sunt palindroame pe c=nd [irurile: aabbcbb. ccab, aaaaaaaacaaaabb, acb, aaaa nu sunt palindroame. 13. Fie a un tablou bidimensional (m x n). Un punct sa al acestui tablou este un element a[i0,j0] cu proprietatea: a[i0,j0] = min{a[i0,j] : 0<=j<=n-1} = max{a[i,j0] : 0<=i<=m-1} Scrieti un program care determina punctele sa (daca exista) ale unui tablou bidimensional. 14. Se da o matrice patratica. Sa se ordoneze crescator fiecare linie a matricii, apoi sa se rearanjeze liniile astfel incat suma elementelor de pe diagonala sa fie minima.
15. Scrieti un program care, avand la intrare doua siruri de caractere, determina cel mai lung subsir comun si pozitiile la care acesta incepe in fiecare dintre cele doua siruri. 16. Proiectati structuri de date pentru reprezentarea unui punct, a unui triunghi, dreptunghi, cerc. Scrieti proceduri de citire si scriere a unui punct, triunghi etc. Scrieti o procedura care determina, pentru un triunghi dat, numarul punctelor de coordonate intregi aflate in interiorul sau. Scrieti o procedura similara pentru cerc. 17. Proiectati o structura de date pentru reprezentarea datei calendaristice si scrieti subprograme care: verifica daca valoarea unei variabile din structura este o data valida. calculeaza data calendaristica care urmeaza unei anumite date. calculeaza data calendaristica care precede o anumita data.
18. Sa se proiecteze o structura de date pentru reprezentarea multimilor finite de numere complexe si sa se scrie proceduri care realizeaza operatii cu multimi de numere complexe reprezentate astfel.
85
Gheorghe GRIGORA
BIBLIOGRAFIE
1. Herbert Schildt C - Manual Complet, Bucuresti, Ed. Teora 1998
2. Liviu Negrescu Limbajele C si C++ pentru incepatori, vol I- III, Cluj-Napoca Volumul I - Limbajul C Volumul II - Limbajul C++ Volumul III - Limbajele C si C++ in Aplicatii 3. Cristea,V.,C.Giumale, E.Kalisz. A.P\nsiu, Limbajul C standard. Editura Teora, 1992. 4. Grigora[, Gh. Programarea calculatoarelor:Fundamente, Editura Spiru Haret, Ia[i, 1999. 5. P\tru], B., Aplica]ii `n C [i C++, Editura Teora, Bucure[ti, 1998. 6. Musc\, Gh. [i al]ii, Informatic\ aplicat\, manual pentru licee de informatic\, cls.XI, Editura Didactic\, Bucure[ti 1998
86
ANEXA
Biblioteca standard C
FI{IERE HEADER
1. Fi[ierul <assert.h> con]ine macro-ul assert() void assert (int expr); Dac\ expr este zero, este tip\rit un mesaj(diagnostic) [i se iese din program. 2. Fi[ierul <ctype.h> con]ine macrouri (func]ii) pentru testarea caracterelor: int isalnum(int c); int isalpha(int c); int iscntrl(int c); int isdigit(int c); int isgraph(int c); int islower(int c); int isprint(int c); int ispunct(int c); int isspace(int c); int isupper(int c); int isxdigit(int c); /* c este alfanumeric? */ /* c este alfabetic? */ /* c este car. de control? */ /* c este cifra? */ /* c este car. grafic? */ /* c este litera mica? */ /* c este tiparibil? */ /* c este car. punctuatie? */ /* c este spatiu? */ /* c este litera mare? */ /* c este cifra hexa? */
Mai con]ine: int tolower (int c) [i int toupper ( int c) funcii ce permit transformarea literelor din mari ]n mici sau invers. 3. Fi[ierul <errno.h> con]ine macrouri folosite pentru raportarea erorilor. 4. Fi[irerul <float.h> con]ine defini]iile unor constante pentru limitele unor valori flotante: DBL_MAX, FLT_MAX, DBL_MIN, DBL_EPSILON etc. 5. Fi[ierul <limits.h> con]ine defini]iile unor constante ce caracterizeaz\ `ntregii: CHAR_BIT, CHAR_MAX, INT_MAX etc.
Gheorghe GRIGORA
6. Fi[ierul <locale.h> con]ine construc]ii (structuri, func]ii) ce permit setarea unor propriet\]i locale: forma punctului zecimal, informa]ii monetare etc. 7. Fi[ierul <math.h> con]ine prototipuri pentru func]iile matematice: double cos ( double x); double acos ( double x); double sin ( double x); double asin ( double x); double tan ( double x); double atan ( double x); double cosh ( double x); double exp ( double x); double sinh ( double x); double log ( double x); double tanh ( double x); double log10 ( double x); double ceil ( double x); double flor ( double x); double fabs ( double x); double sqrt ( double x); double atan2 ( double y, double x); double fmod ( double x, double y); double pow ( double x, double y);
8. Fi[ierul <setjmp> con]ine declara]ii [i prototipuri ce permite programatorului s\ utilizeze salturi nelocale. 9. Fi[ierul < signal.h> con]ine construc]ii ce permit programatorului s\ m=nuiasc\ condi]ii sau semnale excep]ionale. 10. Fi[ierul <stdarg.h> con]ine o redefinire (typedef) de tip [i trei m macrouri ce permit scrierea de func]ii cu un num\r variabil de argument ( ca [i printf ). 11. Fi[ierul <stddef.h> con]ine defini]ii de tip [i macrouri ce se folosesc `n alte defini]ii: typedef typedef typedef #define char wchar_t; /* tipul wide character*/ int ptrdiff_t; /*tipul diferen]\ de pointeri */ unsigned size_t; /*tipul ob]inut din sizeof() */ NULL;
12. Fi[ierul <stdio.h> con]ine macrourile, defini]iile de tip [i prototipurile func]iilor utilizate de programator pentru a accesa fi[iere. Sunt definite aici constantele BUFSIZ, EOF, FILENAME_MAX, FOPEN_MAX, NULL, TMP_MAX. De asemenea sunt defini]i pointerii stdin, stdout, stderr [i pentru structura FILE. Func]ii pentru accesul la fi[iere:
88
FILE *fopen (const char *filename, const char *mode); int fclose (FILE *fp); int fflush (FILE *fp); FILE *tmpfile (void); int fseek (FILE *fp, long offset, int place); /* Pozi]ioneaz\ indicatorul pentru urm\toarea opera]ie `n fi[ier la place + offset */ long ftell (FILE *fp); /* Returneaz\ valoarea indicatorului de pozi]ie a fi[ierului pointat de fp*/ void rewind (FILE *fp); /* Seteaz\ indicatorul la `nceputul fi[ierului */ int fsetpos (FILE *fp, const fpos_t *pos); /* Seteaz\ indicatorul la valoarea pos */ int rename (const char *from, const char *to); De asemnea fi[ierul mai con]ine prototipurile func]iilor de intrare/ie[ire (vezi cap.4).
Programare_ Limbajul_C
13. Fi[ierul <stdlib.h> con]ine prototipurile func]iilor de uz general : alocarea dinamic\ a memoriei, c\utare binar\, sortare, generare de numere aleatoare, comunicarea cu sistemul, conversie de stringuri,etc. Iat\ unele dintre ele: void *calloc(size_t n, size_t el_size); void *malloc(size_t size); void *realloc(void *ptr, size_t size); void free(void *ptr); void *bsearch(const void *key_ptr, const void *a_ptr, size_t n_els, size_t el_size, int compare(const void *, const void *)); void qsort(void *a_ptr, size_t n_els, size_t el_size, int compare(const void *, const void *)); int rand(void); void srand(unsigned seed); int abs(int i); long labs(long i); div_t div(int numer, int denom); ldiv_t ldiv(long numer, long denumer); double atof(const char *s); int atoi(const char *s); long atol(const char *s); double strtod(const char *s); long strtol(const char *s, char **end_ptr, int base); void abort(void); void exit(int status);
14. Fi[ierul <string.h> con]ine prototipuri de func]ii pentru manipularea blocurilor de memorie sau a [irurilor: void memcmp(const void *p, const void *q, size_t n); void *memcpy(void *to, void *from, size_t n); void *memmove(void *to, void *from, size_t n); void *memset(void *p, int c, size_t n); char *strcat(char *s1, const char *s2); /*concatenare*/ char *strchr(const char *s, int c); /*cauta c `n [irul s*/ int strcmp(const char *s1, const char *s2); /* comparare [iruri*/ char *strcpy(char *s1, const char *s2) /* copie s2 `n s1 */ size_strlen(const char *s); /*lungimea [irului s */ char *strncat(char *s1, const char *s2, size_t n); /*concatenarea a cel mult n caractere din s1 la s2*/ int strncmp(const char *s1, const char *s2, size_t n); /* comparare a cel mult n caractere din [irurile s1 [i s2*/ char *strncpy(char *s1, const char *s2, size_t n) /* copie cel mult n caractere din s2 `n s1 */ char *strstr(const char *s1, const char *s2); /* caut\ `n s1 prima apari]ie a lui s2 */ char *strtok(char *s1, const char *s2); /* caut\ tokenuri `n s1 folosind caracterele din s2 ca separatori */ 15. Fi[ierul <time.h> con]ine prototipuri ale func]iilor pentru ob]inerea datei, orei ]i cele privind ceasul intern al calculatorului. Se folose[te structura tm care este definit\ astfel:
89
Gheorghe GRIGORA
struct tm { int tm_sec; /*secunde 0..60*/ int tm_min; /*minutr 0..59 */ int tm_hour; /*ore 0..23 */ int tm_mday; /*ziua lunii 1..31 */ int tm_mon; /*luna anului 0..11 */ int tm_year; /*anul de la 1900 */ int tm_wday; /* zilele de la 0 la 6 */ int tm_yday; /*zilele din an 0..365 */ int tm_isdst; /* Indicator de timp */ }; Func]ii: typedef long clock_t; typedef long time_t; clock_t clock(void); /*returneaz\ num\rul de tacturi CPU utilizate de program p=n\ aici*/ time_t time(time_t *tp); /*returneaz\ nr. de secunde trecute de la 1 Inuarie 1970 */ char *asctime(const struct tm *tp); /*converte[te timpul inregistrat [i pointat de tp `ntr-un string ce reprezint\ data*/ char *ctime (const time_t *t_ptr); ?8 converte[te timpul pointat de t_ptr la string - data*/ struct tm *localtime(const time_t *t_ptr); De exemplu: time_t acum; acum = time(NULL); printf("%s\n%s ", ctime(&acum), asctime(localtime(acum))); .. are ca efect scrierea datei calenderistice.
Codul ASCII
American Standard Code for Information Interchange 0 1 2 3 4 5 6 7 8 9 0 nul soh stx etx eot enq ack bel bs ht 1 nl vt np cr so si dle dc1 dc2 dc3 2 dc4 nak syn etb can em sub esc fs gs 3 rs us sp ! # $ % & 4 ( ) * + , . / 0 1 5 2 3 4 5 6 7 8 9 : ; 6 < = > ? @ A B C D E 7 F G H I J K L M N O 8 P Q R S T U V W X Y 9 Z [ \ ] ^ _ a b c 10 d e f g h i j k l m 11 n o p q r s t u v w 12 x y z { | } ~ del
90
Programare_ Limbajul_C
Observa]ii: Caracterul G este `n linia 7 [i coloana 1 deci are codul ASCII 71; Caracterele cu codurile 0-31 [i 127 nu sunt tip\ribile; Caracterul cu codul 32 este spa]iu (gol) [i se tip\re[te ca [i un spa]iu gol; Codurile caracterelor 0-9, A - Z [i a - z sunt contigue; Diferen]a `ntre codurile literelor mari [i cele mici corespunz\toare este 32; ~n tabela urm\toare se da semantica unora din caracterele de pe liniile 0-2.
bel bs cr esc
~n]elesul unor abrevieri audible bell ht tab orizontal backspase nl linie nou\ retur car nul null escape vt tab vertical
~n sistemele UNIX, comanda man ascii produce afi[area tabelei codurilor ASCII pe ecran.
91