# Gh.

GRIGORAŞ

Programare C

CUPRINS
Cap 1 INTRODUCERE ÎN LIMBAJUL C.............................................................................................................................. 1 1.1 Scurt istoric................................................................................................................................................................... 1 1.2 Forma unui program C ................................................................................................................................................. 3 1.3 Compilarea unui program C ......................................................................................................................................... 5 Cap 2 TIPURI, OPERATORI, EXPRESII............................................................................................................................... 7 2.1. Tipuri simple ............................................................................................................................................................... 7 2.2. Literali ......................................................................................................................................................................... 8 2.3. Declararea unei variabile şi a unei constante .............................................................................................................. 8 2.4. Operatori.................................................................................................................................................................... 10 2.4.1. Operatori aritmetici .......................................................................................................................................... 10 2.4.2. Operatorii relaţionali (de comparaţie).............................................................................................................. 11 2.4.3. Operatori logici ................................................................................................................................................ 11 2.4.4. Operatori la nivel de bit.................................................................................................................................... 11 2.4.5. Operatorul de atribuire ..................................................................................................................................... 12 2.4.6. Operatorul condiţional ..................................................................................................................................... 13 2.4.7. Operatorul virgulă ............................................................................................................................................ 13 2.4.8. Precedenţa operatorilor .................................................................................................................................... 13 Cap 3 FUNCTII...................................................................................................................................................................... 15 3.1. Definirea unei funcţii ................................................................................................................................................ 15 3.2. Returnarea unui apel.................................................................................................................................................. 16 3.3. Funcţii cu un număr variabil de parametri ................................................................................................................ 16 3.4. Sfârşitul execuţiei unui program ............................................................................................................................... 16 3.5. Apelul şi transmiterea parametrilor........................................................................................................................... 16 3.6. Funcţia principală main ............................................................................................................................................. 17 Cap 4 FUNCŢII DE INTRARE IEŞIRE................................................................................................................................ 18 4.1. Fluxuri şi fişiere......................................................................................................................................................... 19 Semnificaţia ...................................................................................................................................................................... 20 4.2. Funcţii de intrare/ieşire pentru caractere................................................................................................................... 22 4.3. Scrierea cu format...................................................................................................................................................... 22 4.5. Citirea cu format........................................................................................................................................................ 26 Cap 5 INSTRUCŢIUNI DE CONTROL ............................................................................................................................... 29 5.1. Instrucţiunea if........................................................................................................................................................... 29 5.2. Instrucţiunea switch................................................................................................................................................... 31 5.3. Instrucţiunea while .................................................................................................................................................... 31 5.4. Instrucţiunea do ... while ........................................................................................................................................... 32 5.5. Instrucţiunea for ........................................................................................................................................................ 33 5.6. Instrucţiunea break .................................................................................................................................................... 35 5.7. Instrucţiunea continue ............................................................................................................................................... 35 5.8. Instrucţiunea go to ..................................................................................................................................................... 35 5.9. Exerciţii ..................................................................................................................................................................... 36 Cap 6 TABLOURI ŞI POINTERI.......................................................................................................................................... 37 6.1. Pointeri ...................................................................................................................................................................... 37 6.2. Tablouri cu o dimensiune.......................................................................................................................................... 38

i

Gh. GRIGORAŞ

Programare C

6.2.1. Referenţierea unui element al tabloului ........................................................................................................... 38 6.2.2. Iniţializarea unui tablou.................................................................................................................................... 38 6.3. Relaţia între pointeri şi tablouri................................................................................................................................. 39 6.4. Operaţii aritmetice cu pointeri................................................................................................................................... 40 6.5. Şiruri de caractere...................................................................................................................................................... 40 6.6. Tablouri cu mai multe dimensiuni............................................................................................................................. 41 6.7. Tablouri de pointeri ................................................................................................................................................... 42 6.8. Pointeri şi alocarea memoriei .................................................................................................................................... 43 6.9. Operatorul “sizeof” ................................................................................................................................................... 44 6.10. Pointeri la funcţii ..................................................................................................................................................... 44 6.11. Exerciţii ................................................................................................................................................................... 45 Cap 7 CONSTRUIREA DE NOI TIPURI ............................................................................................................................. 47 7.1. Redenumirea unui tip ................................................................................................................................................ 47 7.2. Tipul structură .......................................................................................................................................................... 47 7.3. Accesul şi iniţializarea câmpurilor unei structuri ..................................................................................................... 48 7.4. Structuri autoreferenţiate........................................................................................................................................... 49 7.5. Tipul enumerare......................................................................................................................................................... 50 7.6. Uniuni ........................................................................................................................................................................ 51 7.7. Exerciţii ..................................................................................................................................................................... 52 Cap 8 GESTIUNEA MEMORIEI.......................................................................................................................................... 54 8.1. Precizarea unui mecanism de memorare ................................................................................................................... 54 8.2. Gestiunea automată a memoriei ................................................................................................................................ 55 8.3. Gestionarea “register” a memoriei ............................................................................................................................ 55 8.4. Gestionarea statică a memoriei.................................................................................................................................. 55 8.5. Variabile globale ....................................................................................................................................................... 56 8.6. Declararea variabilelor externe ................................................................................................................................. 56 8.7. Gestiunea dinamică a memoriei ................................................................................................................................ 57 8.8. Exerciţii ..................................................................................................................................................................... 57 Cap 9 CONVERSIA DE TIP ................................................................................................................................................. 58 9.1. Mecanisme de conversie .......................................................................................................................................... 58 9.1.1 Conversia la asignare ........................................................................................................................................ 58 9.1.2 Evaluarea expresiilor aritmetice ................................... 59 9.1.3. Evaluarea expresiilor logice............................................................................................................................. 60 9.1.4 Conversii explicite............................................................................................................................................. 60 9.1.5 Conversie de pointeri. Pointerul universal........................................................................................................ 60 Cap 10 PREPROCESORUL .................................................................................................................................................. 62 10.1 Directive preprocesor ............................................................................................................................................... 62 10.1.1 Constante simbolice. Directiva #define .......................................................................................................... 63 10.1.2 Includerea fişierelor......................................................................................................................................... 63 10.1.3 Compilare condiţionată ................................................................................................................................... 64 10.1.4 Directiva #error ............................................................................................................................................... 64 10.2 Macroinstrucţiuni ..................................................................................................................................................... 64 10.2.1 Macroinstrucţiuni predefinite.......................................................................................................................... 65 10.2.2 Macroinstrucţiuni tip funcţie........................................................................................................................... 65 Cap 11 EXERCIŢII ................................................................................................................................................................ 66 BIBLIOGRAFIE .................................................................................................................................................................... 67

ii

Gh. GRIGORAŞ

Programare C

Cap 1 INTRODUCERE ÎN LIMBAJUL C
1.1 Scurt istoric 1.2 Forma unui program C 1.3 Compilarea unui program C

1.1 Scurt istoric
Strămoşii limbajului C sunt limbajele de programare CPL, BCPL, B şi Algol 68. CPL a fost dezvoltat la Universităţile Cambridge şi London în anii 1960-1963. BCPL a fost proiectat de Martin Richards în 1967 şi face parte din categoria “low-level languages”, sau “systems programming languages”. În anii ’60, la Bell Laboratories în USA, Ken Thomson a început proiectarea sistemului de operare Unix. Primul limbaj de nivel înalt implementat sub acest sistem de operare a fost limbajul B, un limbaj bazat pe BCPL care a fost proiectat de asemenea de către Ken Thompson în 1970. Asemănarea cu BCPL a fost mai degrabă semantică; sintaxa este total diferită. Proiectarea limbajului B a fost influienţată de limitele maşinii pe care a fost implementat: un PDP-7 cu capacitatea 4K ( cuvinte de 18 biţi). Limbajele BCPL şi B sunt limbaje de nivel scăzut faţă de limbajul Pascal şi nici unul din ele nu este tipizat: toate datele sunt considerate cuvinte maşină încât, exemple simple de programe duc la complicaţii nebănuite. În una din implementările BCPL-ului, operanzii flotanţi din expresii trebuiau precedaţi de punct; restul operanzilor erau consideraţi întregi. Problemele create în jurul limbajelor BCPL şi B au dus la dezvoltarea unui nou limbaj, bazat pe B, care la început s-a numit NB dar numele său a fost stabilit C. Numele “C”, considerat la început a proveni de la Cambridge, este oficial din “Combined” dar neoficial se consideră a fi iniţiala pronumelui lui Cristopher Strachey, cel care a condus echipa ce a proiectat CPL-ul. Limbajul a fost proiectat şi implementat de către Dennis Ritchie la Bell Laboratories în 1972. În mare parte ( construcţiile for şi switch, tratarea pointerilor etc.) limbajul a fost influienţat ş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 creştere popularităţii calculatoarelor personale, au apărut numeroase alte implementări ale limbajului C şi astfel a crescut şi popularitatea acestuia. Un fapt cu totul inedit a fost acela că, mare parte din implementările 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 înţelege că au existat şi neconcordanţe î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 dispoziţia celor interesaţi la începutul lui 1990. Firma Borland a folosit acest standard în faza de proiect şi a realizat implementările Turbo C care s-au bucurat de un succes remarcabil. De altfel, în prezent , toate compilatoarele C sunt compatibile cu standardul ANSI C; unele din acestea au adăugate o serie de facilităţi sau extensii proprii care nu împiedică compilarea unui program standard.

1

numit Cpre. a octeţilor şi a adreselor de memorie. De exemplu. Algol. adică a elementelor de bază în funcţionarea unui calculator. 1958 ALGOL 60. Modula. Unul din motivele pentru care limbajul C este în acelaşi timp agreat de mare parte din programatori şi dezagreat de altă mare parte din programatori. În ciuda acestui fapt .1. Limbajul C este privit ca un limbaj de nivel mediu întrucât combină cele mai reuşite elemente ale limbajelor de nivel înalt ( Pascal. Ada) cu gradul de control şi flexibilitatea oferite de către limbajul de asamblare. Ceea ce este sigur. 1957 FORTRAN II.Gh. 1972 C++. 1967 ALGOL 68. 1968 B. programele scrise în C au un grad mare de portabilitate: uşurinţa de a adapta programele scrise pentru o anumită categorie de calculatoare sau sisteme de operare la alte calculatoare sau sisteme de operare. a apărut în 1984. numită C++. este că datorită faptului că face parte din sistemele de operare de tip Unix. Cei care agrează limbajul C îi apreciază flexibilitatea sa. care a adăugat 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). Are de asemenea o mulţime bogată de operatori care-i dau un înalt grad de expresivitate. 1984 Figura 1. 1970 C. este lipsa totală a verificării tipului (type checking). limbajul a căpătat o mare popularitate în lumea academică şi nu numai. GRIGORAŞ Programare C FORTRAN I. C permite manevrarea biţilor. Motivele pentru care a fost ales 2 . 1960 CPL. proiect ce trebuia să realizeze distribuirea nucleului Unix într-o reţea de computere conectate LAN. Originea acestui limbaj [STR 94] trebuie căutată în proiectul care a început în Aprilie 1979 la “Computing Research Center of Bell Laboratories” în Murray Hill. USA. Compilatoarele oferite împreună cu sistemul Unix sunt relativ ieftine (uneori chiar gratuite) şi pot fi folosite pe o diversitate de maşini. În martie 1980 preprocesorul îmbunătăţit era implementat pe 16 sisteme iar limbajul acceptat de acesta a fost numit “C cu Clase”. New Jersey. Fortran. Pentru realizarea acestui proiect. Limbajul C are structuri de control adecvate precum şi facilităţi de structurare a datelor care îl fac să fie folosit într-o diversitate de domenii. 1963 BCPL. O nouă versiune a limbajului C. cei care nu-l agrează îl consideră un limbaj lipsit de siguranţă. Bjarne Stroustrup a realizat în Octombrie 1979 un preprocesor. o variabilă de un tip scalar oarecare poate apare în aceeaşi expresie cu o variabilă de un alt tip scalar.

Iată ce spune Stroustrup despre această alegere: <<I picked C++ because it wos short. tough it is always pronounced “plus plus”. chiar dacă era clădit pe C. Datele care apar în paranteză se referă la prima implementare disponibilă pentru limbajul respectiv(după [STR 94]). “succesor” or “increment” . După numele “ C cu clase” limbajul a fost “botezat” C84. In C++. 3 . 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 oricăror tehnici de programare. Ada. Asta pentru scurt timp însă deoarece comitetul ANSI pentru standardizarea C-ului a atras atenţia că se poate crea o confuzie cu această denumire.Gh. and wasn’t of the form “adjective C”. GRIGORAŞ Programare C limbajul C (în faţa altor limbaje ale vremii: Modula-2. indiferent de ce calculator dispunem. Cele mai mari influienţe asupra noului limbaj le-au avut: Simula care a dat clasele.>> În tabela de mai jos sunt prezentate etapele importante din proiectarea limbajului C++. depending on context. Smalltalk.2 Forma unui program C Vom începe prezentarea printr-un exemplu. şi a fost folosit pentru prima dată în Decembrie 1983. referinţele şi abilitatea de a declara variabile oriunde în cadrul unui bloc şi BCPL de unde a fost luată forma de prezentare a comentariilor cu //. • C este un limbaj eficient: semantica sa este “low level” ceea ce înseamnă că pot fi folosite cu eficienţă resursele hardware pentru programele C. Mesa. Algol 68 care a dat supraîncărcarea operatorilor. • C este un limbaj portabil: chiar dacă un program C nu este automat portabil de la o maşină la alta. un membru al echipei lui Stroustrup. acesta oglindeşte conceptele fundamentale ale unui computer tradiţional. Numele C++ a fost sugerat de Rick Mascitti. had a nice interpretation. acest lucru este posibil în general fără un efort deosebit. • C este un limbaj disponibil: un limbaj pentru care se poate găsi cu uşurinţă un compilator. ++ can. 1979 1980 1982 1983 1984 1985 1986 1987 1988 1990 Mai Octombrie Aprilie Ianuarie August Decembrie Ianuarie Februarie Octombrie Septembrie Noiembrie Decembrie Ianuarie Iunie Martie Mai Mai Iunie Februarie Martie Mai August Î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++ 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 1991 1992 1994 1. Aşa cum vom vedea pe parcursul prezentării limbajului. be read as “next”. C++arm reprezintă “The Annotated C++ Reference Manual” iar C++std este standardul ANSI C++. era vorba totuşi de un nou limbaj.

a = b. operaţiile de intrare/ieşire se realizează prin apel la funcţiile unei biblioteci standard furnizate de sistem. În corpul funcţiei main în Programul 1 avem apeluri la alte funcţii: printf. &b corespund adreselor 4 .b.h. x = y. &raspuns). Primele linii din program constituie comentarii. daca acest rest este nul. } Să trecem în revistă structura programului prezentat. a capata valoarea lui b. Programul pe care l-am prezentat este constituit din două funcţii: main şi cmmdc. Metoda care se aplica pentru a determina cmmdc este urmatoarea: se calculeaza restul impartirii intregi a lui a prin b.cmmdc (a. &a şi &b. scanf ("%c". Trebuie reţinut faptul că orice program C conţine definiţia unei funcţii cu numele main care ţine locul programului principal: punctul de intrare în execuţia unui program C este funcţia main. În exemplul de aici avem: printf cu un parametru aflat între ghilimele: acest text va fi tipărit pe ecran scanf cu trei parametri: "%d%d". return x. &b). GRIGORAŞ Programare C Programul 1 /* Calculul celui mai mare divizor comun (cmmdc) a doua numere intregi pozitive a si b. char raspuns. Pe această linie este o directivă care este gestionată de precompilator (preprocesor) şi permite includerea definiţiilor funcţiilor intrare/ieşire furnizate de sistem şi care se găsesc în fişierul stdio.b)). In caz contrar. } printf (" Cmmdc al celor doua numere este: %d\n". Urmează o linie ce începe cu #. } while (raspuns!=’n’). În limbajul C nu există instrucţiuni de intrare/ieşire. b capata valoarea restului si procedeul se repeta. b este valoarea cautata. do { rest = x%y. y = rest. cmmdc. printf (" Continuati ? d)a n)u -->"). Aceste funcţii vor fi prezentate mai târziu într-un capitol special. return 0. if (b>a) { int c = a.h> int cmmdc (int x. */ # include <stdio. Parametrul "%d%d" specifică formatul de citire a două numere întregi (d de la digit) iar parametrii &a. int y){ int rest. } while (rest!=0). } int main (){ int a. Acesta este un apel la o operaţie de citire a unor date pe care le introducem de la tastatură. Corpul unei funcţii este delimitat de acolade: ‘{’ ţine locul lui begin (din alte limbaje) iar ‘}’ al lui end.Gh. scanf ("%d%d". do { printf (" Introduceti 2 valori intregi pozitive -->").b = c. scanf. &a. Aceste comentarii sunt incluse între "parantezele" “/*” şi “*/”.

Toate programele în C constau dintr-una sau mai multe funcţii. Aceasta este prima funcţie apelată la începutul executării programului.b)) este un apel la o operaţie de scriere (pe ecranul monitorului) a textului: cuprins între " şi " în afară de %d\n care reprezintă formatul cu care se va tipări valoarea returnată de apelul la funcţia cmmdc (a. printf ("Cmmdc al celor doua numere este: %d\n" cmmdc (a. singura funcţie care trebuie să fie prezentă în orice program se numeşte main( ).Gh. Procesul de legare (linking) este acela în care programul de legare(linker) combină codul scris de programator cu codul obiect al funcţiilor utilizate şi care se găseşte în bibliteca standard. Standardul ANSI C precizează setul minimal de funcţii care va fi inclus în biblioteca standard. • memorează variabilele locale. Crearea unui format executabil al unui program C parcurge următoarele trei etape: 1. Dacă se foloseşte un compilator care nu are integrat un editor (cum sunt cele din sistemul de operare UNIX). Stiva este folosită în timpul execuţiei programului în scopurile: • memorează adresele de revenire ale apelurilor la funcţii. Construcţia "do {. )" care apare şi în funcţia main şi în funcţia cmmdc este una din structurile iterative de care dispune limbajul C: grupul de instrucţiuni cuprins între acoladele de după do se execută atâta timp cât condiţia scrisă după while este îndeplinită (de precizat că acel grup de instrucţiuni se execută măcar odată). Deşi au fost concepute şi interpretoare de C şi acestea sunt disponibile pentru diferite medii.. GRIGORAŞ Programare C (faptul că & apare în faţă) unde vor fi memorate cele două valori întregi: &a adresa variabilei a. C a fost gândit de la bun început ca fiind destinat compilării. Legarea programului cu funcţiile necesare existente în bibliotecă.. Majoritatea compilatoarelor C asigură medii de programare care includ facilităţi pentru realizarea tuturor etapelor precizate mai sus. 3.4. dacă se crează un fişier sursă cu editorul Word su Windows.b): va fi scris un număr întreg (%d) după care se trece la linie nouă (caracterul \n). Definiţiile acestor funcţii se află în fişierul sistemului stdio. Urmează o zonă de memorie în care sunt stocate variabilele globale. compilatoarele ce există pe piaţă conţin cu siguranţă şi alte funcţii. Într-un program C corect conceput. iar &b adresa variabilei b. schemă compusă din apeluri ale altor funcţii aflate în biblioteca standard a limbajului C sau definite de programator.h. care îndeplinesc funcţii distincte (figura 1. funcţia main( ) conţine. acesta trebuie salvat ca fişier text pentru a putea fi utilizat de un compilator. Celelalte două zone reprezintă aşa zisa stivă a programului şi zona de manevră. o schemă a ceea ce face programul. Compilarea programului. 1. Structurile de control ale limbajului C se vor prezenta într-un capitol special. Limbajul C a fost optimizat special în vederea compilării. 2. • memorează starea curentă a unităţii centrale de prelucrare. • memorează argumentele funcţiilor.). Atenţie însă: compilatoarele acceptă ca fişiere de intrare (programe sursă) numai fişiere text standard.3 Compilarea unui program C Se ştie că există două metode generale de transformare a unui program sursă într-un program executabil: compilarea şi interpretarea. în esenţă.} while ( . altele folosesc programul de legare al sistemului de operare. Prima regiune este aceea care stochează codul programului. Prin urmare. 5 . Un program C compilat creează şi foloseşte patru zone de memorie disjuncte. atunci trebuie folosit un editor pentru crearea fişierului sursă C. Crearea programului. Unele compilatoare au programul propriu de legare.

4. GRIGORAŞ Programare C STIVA ZONA DE MANEVRĂ (heap) VARIABILE GLOBALE CODUL PROGRAMULUI Figura 1. prin intermediul funcţiilor de alocare dinamică ale limbajului C. le foloseşte pentru stocarea unor articole de genul listelor înlănţuite şi arborilor. în încheierea acestei scurte introduceri . 6 . Zona de manevră (heap) este o regiune de memorie liberă pe care programul . compilatoarele existente pe piaţă. După unii autori. De aceea C++ acceptă limbajul C: orice program C va putea fi compilat de un compilator C++. C++ este o versiune extinsă a limbajului C. Să mai precizăm . un programator care nu ştie C nu va putea programa în C++. care a fost concepută pentru programarea orientată pe obiecte. aşa cum dăinuie de când a fost creat limbajul şi. sunt compilatoare C++. că în general.Gh. programarea în limbajul C va dăinui multă vreme de acum încolo.

double Aceste tipuri sunt folosite pentru a reprezenta numere reale.128. Aceşti operanzi. Tipuri simple Tipurile simple (sau tipurile de bază) ale limbajului C definesc dimensiunea zonei de memorie ocupată de valoarea unei variabile precum şi modul în care va fi interpretat conţinutul acestei zone.32767) 31 -2 .. GRIGORAŞ Programare C Cap 2 TIPURI.. 215 -1 (.. Tipul vid: void.32767) -215 . int şi long. 231 -1 (-2147483648.. Declararea unei variabile 2. Este un tip folosit pentru reprezentări de dimensiune nulă. 7 • • .. 2. Tipuri întregi unsigned Tipul Dimensiunea Domeniul unsigned char 8 biţi 0 .127) -2 .. Limbajul C oferă de asemenea un mecanism de conversie a tipului care va fi prezentat mai târziu în lucrare. Acest lucru este sintetizat în Tabela 1. 4294967295 Tipuri reale: float. 2 7-1 (.2147483647) 15 Tabela 2.1. Operanzii într-o expresie sunt nume de variabile.1. 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. Tipurile de bază ale limbajului C pot fi grupate în trei mulţimi: • Tipuri întregi: char..32768. Tipuri simple 2.Gh. Specificarea signed tip este echivalentă cu tip iar unsigned tip are semnificaţia din Tabela 2.. Literali 2.4.. Aceste tipuri sunt utilizate pentru reprezentarea numerelor întregi..2. nume de constante (literali). 215 -1 (. fiecare dintre ei. EXPRESII 2..3.. Pentru tipurile întregi se pot folosi specificările signed şi unsigned. 255 unsigned short 16 biţi 0 . apeluri la funcţii.iar double pentru numere reale în dublă precizie – aproximativ 15 cifre semnificative. short. Diferenţa între acestea constă în dimensiunea zonei de memorie ocupată de o variabilă de acest tip şi implicit de mărimea domeniului de valori. OPERATORI. Tipurile întregi Tipul char short int long (long int) Dimensiunea memoriei ocupate 8 biţi 16 biţi 16 biţi 32 biţi Domeniul -27 .. au un anume tip iar în funcţie de aceste tipuri se vor aplica algoritmi specifici pentru determinarea valorii expresiei. 65535 unsigned (int) 16 biţi 0 .32768. Tabela 1. Ele se disting prin precizia de reprezentare: float – numere reale în simplă precizie –aproximativ 6 cifre semnificative . 65535 unsigned long (int) 32 biţi 0 .

‘A’.Nu trebuie să coincidă cu vreun cuvânt cheie (vezi Tabela 4) Tabela 4. .tabulare. -12.caracterul \ (backslash).interlinie (linie nouă). . Un identificator în limbajul C este un şir de caractere care trebuie să îndeplinească condiţiile: . Cuvintele cheie ale limbajelor C şi C++ asm auto break case catch char class const continue default delete do double else enum extern float for friend goto handle if inline int long new operator overload private protected public register return short signed sizeof static struct switch template this throw try typedef union unsigned virtual void volatile while O variabilă se declară prin una din următoarele instrucţiuni: 8 . 42153l. 25AfuL 3. 03724 0x7F.retur de car.primul caracter este literă sau ‘_’ (underscore). 0245u. 10e-12 ‘a’. . în limbajul C se folosesc diferite reguli sintactice pentru reprezentarea constantelor. .caracterul ”. ‘\f’ . GRIGORAŞ Programare C 2. ‘\b’ . Variabilele sunt obiectele de bază ale limbajului şi au un nume care este un identificator. Constante literale Constante întregi Exemple 123. . ‘+’ “şir de caractere” Explicaţii Notaţia zecimală Notaţia octală (prefix 0) Notaţie hexazecimală (prefix 0x sau 0X) Constante fără semn Reprezentare pe 32 biţi Constante fără semn pe 32 biţi Reprezentare fără exponent Reprezentare cu exponent întregi long flotant caracter şir Există şi caractere speciale (de control) care sunt compuse din caracterul “\” şi din alt caracter. .Gh.2E+3. 042l 423uL.03.1415.2. ele reprezintă valori care nu pot fi modificate pe parcursul execuţiei unui program.3.caracterul NULL. 0 012.425 -15. -457.retur de linie. . 0xA24E 39u. Tabela 3. ‘\’’ . Literali Literalii (sau constantele literale) sunt valori constante. Acestea sunt: ‘\n’ ‘\\’ ‘\0’ ‘\”’ ‘\u’ . +423. Declararea unei variabile şi a unei constante Toate variabilele unui program trebuiesc declarate înainte de a fi utilizate.următoarele caractere sunt caractere alfanumerice (litere sau cifre) sau ‘_’. 2.backspace. Tabela 3 sintetizează unele din aceste reguli. 57F3u 12L.caracterul ‘’’ ‘\r’ .tabulare verticală. 0X3. ‘\t’ .

Gh. Variabilele din clasa static diferă de cele din clasa auto sau register prin aceea că sunt memorate în locaţii fixe de memorie (au o adresă fixă. Această clasă este implicită. Tabelul 5 sintetizează clasele de alocare. alocarea se face în registrele unităţii centrale şi nu în memoria RAM.71828182845905. register. Clasele de alocare sunt automatic. float eps = 1. dacă e posibil. Un astfel de nume nu poate să apară în stânga unei atribuiri: o constantă nu poate fi modificată. Variabilele definite în exteriorul tuturor funcţiilor fără vreun specificator de clasă sunt în clasa extern şi au alocate adrese fixe. const float pi = 3. Dacă se definesc în interiorul unei funcţii trebuie să aibă neapărat în faţă cuvântul rezervat static (se mai spune că sunt în clasa static intern). Pentru a ne putea referi la memoria computerului. Un nume de constantă se declară prin: const tip nume = valoare. Mai multe variabile de acelaşi tip pot fi declarate în aceeaşi instrucţiune.e-4. într-o declaraţie se specifică clasa de alocare (implicit sau explicit). având durata de viaţă pe parcursul execuţiei funcţiei. Legată de noţiunea de variabilă este cea de “lvalue”. Este folosită litera l ca prefix pentru a desemna „ceva ce poate fi în stânga unei atribuiri”. explicit se poate specifica prin cuvântul cheie aut: aut int x.14159. const double e = 2. Cu toate acestea nu orice lvalue poate apare în stânga unei atribuiri: o variabilă declarată const referă o zonă de memorie dar nu poate să apară în stânga unei atribuiri. Tot în interiorul unei funcţii variabilele mai pot fi definite şi în clasa register care este asemănătoare cu cea automatic cu deosebirea că. char n1='\n'. În afară de tipul variabilei şi eventual o valoare iniţială pentru aceasta. GRIGORAŞ Programare C tip nume. static şi extern. Variabilele din clasele auto şi register nu îşi păstrează valoarea de la un apel la altul al funcţiei în care sunt definite. Variabilele se declară la începutul funcţiilor (variabile locale) sau în exteriorul tuturor funcţiilor (variabile globale). Tabelul 5. tip nume = valoare. Clase de alocare CLASA Auto. j. k. despărţite prin virgule. Un obiect (noţiune care aici nu are legătură cu cea din programarea orientatăobiect) este o regiune contiguă de memorie. aceasta are nevoie de un nume. O lvalue este o expresie care desemnează un obiect. permanentă). Exemple: int i. Variabilele din clasa automatic sunt definite în interiorul funcţiilor fiind locale funcţiei în care au fost definite. Register Static intern Extern Vizibilitate în interiorul funcţiei în interiorul funcţiei în toate modulele CARACTERISTICI Adresă Păstrează fixă valoarea NU NU DA DA 9 DA DA Durata de viaţă Pe durata funcţiei Pe durata aplicaţiei Pe durata aplicaţiei .14159. p = 3. const char mesaj[ ] = "Eroare".

0 20. atunci evaluarea expresiei: alpha + sqrt (beta). aceasta este de la stânga la dreapta. Operatorii ce pot apare într-un program C sunt de patru feluri: • operatorul paranteză ( ).0 25. se aplică conversii implicite de tip: unul din operanzi se converteşte la tipul celuilalt iar tipul rezultatului va fi tipul comun. dacă se declară: float alpha = 20. o expresie ce conţine un astfel de operator se construeşte astfel: operand operator operand. -decrementarea. Am văzut că operanzii pot fi variabile. Operatori Operatorii se folosesc la constituirea de expresii pornind de la constante şi variabile.45 + 5. Evaluarea expresiilor se face înlocuind operanzii prin valorile sale şi efectuînd operaţiile conform cu specificaţiile acestora. 2. Fiecare dintre aceştia au un tip declarat. Dacă tipul operanzilor este acelaşi într-o expresie. constante sau apeluri la funcţii (despre care se va vorbi mai târziu).incrementează (decrementează) cu 1 valoarea unei lvalue şi pot fi: • prefixaţi: variabila este incrementată (decrementată) înainte de evaluarea expresiei în care se află. Spre exemplu. k. acesta delimitează subexpresii şi poate schimba asociativitatea operatorilor. ++x. x++. float beta = 25. asociativitatea este de la dreapta la stânga. • postfixaţi: variabila este incrementată (decrementată) după evaluarea expresiei în care se află. Operatorii ++ şi -. Operatori aritmetici Operatorii aritmetici sunt aceea care se aplică operanzilor numerici. • operatori unari cu notaţie prefixată sau postfixată: -x. • operatorul ternar condiţional “?:” : operand 1 ? operand 2 : operand 3. evaluarea se face în mod natural.4. m.1.45 Dacă operanzii au tipuri diferite . (x + y) * z. y--. Exemple: Fie declaraţia int i = 10. în modulul în care-i definită în toate modulele aplicaţiei DA DA Pe durata aplicaţiei - 2. • Operatorii unari: +. Semnul minus are semnificaţia obişnuită: schimbă semnul valorii expresiei în faţa căreia se află. ++ incrementarea. j. l. Exemplu: x+y x % y.45. De pildă expresiile x * y +z şi x * (y + z) vor fi evaluate diferit pentru aceleaşi valori ale variabilelor.4. se face în ordinea indicată mai jos: alpha + sqrt (25) alpha + 5. GRIGORAŞ Programare C Static extern Extern (în DA funcţie) Static extern în toate modulele aplicaţiei DA (în funcţie) În capitolul al optulea sunt tratate mai în amănunt problemele legate de gestiunea memoriei.Gh. 10 . ca şi perechea: x + y * z. Aceştia pot fi unari sau binari iar în privinţa asociativităţii. acolo unde nu este specificat altfel.plus şi minus unari. • operatori binari cu notaţie infix adică.

<< . asin. x. În secvenţa anterioară. int. dacă x va fi egal cu y atunci a va căpăta valoarea 0 iar în caz contrar a va căpăta valoarea 1. Aceştia se pot aplica doar operanzilor de tip întreg (char.  .funcţiile exponenţială.negare la nivel de bit (operator unar).prezentate în Anexa – care sunt operatori cu scriere prefixată: . cos.4. Aceşti operatori sunt: & .conjuncţia logică (şi) Expresia: • op1 && op2 are valoarea adevărat (1) dacă cei doi operanzi sunt evaluaţi la adevărat. • !op1 are valoarea fals dacă op1 este adevărat şi are valoarea adevărat dacă op1 este fals. GRIGORAŞ Programare C j = i++ /* j = 10 apoi i = 11 */ k = ++i /* i = 12 apoi k = 12 */ l = i-/* l = 12 apoi i = 11 */ m = --i /* i = 10 apoi m = 10 */ • Operatorii binari sunt următorii + adunare * înmulţire scădere / împărţire % modulo La aceşti operatori se adaugă funcţiile din biblioteca matematică . Aceştia sunt operatori binari iar sintaxa lor în C este: < (mai mic) <= (mai mic sau egal) > (mai mare) >= (mai mare sau egal) == (egal) != (diferit) Cum rezultatul unei comparări este un număr întreg este posibilă asignarea acestuia unei variabile întregi: int a. .disjuncţie logică la nivel de bit.4.funcţiile trigonometrice uzuale: sin. iar dacă unul din ei are valoarea fals (0) atunci expresia are valoarea zero.deplasare la stânga. acos. logaritmică etc.negaţia logică cu asociativitate de la dreapta la stânga  .funcţia de ridicare la putere pow. Operatori logici Operatorii logici sunt operatori binari care se aplică unor operanzi ce au valori adevărat (1) sau fals (0) şi ei corespund operaţiilor logice uzuale: ! .3. 2. . 2.sau exclusiv la nivel de bit.Gh.2. ~ .conjuncţie logică la nivel de bit.4. short.4. atan. Operatori la nivel de bit Limbajul C dispune de şase operatori pentru tratarea informaţiei la nivel de bit. • op1  op2 are valoarea zero (fals) dacă şi numai dacă op1 şi op2 au ambii valoareea zero (fals). long) şi servesc la poziţionarea sau interpretarea valorilor întregi ca o secvenţă de valori binare.disjuncţia logică (sau) && . 11 . ^ . atunci: 2. tan. y a = (x != y). Operatorii relaţionali (de comparaţie) Operatorii relaţionali sunt operatori binari şi permit compararea valorilor a doi operanzi (expresii) iar rezultatul evaluării este zero (dacă rezultatul este fals) sau 1 (dacă rezultatul este adevărat).

|. z = x >> 2. Acest lucru permite “atribuirea în lanţ”precizând că asociativitatea operatorului = este de la dreapta la stânga: var1 = var2 = . permite ca aceştia să formeze expresii care au atribuite valori. limbajul C permite compunerea atribuirii cu un operator binar (op) obţinându-se operatori de atribuire compusă: e1 op = e2. %= • Operatori logici la nivel de bit: &=. Pe lângă acest tip de atribuire. 12 . ^= Operatori de decalare: <<=.. valoarea expresiei din dreapta semnului = . are ca efect evaluarea expresiei din dreapta semnului = şi atribuirea acestei valori variabilei ce se află în stânga acestui semn. exprimat prin =.5. = varn = expresie. variabila = expresie.4. op1 op2 = = 01100100 10111010 op1 & op2 = 00100000 op1 | op2 = 11111110 op1 ^ op2 = 11011110 De asemenea negaţia (~) acţionează la nivelul fiecărui bit schimbând 1 în 0 şi 0 în 1. GRIGORAŞ Programare C >> . Exemple: x = 13.Gh. Se obţin atribuiri compuse cu: Operatori aritmetici: +=. >>= Faptul că atribuirea este privită ca un operator binar. ^) este cea naturală (aminitm că 1 reprezintă valoarea logică “adevărat” iar 0 valoarea “fals”). x = 0000 0000 0000 1101 (= 13) y = 0000 0000 0011 0100 (= 13*4) z = 0000 0000 0000 0011 (= 13/4) 2. *=. ~ op1 = 10011011 Operatorii binari de decalare ( << şi >>) servesc la decalarea cu un anumit număr de biţi (dat de cel de-al doilea operand) la stânga sau la dreapta în cadrul primului operand.. Operatorul de atribuire Operatorul de atribuire. unde e1 şi e2 sunt expresii (e1 este lvalue). y = x << 2. Această atribuire este echivalentă cu : e1 = (e1) op (e2). este o expresie ce are ca valoare.deplasare la dreapta. Astfel variabilă = expresie. -=. |=. /=. În particular se poate obţine (rapid!) înmulţirea cu 2op2 (op1 << op2) sau cu 2-op2 (op1 >> op2). Semnificaţia primilor trei operatori binari (&. Exemplele următoare sunt edificatoare.

Operatorul condiţional se notează cu ?: şi are asociativitatea de la dreapta la stânga. Forma generală a sa este: opr1 ? opr2 : opr3.7.6. i < j. O asemenea construcţie apare frecvent în instrucţiunile for pentru realizarea mai multor acţiuni: for(i = 0.Gh. Expresiile sunt evaluate de la stânga la dreapta iar valoarea întregii expresii este valoarea (şi tipul. expresie2. operatorul condiţional şi cel de atribuire li se aplică asociativitatea la dreapta pe când tuturor celorlalţi li se aplică asociativitatea la stânga. 6. k.8. este o expresie echivalentă cu construcţia: if (a > b) max = a. prin aceasta cele patru variabile i. j. Operatorul virgulă Forma generală a unei expresii ce utilizează acest operator este: expresie1. expresia este egală cu opr2 iar dacă opr1 are valoarea zero (fals). expresia este egală cu opr3. else max = b. opr2. Precedenţa operatorilor limbajului C este dată în tabelul nr. opr3 sunt operanzi. ..4. Dacă opr1 are valoarea nenulă (adevărat). Exemplu: max = (a > b) ? a : b. Spre exemplu i = j = k = l = 1. se înţelege) expresiei ultime ce apare în acest şir. 2.. Am văzut că operatorii parantetizare pot schimba asociativitatea operatorilor. operatorilor unari. GRIGORAŞ Programare C prin care cele n variabile capătă valoarea expresiei din partea dreaptă. 2. i < 100. unde opr1. i++. Operatorul condiţional Acesta este singurul operator ternar al limbajului C.4. Precedenţa operatorilor Asociativitatea şi precedenţa operatorilor indică ordinea în care se evaluează subexpresiile într-o expresie. i++. j--) 2. 13 . este o construcţie validă în C. j = n-1. Pentru limbajul C. j = 0.4. j++) for(i = 0. l capătă valoarea 1.

-> ASOCIATIVITATEA stânga .dreapta --(postfix) ! ~ dreapta-stânga --(prefix) &(adresa) + *(deref) -(unari) sizeof() * + << < <= == & ^(sau exclusiv) | && || ?: / >> > != >= % stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta stânga – dreapta dreapta-stânga >>= <<= dreapta-stânga stânga .Gh. GRIGORAŞ Programare C Tabelul nr.(operatorul virgula) 14 . OPERATORII () ++ ++ [] .6.dreapta = += -= *= /= %= &= ^= |= .

Limbajul C permite de asemenea utilizarea unor funcţii predefinite (printf. int). Există două moduri de a declara o funcţie: .descompunerea unei secvenţe lungi de procesare a informaţiei într-o mulţime de secvenţe “mici” de transformări de bază ale informaţiei. Funcţia principală main În limbajul C . Este posibilă definirea de funcţii care să retuneze valoarea vidă: în acest caz tipul valorii returnate este void. tipul implicit al funcţiei este int. Numele unei funcţii este un identificator şi acesta trebuie să respecte aceleaşi reguli ca cele referitoare la numele unei variabile.furnizarea către alţi programatori a unor elemente de bază de nivel înalt. 3. etc.relativ uşor . Sfârşitul execuţiei unui program 3. instructiuni. Mulţimea funcţiilor unui program poate fi partiţionată în mai multe fişiere care pot fi compilate separat. o funcţie trebuie declarată înainte de a fi apelată. Apelurile la aceste funcţii sunt gestionate în faza de editare a legăturilor. Apelul şi transmiterea parametrilor 3.explicit: printr-o instrucţiune ce descrie signatura sa în cazul în care se face apel la o funcţie descrisă într-un fişier separat sau în cazul în care apelul la funcţia respectivă precede declaraţia funcţiei în acelaşi fişier. Declararea unei funcţii constă în a da signatura funcţiei. detaliile acestor elemente rămân “ascunse” pentru utilizator. În general. funcţii ce se găsesc în bibliotecile sistemului. Aceste elemente de nivel înalt oferă o claritate în plus programului global şi permite .5. Returnarea unui apel 3. Dacă această specificare lipseşte.implicit: în definiţia funcţiei. Definirea unei funcţii Definiţia unei funcţii are următoarea sintaxă: tip nume (lista parametri) { declaratii (de variabile). respectiv tipul valorii returnate de un apel la această funcţie.3. Funcţii cu un număr variabil de parametri 3. Iată câteva instrucţiuni de declarare a unei funcţii: long int cod (const char *. linia de început conţine tipul său şi argumentele sale (număr şi tip). Signatura unei funcţii defineşte atât tipul său (tipul valorii returnabile) cât şi numărul şi tipul argumentelor sale. 15 . În C există doar funcţii care returnează o valoare.4. .).nu există noţiunile distincte de procedură şi de funcţie. } În această definiţie.modificări ulterioare ale acestuia. tip este unul din cuvintele rezervate care defineşte tipul funcţiei.1. Definirea unei funcţii 3. .Gh.6. Aşa cum o variabilă trebuie declarată înainte de a fi utilizată într-o expresie.spre deosebire de Pascal sau alte limbaje . utilizarea unei funcţii permite: . getc. GRIGORAŞ Programare C Cap 3 FUNCTII 3.2. Funcţiile care returnează în programul apelant valoarea vidă se declară de tip void. Să precizăm că nu se poate defini o funcţie în interiorul altei funcţii.1.

Apelul şi transmiterea parametrilor Apelul unei funcţii se face prin: nume (listă _ parametri_actuali). Acesta este singurul exemplu de apel la o funcţie care nu returnează nimic: sistemul opreşte execuţia programului şi raportează valoarea transmisă la apel. GRIGORAŞ Programare C char* decod (long int). O funcţie f returnează o valoare în corpul funcţiei din care s-a făcut apel la f. 16 . 3. n).a. Returnarea unui apel Sintaxa instrucţiunii de returnare a valorii unei funcţii este: return expresie. Funcţia "exit" este o funcţie sistem care termină execuţia unui program. De pildă. În ultima linie avem un exemplu de funcţie fără parametri (numărul parametrilor este zero). Tipul acestei expresii trebuie să fie acelaşi cu tipul funcţiei. Această valoare poate fi utilă atunci când programul este executat în interiorul unei linii de comandă a interpretorului de comenzi. O funcţie care returnează valoarea vidă (funcţie de tip void) poate conţine instrucţiunea "return" fără argument sau poate omite această instrucţiune. În absenţa unei instrucţiuni exit. printf ("%d %d \n". printf("x\%f\n". Funcţii cu un număr variabil de parametri Există funcţii care pot avea un număr nedeterminat de parametri. void schimbare (int . Valoarea expresiei "expresie" va fi returnată şi poate fi utilizată în programul apelant. 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. int funct ( ). prin instrucţiunea return (plasată în corpul lui f).b). 3. 3. int strlen (const char* s).5.: printf ("Un apel cu un singur argument \n"). n++.b\%d\n". scanf. încât trebuiesc luate măsurile corespunzătoare. valoarea implicită a codului returnat la sfârşitul execuţiei unui program este 0. int). dacă funcţia este declarată fără tip şi prin folosirea numelui funcţiei urmat de lista de parametri actuali într-o expresie (în care este permis tipul ei) atunci când funcţia este declarată cu tip . Ordinea evaluării parametrilor este în general de la stânga la dreapta dar acest lucru nu este garantat de orice compilator. secvenţa: int n = 10. Prin convenţie. 3./*Un apel cu 2 argumente */ printf ("a\%d.2.x). etc. Un exemplu la îndemână îl oferă funcţiile sistemului: printf.4.Gh. /*3 argumente */. Sfârşitul execuţiei unui program exit (valoare).3.

Asta înseamnă că parametrul actual (de la apel) şi parametrul formal corespunzător (declarat în antetul funcţiei) sunt două variabile distincte. pentru schimbarea conţinutului unor variabile printr-o funcţie. int* y){ int temp = *x. Singura diferenţă este că valoarea transmisă prin parametrii actuali &a. Argumentele de tip pointer vor fi utilizate de asemenea pentru transmiterea ca parametru a unei variabile de tip structură (vezi Cap. printf ("a = %d. În capitolul 6 vom discuta mai pe larg modul de transmitere al parametrilor de tip tablou ca argumente ale unei funcţii. } main ( ){ int a = 1. GRIGORAŞ Programare C tipăreşte (cu unele compilatoare) 10 10 şi nu 10 11.&b este adresa lui a respectiv b şi prin funcţia schimba2 se modifică valorile referenţiate. *x = *y. } iar apelul în funcţia main se face prin: schimba2 (&a . 3. Să facem precizarea că şi în cazul funcţiei schimba2 transmiterea parametrilor se face prin valoare: am simulat aici transferul prin referinţă ce ar fi trebuit să se facă în cazul funcţiei schimba1. &b). Pentru înlăturarea acestui efect este posibilă simularea unui mod de transmitere prin referinţă. } Funcţia main este singura funcţie absolut necesară într-un program C. a. În C adresa unei variabile poate fi specificată într-o variabilă de tip "pointer" (de care vom vorbi mai târziu). Aceasta defineşte punctul de intrare în execuţia programului.Gh.char* argv [ ]) { instructiuni. punând drept parametri actuali valorile adreselor variabilelor ce trebuiesc modificate. } Apelul funcţiei schimba1 în funcţia main nu are efectul scontat: prin apelul la printf se tipăresc valorile 1 şi 2 pentru a respectiv b. Valoarea întreagă returnată de funcţia main este 17 .b). Funcţia principală main int main(int argc. b). Modificările efectuate asupra parametrilor formali nu au decât un efect local care nu este vizibil în afara funcţiei. Să precizăm că în C nu există decât un singur mod de transmitere a parametrilor şi anume transmiterea prin valoare. b =%d". Asta înseamnă că o funcţie nu poate modifica parametrii actuali.7) precum şi pentru limitarea numărului de valori ce trebuiesc puse în stiva de apel a execuţiei unui program.6. parametrul corespunzător trebuie să fie de tip pointer. Funcţia schimba2 care simulează transmiterea parametrilor prin referinţă este: void schimba2(int* x . Un exemplu clasic al consecinţei acestui mod de transfer al parametrilor este ilustrat mai jos: void schimba1(int x. y = temp.. Parametrii funcţiei main permit recuperarea argumentelor transmise din momentul execuţiei programului. *y = temp. schimba1(a. Aşadar. b = 2. int y){ int temp = x. x = y.

.. linia de comandă conţine numele programului urmat de o listă de parametri: nume-program p1. 1.argc: numărul argumentelor din linia de comandă (inclusiv numele programului). “nume . argv [i] ). for (i â 1. GRIGORAŞ Programare C valoarea transmisă mediului la sfârşitul execuţiei programului (vezi funcţia exit).eroare).2 Accesul la fişiere. i++) printf ("%s". este permisă utilizarea funcţiei main fără parametri: int main( ) este o declaraţie validă pentru funcţia main. În cazul în care programatorul nu doreşte să utilizeze argumentele (parametrii) transmise la execuţie. .argv: un tablou de pointeri către fiecare din argumentele liniei de comandă. Tipul FILE 18 . char* argv[ ] ){ if (argc<2) printf ("Nu exista argumente in aceasta executie ! \n"). p2.. În general. pn Atunci argc va avea valoarea n+1 iar argv va fi un tablou ce conţine pointeri astfel: 0 1 : . în particular. arg[0] conţine numele programului executabil.succes. else { printf ("Argumentele executiei sunt: \n").1 Fluxuri şi fişiere 4.Gh. Utilizarea instrucţiunii return precizează condiţia de terminare ( 0 . la lansarea unui program.program” “p1” “pn” 0 n Iată un exemplu de utilizare a acestor argumente : Programul 2 main (int argc. Parametrii disponibili la apelul funcţiei main sunt: . } } 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 ! produce scrierea mesajului: Iata niste argumente! Cap 4 FUNCŢII DE INTRARE IEŞIRE 4. i < argc..

Fluxuri şi fişiere Sistemul de fişiere al limbajului C este conceput pentru a lucra cu o mare varietate de dispozitive.1. Tabelul 5 NUME fopen( ) fclose( ) putc( ) fputc( ) puts( ) fputs( ) OPERAŢIE Deschide un fişier Închide un fişier Scrie un caracter într-un fişier Analog lui putc() Scrie un şir pe ecran Scrie un şir pe fişier 19 . Datorită posibilelor conversii s-ar putea ca numărul de caractere scrise/citite să difere de numărul de caractere existente pe dispozitivul extern.h> Orice program C începe execuţia prin deschiderea unităţilor de intrare/ieşire standard: stdin stdout stderr stdprn : : : : unitatea unitatea unitatea unitatea standard de intrare standard de ie[ire de eroare standard de iesire prn (imprimanta) Aceste unităţi standard sunt asociate. Fluxurile sunt independente de dispozitivele iniţiale . Pointerul de fişier identifică un anumit fişier şi este folosit de către fluxul asociat pentru a conduce operaţia de intrare/ieşire. încât aceeaşi funcţie care scrie date pe un fişier de pe disc poate fi folosită pentru a scrie date pe o altă categorie de dispozitive: consola. Standardul ANSI C permite organizarea unui flux text în linii care se încheie cu un caracter linie nouă(“ăn”). Există două tipuri de fluxuri: •Fluxul text este o scvenţă de caractere.Gh. imprimanta.. stare. Acesta din urmă este opţional pe ultima linie şi este determinat de modul de implementare. •Fluxul binar este o secvenţă de octeţi cu corespondenţă biunivocă faţă de octeţii existenţi pe dispozitivul extern: numărul de octeţi scrişi/citiţi este acelaşi cu numărul acestora de pe dispozitivul extern.h . pot avea loc transferuri de informaţii între fişier şi programul curent în execuţie. tastaturii calculatorului (intrare) şi ecranului (ieşire şi eroare).3 Funcţii de intrare /ieşire pentru caractere 4.5 Citirea cu format 4. o imprimantă sau “o zonă” (fişier) pe disc.4 Scrierea cu format 4. Un fişier în limbajul C poate fi un terminal. Un pointer de fişier este un pointer către informaţia care defineşte diverse caracteristici ale unui fişier(nume. Pentru a putea utiliza într-un program funcţiile standard de intrare/ieşire trebuie scrisă la începutul acestui program o directivă "include" care are ca argument numele fişierului antet ce conţine definiţiile acestor funcţii: stdio. inclusiv cu terminale. GRIGORAŞ Programare C 4. în lipsa altor indicaţii. Aşadar dispozitivul fizic pe care se scriu datele se numeşte fişier iar abstractizarea acestuia se numeşte flux (sau stream). poziţie curentă etc). Un flux se poate asocia unui fişier executând o operaţie de deschidere: odată deschis. Unificarea sistemului de intrare/ieşire în C se face prin “pointerii” de fişier. Cele mai frecvent folosite dintre acestea sunt date în Tabelul 5 şi vor fi prezentate în continuare. unităţi de disc sau de bandă etc. Deşi fiecare dintre acestea este foarte diferit de celelalte. sistemul de fişiere le transformă pe fiecare într-un dispozitiv logic numit flux. Sistemul de fişiere ANSI C este compus din mai multe funcţii legate una de alta. # include <stdio.

Deschiderea unui fişier se face prin funcţia fopen care returnează ca rezultat un pointer la un descriptor de fişier. Aceste fişiere (stdin. Tabelul 6.h şi are forma: FILE* pointer_la_fisier unde pointer_la_fişier este un identificator (numele pointerului). Declararea unei variabile de tip "pointer la fişier" se face folosind tipul FILE definit în fişierul antet stdio. Accesul la fişiere. mod Semnificaţia "r" "w" "a" "r+" "w+" "a+" Citire Creare (schimbare) pentru scriere Adăugare la sfârşitul fişierului (existent) Actualizare (citire/scriere) Schimbare pentru actualizare Actualizare cu scriere la sfârşitul fişierului (existent) 20 . stdout) pot fi orientate şi către alte dispozitive sau fişiere. FILE* fopen(const char* fisier. Modurile posibile de deschidere (deci şirurile de caractere ce constituie parametrul lui fopen) sunt date în Tabelul 6.const char* mod) Aici. fişier este un şir de caractere (identificator) ce desemnează numele extern al fişierului iar mod este un şir de caractere ce descrie modul de deschidere al fişierului. GRIGORAŞ Programare C getc( ) fgetc( ) gets( ) fgets( ) fseek( ) printf( ) fprintf( ) scanf( ) fscanf( ) feof( ) ferror( ) rewind( ) remove( ) fflush( ) Citeşte un caracter dintr-un fişier Analog lui getc( ) Citeşte un şir de la tastatură Citeşte un şir din fişier Caută un anumit octet într-un fişier Scrie la ieşirea standard Analogul pentru fişiere al funcţiei printf( ) Citeşte din intrarea standard Analogul pentru fişiere al funcţiei scanf( ) Returnează “adevărat” dacă se atinge sfărşitul fişierului Returnează “adevărat” dacă survine o eroare Iniţializează indicatorul de poziţie al fişierului la început Şterge un fişier Goleşte conţinutul unui fişier 4.Gh.1. Dispozitivele standard de intrare (tastatura) şi de ieşire (ecranul) sunt văzute tot ca fişiere cu pointerii stdin şi stdout. Bibliotecile standard C oferă o serie de funcţii pentru operaţii de intrare/ieşire orientate pe fişiere. Acest dispozitiv este gestionat de sistem pentru a asigura intrările şi ieşirile efectuate în program. Tipul FILE Conceptul de bază pentru intrări/ieşiri standard este cel de pointer la fişier.

Dacă deschiderea nu s-a efectuat corect valoarea returnată este NULL(pointer la informaţia nulă) . se actualizează fişierul pe disc şi încetează conexiunea logică între pointer şi fişier. deschiderea unui fişier existent în modurile "w" sau "w+" produce pierderea conţinutului său de până atunci. deschide fişierul cu numele “nume_fişier” în modul de acces "mod".dat". } printf ("Mesaj scris din nou pe monitor \n"). GRIGORAŞ Programare C Facem precizarea că. exit (1). atribuind pointerul "pf" la acest fişier. Acest lucru poate fi folosit pentru tratarea erorilor la deschiderea fişierului. Apelul la fclose se poate face prin: int cod. } • • Limbajul C dispune şi de posibilitatea: ştergerii de fişiere prin funcţia remove: int remove(const char* nume_fi[ier). } printf ("Mesaj scris in fisierul iesire. Prin închidere se eliberează descriptorul de fişier. redenumirii unui fişier: stdout la 21 . fclose (stdout). "wt". FILE* pf). Dacă funcţia fopen returnează o valoare nenulă."w+". FILE* pointer_la_fisier cod = fclose (pointer_la_fisier) Fişierele pot fi reasignate prin program folosind funcţia freopen: FILE* freopen (const char* nume_fisier. exit (1). Aceasta permite redirecţionarea dispozitivelor periferice prin program. Valoarea returnată este un pointer la un descriptor de fişier valid. valoare ce va fi utilizată în apelul la funcţiile de intrare/ieşire. Programul următor ilustrează acest fapt. Programul 3 # include <stdio. stdout) == NULL) { fprintf (stderr. const char* mod.Gh.dat \n"). if (freopen ("CON". Apelul la această funcţie închide fişierul cu pointerul "pf". "eroare la reasignarea lui CON\n"). aceasta indică faptul că deschiderea s-a efectuat corect. "Eroare la reasignare \n").stdout)==NULL) { fprintf (stderr. if(freopen("iesire. Închiderea unui fişier se face prin apelul la funcţia fclose: int fclose (FILE* pointer_la_fisier) Funcţia returnează EOF în caz de eroare şi 0 în caz normal.h> main ( ){ printf ("Mesaj scris pe monitor \n").

Funcţia printf se utilizează pentru afişarea la ieşirea standard. FILE* pointer_la_fişier.const char* şir_format.const char* şir_format. a scrierii apelului la fgetc şi fputc în cazul în care intrarea respectiv ieşirea este cea standard : stdin respectiv stdout. fprintf pentru scrierea într-un fişier. GRIGORAŞ Programare C int rename (const char* nume_nou. char c. • creării unui fişier temporar: FILE*tmpfile (void). simplificată. …). Scrierea cu format Funcţiile de scriere cu format permit afişarea sub forma unui şir de caractere a diverselor valori. iar sprintf pentru scrierea într-un şir de caractere. Funcţii de intrare/ieşire pentru caractere int fgetc(FILE* flux). stdout). int fputc(char c.. int cod. c = fgetc(pointer_la_fişier). 4. Funcţia fgetc returnează valoarea întreagă a caracterului următor din fişierul pointat cu pointer_la_fişier sau constanta EOF în caz de eroare sau sfârşit de fişier.…). FILE* flux).…).3. char c. int sprintf(char* s.Gh. Acestea pot fi transmise la ieşirea standard. pointer_la_fişier). Funcţia fputc scrie un caracter la sfârşitul fişierului pointat prin pointer_la_fişier.2. Spre exemplu: c = getchar ( ). int printf(const char* şir_format. într-un fişier sau într-un şir de caractere. iar putchar (c). FILE* pointer_la_fişier. Formatul "şir-format" este un şir de caractere ce conţine fie caractere ordinare fie directive de conversie pentru transformarea valorii unei variabile într-un şir de caractere 22 . 4. este echivalentă cu: fput (c. În caz de eroare este returnată valoarea EOF. const char* nume_vechi ). cod = fgetc(c. int fprintf(FILE* flux. Există de asemenea două funcţii getchar şi putchar care permit exprimarea în C. este echivalentă cu: c = fgetc (stdin).

caracterele se vor cadra la stânga (implicit) sau la dreapta (pentru -). b.dddddd sau numărul de d este dat de argumente de precizie (implicit acesta este 6). în ordine: • eventual unul sau mai mulţi indicatori ce modifică semnificaţia conversiei. întreg în notaţie hexa fără semn. D. p sau X u f int int int int float sau double e sau E float sau double g sau G c s p n % float sau double int char* void* int* 23 . în plus . iar pentru X literele A. Această secvenţă conţine. Dacă dimensiunea rezultată prin conversie este mai mică. CARACTER TIP ARGUMENT CONVERSIA întreg în notaţie zecimală cu semn. real în notaţie zecimală sub forma: [-]m. C.: rezultatul conversiei se va cadra la dreapta. întreg în notaţie octală fără semn. + : la formatul de ieşire va fi ataşat semnul + sau . F. în stil f în caz contrar. Dacă formatul este octal se va adăuga un o înaintea numărului.dddddde ± xx sau [-]m. Formatele de conversie utilizate de printf. eventual caracterul l care precizează că numărul ce urmează a fi afişat este un "întreg lung" (long integer). e. E.Gh. la g sau G se vor afişa şi zerourile nesemnificative de după punctul zecimal. afişare în acelaşi stil cu e dacă exponentul este -4 sau superior preciziei. • • Tabelul 7. afişează argumentul în notaţia pointer. afişarea unui singur caracter după conversie în unsigned char. caracterul ce indică formatul conversiei. afişarea unui şir de caractere până la întâlnirea caracterului '\0' (sfârşit de şir) sau până când numărul de caractere este egal cu numărul de precizie. d. GRIGORAŞ Programare C Caracterele ordinare sunt transmise la ieşire aşa cum apar ele pe când directivele de conversie indică formatul de ieşire pentru argumentele transmise în apelul funcţiei. E. O directivă se specifică prin caracterul % urmat de o secvenţă de caractere ce identifică formatul.ddddddE ± xx. dacă acesta este hexazecimal se va adăuga ox sau OX înaintea numărului. întreg în notaţie zecimală fără semn real în notaţie zecimală sub forma [-]mmm. Numărul de d esteargument de precizie (implicit este 6). Indicatorii disponibili în C sunt: . f. Se pot utiliza şi dimensiuni variabile: se utilizează '*' în loc de numărul ce indică dimensiunea iar variabila respectivă se pune în lista de argumente. nu există nici conversie nici afişare în acest caz: este vorba de referinţe la un pointer la un întreg în care se va stoca numărul de caractere afişate până în prezent. G) rezultatul se va afişa cu un punct zecimal. g. • eventual un număr pentru a indica dimensiunea minimală a câmpului în care se va afişa.cu excepţia cazului când primul caracter nu este semn. Pentru formatele flotante ( e. c. Formatele ce se folosesc în C sunt date în Tabelul 7. B. #: desemnează un format alternativ. pentru x sau p se folosesc literele a. f. Permite afişarea caracterului ‘ % ‘ d sau i o x.

Programul 4 # include <stdio. printf(" i = %lo\n\n".-123. 123. In urma execuţiei se va afişa: Forma zecimala: i = 123 l = 123456789 ui = 45678 ul = 987654321 Forma octala: ui = 131156 ul = 7267464261 printf("Scrierea hexazecimala:\n"). ul). long int l = 123456789L. 123). ui). printf(" ui = %o\n". printf(" ul = %lX\n\n". printf(" ul = %lu\n\n". printf("|%o| |%#o|\n". printf(" l = %ld\n". unsigned long int ul = 987654321UL. i). double d = 123. GRIGORAŞ Programare C Exemple Următoarele programe ilustrează folosirea formatelor de afişare la apelul funcţiei printf rezultatele sunt ilustrate la sfârşitul fiecărui program.-123). printf("|%X| |%#X|\n". l).Gh. 123. printf(" ui = %X\n". printf(" Forma zecimala:\n").ul). printf("\n"). 123). 123. printf(" ui = %u\n". ui). printf(" Forma octala:\n").45. În urma execuţiei se va afişa: Scrierea hexazecimala: ui = B26E ul = 3ADE68B1 24 .-123. 123. printf("Afisarea semnului:\n"). printf("|% d| |%+d| |%d|\n". ui). printf(" ul = %d\n".h> int main ( ) { int i = 123. printf(" i = %d\n". 123). i). printf("|% d| |%+d| |%d|\n". hexazecimal:\n"). ul). 123. unsigned int ui = 45678U. printf("Afisare in octal si printf("|%x| |%#x|\n". 123).

0001.4d|\n".4d|\n".1).4d|\n".14157). 123).123. printf("|%. 3.0E|\n" 3. printf("|%f|\t|%G|\n". printf("|%. GRIGORAŞ Programare C 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("|%.Gh. 123). 123).4o|\n". printf("|%#.1.00001). printf("|%+6. printf("|%+6.3f|\n" 3. 3.3E|\n" 3.11G|\n".123. 3. printf("|%G|\t|%G|\n". 3.4X|\n". printf("\n"). printf("|%. 3.1e11).0E|\n" 3. 123. printf("|%. printf("|%#5d| |%#-5d|\n". printf("\n"). printf("|%E|\t|%G|\n". printf("|%+6.14157e123). 3. printf("|%#.4X|\n". 25 .14157e123). printf("|%+5d| |%+-5d|\n". 123).14157e123).11G|\t|%.0f|\n" 3.1e6.110). printf("|%+. printf("|%E|\n" 3.14157).14157e123).14157e123). 123).14157). printf("|%5d| |%-5d|\n". 3. -0. printf("|%f|\t|%G|\n". Iată ce se va afişa: |0123| |+0123| |0X007B| | 123| |123 | | +123| |+123 | | 0X7B| |0X7B | | +0123| | 0173| |0X007B| /* Afisarea numerelor in virgula flotanta*/ printf("|%f|\n" 3. printf("|%. printf("\n").0f|\n" 3. 123). 3. 123).1e7). printf("|%. printf("|%E|\n" 3.1e5. 123). 123). 0.14157).1e10.

directive de specificare a formatului.142E+123| |3E+123| |3.0001| |1E-05| |310000| |3. caracter.1| |3.142| |3| |3.1E+10| |-1. În general. întreg în notaţie octală(primul caracter 'o') sau hexa (primul caracter 'Ox' sau 'OX'). const char* şir_format. Se poate face o omisiune de afectare. GRIGORAŞ Programare C return0. const char* şir_format. care încep cu '%'.…). } Iată ce se va afişa de această dată: |3. citindu-se şi spaţiile care.E+123| |3. Rezultatul interpretării este încărcat la adresele ce se dau ca argumente la apelul funcţiilor de citire. Citirea cu format Funcţiile de citire cu format permit interpretarea unui şir de caractere ca o secvenţă de valori în concordanţă cu un format dat. .100000E+10| |3. %*s): în acest caz câmpul este citit dar nu-i memorat. Dacă este indicat un număr q (%qC) atunci sunt citite cel mult q caractere. corespunzătoare citirii de la intrarea standard. Directivele pentru specificarea unui format sunt utilizate pentru precizarea conversiei următorului câmp citit.100000| |3.| |3. întreg în notaţie hexa. int fscanf (FILE* flux. Formatul (şir_format) este un şir de caractere care poate conţine: .…). de la un fişier respectiv dintr-un şir de caractere sunt: int scanf (const char* şir_format.caractere de spaţiere sau tabulare care vor fi ignorate. Aceste funcţii. întreg fără semn în notaţie zecimală.1E+07| |3100000| |3. Pentru citirea următorului 26 TIPUL ARGUMENTULUI int* int* int* unsigned int* int* char* .141570| |3. int sscanf (char* s. de obicei sunt ignorate. Caracterele pentru specificarea unui format de citire sunt date în Tabelul 8.141570E+123| |1. întreg în notaţie octală. Câmpul ce urmează a fi convertit (ce este citit) se întinde până la primul spaţiu sau numărul de caractere din acest câmp este specificat în format. …).Gh.1 E+11| 4.5. Formatele de conversie pentru scanf CARACTER d i o u x c FORMAT DE INTRARE întreg în notaţie zecimală. .caractere ordinare (în afară de %) care trebuie să corespundă următorului caracter citit. punând caracterul '*' în directivă (de ex. Implicit se citeşte un singur caracter. rezultatul citirii este memorat în variabila corespunzătoare din lista argumentelor funcţiei. Tabelul 8.

&i3).. printf("s1=|%s|\ts2=|%s|\ts3=|%s|\n".s3). scanf("%4d%3d%4d". char s1[10]. printf("Ati introdus:%d\t%d\t%d\n". şir de caractere: se citeşte până la întâlnirea unui spaţiu sau până la atingerea numărului de caractere indicat. scanf("%4s %4s %4s". s1.s5. char* float* double* void* int* char* char* Programul următor ilustrează utilizarea formatelor de intrare. GRIGORAŞ Programare C s e.i3).h> int main(){ int i1. &i3). printf("Introduceti trei numere intregi: ").i3).s4. Programul 5 #include <stdio.] [∧. i2. scanf("%s%s%s. s1. s5[5].s1.Gh.s2. &i1. real în notaţie virgulă flotantă. char s4[5].. Adaugă la argument caracterul '@0' (sfârşit de şir).&c2. scanf("%d%d%d". f.s1. &i1. &i2. s4[4] =s5[4] =s6[4] ="\0". citeşte caracterele din intrare atât cât acestea nu sunt în mulţimea indicată în [ ] după care adaugă '@0'.s5.s6) 0. printf("c1= |%c|\tc2=|%c|\tc3=|%c|\n"c1. c2. printf("Introduceti un text: ").i1. i3. printf("Ati introdus:%d\t%d\t%d\n". g p n [. s6[5].i2.s6). char c1. citeşte caracterele din intrare atât timp cât acestea aparţin mulţi-mii indicate în [ ].s3).. s2. &i2. s3). permite indicarea caracterului literal '%'. valoarea unei adrese. printf("Introduceti un text: "). Nu se face nici o operaţie de citire.s3[10].i1.] % caracter diferit de spaţiu se foloseşte %ls. scrie în argument numărul de caractere citite până acum. &c1. scanf("%4c%4c%4c".. s4.s2.c2. s3"). printf("s4=|%4s|\ts5=|%4s|\ts6=|%4s|\n". printf("Introduceti trei numere intregi: "). c3.s2[10]. s2.&c3). printf("s1=|%s|\ts2=|%s|\ts3=|%s|\n". printf("Introduceti un text: "). } return Rezultatul unei execuţii a acestui program este: Introduceti trei numere intregi: 5454 -9988 0 A-ti introdus numerele: 5454 -9988 0 27 .i2.c3). scanf("%c%c%c".

GRIGORAŞ Programare C 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!| 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| 28 .Gh.

5. real (float sau double) 29 . 5. end din alte limbaje).3) : • • • • • tip tip identificator. cod = printf ("%d\n". blocuri de instrucţiuni. Un bloc este un şir de instrucţiuni încadrat în acolade: { şi } (care joacă rolul de begin .while for break continue go to Limbajul C oferă următoarele tipuri de instrucţiuni: declaraţii. s [i] = t [j]. Iată un bloc în C: { i = 1. În capitolul 2 am văzut cum se construiesc expresiile în C. short. 5. j = 2. 5. definiţii de funcţii. Instrucţiunea if if (expresie) instrucţiune1 else instrucţiune2 if ( expresie ) instrucţiune1 Ca şi în alte limbaje de programare această instrucţiune permite alegerea între două alternative.Gh. O expresie urmată de caracterul '.7. alpha). identificator = valoare. expresii. instrucţiuni de control. 5. Un bloc este echivalent cu o instrucţiune. s [i] = t [j].3.1.1.4.5. 5. int sau long).2. 5. Tipul expresiei de după cuv=ntul rezervat if poate fi întreg (char. Vom trece în revistă în continuare instrucţiunile de control ale limbajului C. 5.. 5. GRIGORAŞ Programare C Cap 5 INSTRUCŢIUNI DE CONTROL 5.9..6. j ++. Următoarele construcţii sunt instrucţiuni C de tip expresii: i = 1. i ++.' este o instrucţiune în C. Instrucţiunile Instrucţiunea Instrucţiunea Instrucţiunea Instrucţiunea Instrucţiunea Instrucţiunea Instrucţiunea Exerciţii if switch while do. } Instrucţiunile de declarare a variabilelor (declaraţiile) sunt de forma(vezi şi paragraful 2.. în general o instrucţiune se termină prin '. ++ k.8. j --.'..

’ 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”). if ((c = getchar ( ) ) ! = EOF) putchar (c). else a -=b. if ( a < b ) { a += 2. deci exemplul este interpretat astfel: if ( a == 1 ) if ( b == 2 ) printf (“ b este egal cu doi\n”). y). if (x>y) printf("primul numar este cel mai mare\n")." este facultativă. else if (x==y) printf("numerele sunt egaleăn"). }. 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 ‘. else y = y-x. else printf (“ b nu este egal cu doi\n”). else printf("%d este cel mai mareăn". 30 . Exemple: if (x>y) x = x-y. 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ă). if (x>y) printf("%d este cel mai mareăn". b += 1. x). Cărui if îi este ataşat else? Limbajul C rezolvă problema aceasta prin ataşarea lui else celui mai apropiat if. else printf("al doilea numar este mai mare\n"). Expresia se pune obligatoriu între paranteze iar partea "else instrucţiune2. else printf (“ b nu este egal cu doi\n”). GRIGORAŞ Programare C sau tip pointer.Gh.

dacă valoarea sa este const2 atunci se execută instrucţiune2 şi următoarele p=nă la "break" etc. switch (getchar ( ) ) { case 'a': printf ("s-a citit break. n++ } int x. default: printf ("s-a citit break. altceva decat a.Gh. Exemplu: int c. litera b\n"). dacă expresia nu are nici una din valorile indicate prin cons1. case 'c': printf ("s-a citit break. y. case 'b': printf ("s-a citit break. este evaluată şi dacă aceasta nu este nulă. caracter sau enumerare. Cazul default este facultativ. Aceasta permite execuţia unei anumite ramuri în funcţie de valoarea expresiei "expresie" din paranteză. n = 0. se execută "instrucţiune" (care poate fi.. cons2. ce trebuie pusă între paranteze. 31 . un bloc). Instrucţiunea while while(expresie) instrucţiune Expresia "expresie". se execută instrucţiunile desemnate de ramura "default". După execuţia acestei instrucţiuni se face din nou evaluarea expresiei indicate în while şi se repetă execuţia până când evaluarea expresiei returnează o valoare nulă.. 5. 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.3.2. } Exemplu: litera a\n"). litera c\n"). 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". b sau c\n").. binenţeles. while ((c = getchar( )) != EOF){ putchar (c). GRIGORAŞ Programare C 5. . care poate fi de tip întreg. dacă aceasta există.

return 0. dacă "expresie" este evaluată prima dată la zero.. Spre deosebire de instrucţiunea while. break. else y = y-x. nspatii..5 spatii si 13 alte caractere. Instrucţiunea do . "instrucţiune" nu se execută.. Execuţia se termină atunci c=nd "expresie" are valoarea nulă. c. GRIGORAŞ Programare C while (x!=y) { if (x>y) x = x-y.Gh. naltele. } Programul următor ilustrează utilizarea instrucţiunii while şi a instrucţiunii switch... printf ("Introduceti un text\n -->").nvocale. spaţiile goale şi restul caracterelor din acesta.4.while (adică "instrucţiune") se execută la intrarea în această buclă după care se testează condiţia "expresie": dacă aceasta are valoare nenulă se execută din nou "instrucţiune" etc. default: naltele++. 32 . } Rezultatul unei execuţii: Introduceti un text: --> Iata un text pentru test ! In text sunt 8 vocale. Aşadar în cazul do. } } printf("In text sunt %d vocale. do Exemplu: int n = 0.\n".h> int main (void){ int nvocale. %d spatii si %d alte caractere. "instrucţiune" se execută măcar o dată pe c=nd în cazul while. nvocale = naltele = nspatii = 0.naltele).nspatii. Acest program analizează un text şi numără vocalele. 5. char c. while instrucţiune while(expresie). 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++.while. break. "corpul" instrucţiunii do.. case ' ': case '\t': nspatii++. Programul 6 # include <stdio.

printf ("Linia contine %d caractere \n". for(. • instrucţiunea expr3 se execută la fiecare sf=rşit de buclă. expr3) instrucţiune Aceasta este echivalentă cu secvenţa dată mai sus (2. int n = 1. i).) Exemplu: i = 0. • se execută (o singură dată) expr1 şi se intră în buclă (execuţia de n ≥0 a instrucţiunii "instrucţiune"). } while (c!= '\n'). n-1). c = getchar ( ) while (c!= '\n') { c = getchar ( ). . while (expr2){ instrucţiune expr3. expr2. expr3) instrucţiune Semantica acestei instrucţiuni poate fi descrisă în două moduri: 1. expr2 sau expr3 sau prin suprimarea corpului ("instrucţiune"): • Suprimarea iniţializării. Se constată uşor că cele două secvenţe de program sunt echivalente. i++) putchar (c). do { 5.(c = getchar( )) != '\0'. Instrucţiunea este echivalentă cu secvenţa: expr1. Să observăm că iniţializarea lui i s-a făcut în afara instrucţiunii for ! • Suprimarea expresiei de control: for( expr .) din care lipseşte prima instrucţiune (expr1. 2. n++ }. Instrucţiunea for for (expr1. n-1). c. expr3) 33 .5. GRIGORAŞ Programare C c = getchar ( ). n++. • expresia expr2 se evaluează şi se testează la fiecare început de buclă: dacă este adevărată atunci se execută "instrucţiune" altfel bucla se termină. } Instrucţiunea poate fi folosită şi prin suprimarea uneia dintre expr1. printf ("Linia contine %d caractere\n".Gh. printf("numarul de caractere:%d\n". for( . expr2 .

expr2 .Gh. (c = getchar( )) != '\0'. expr3 ) . i). • Suprimarea expresiei finale: for( expr1 . Exemplu: for (i = 0. • Suprimarea iniţializării şi a expresiei finale: for( . . getchar( )!= '\n'. 34 . • Suprimarea corpului buclei: for( expr1 . c = getchar ( ). GRIGORAŞ Programare C instrucţiune Aceasta este o buclă infinită. ) instrucţiune Exemplu: for (i = 0. expr2 . (c = getchar( )) != '\0'. i++). Exemplu: for (i = 0. i++) if (c == '\n') break. i). { putchar (c) i++ } printf ("numarul de caractere: %d\n". Singura modalitate de a ieşi din aceasta este utilizarea instrucţiunii break.){ putchar (c) i++ } printf ("numarul de caractere: %d\n". ) instrucţiune Exemplu: i = 0 for (. expr2 .

numar <= 100.Gh. } } printf ("Iesire din bucla while\n").5) break. sum <= 25. } în acest caz instrucţiunea break permite ieşirea din bucla for. Iată un exemplu: int numar. if (sum>=50) { printf ("50\n"). suma++ = numar. while ( ( c = getchar( )) != EOF) { sum++. numar++) { if (numar % 5 == 0) continue.8. Este recomandat să se evite folosirea instrucţiunii go to (aşa cer principiile programării structurate) dar uneori este utilă. 5. Instrucţiunea break Instrucţiunea break permite ieşirea dintr-o buclă (for. p=nă la sf=rşitul buclei în care se află. do) sau dintr-o instrucţiune cu alegeri multiple (switch). ea provoacă trecerea la următoarea iteraţie. for (j = sum. sum).6. Se observă că prin continue se evită adăugarea la suma a numerelor multiple de 5. GRIGORAŞ Programare C 5. în exemplul următor se ilustrează folosirea instrucţiunii go to şi modul cum ea poate fi evitată: 35 . 2. Etichetele sunt nume ce se pun în faţa instrucţiunilor ţintă urmate de ":". în cuprinsul aceleaşi funcţii. Instrucţiunea go to Instrucţiunea go to provoacă saltul necondiţionat la o etichetă. } printf ("Total: %d". suma). suma = 0. 5.5. while. if ( j >= 75.7. Instrucţiunea continue Instrucţiunea continue ignoră toate instrucţiunile ce urmează după ea. } print ("Suma este:%d\n".3): 1. for (numar = 1. go to eticheta . break. while ( ( c = getchar( ))!= EOF) { sum++. j++) { j* = 1. În acest prim exemplu. instrucţiunea break permite ieşirea din bucla while după citirea a 50 de caractere şi nu la înâlnirea caracterului EOF. Iată câteva exemple de utilizare a acestei instrucţiuni (vezi şi programul de la sfârşitul paragrafului 5.

Numerele n şi l se definesc ca valori constante.j. aliniată la sfârşit de linie. .i. j < m.calculul factorialului acestui număr (iterativ). for (i = 0. în bucla de citire. Ultima linie va conţine suma sumelor. if (gasit) printf (" a[%d]= b[%d]= %dăn".Gh. j++) gasit = (a [i] == b [j]). else printf ("Tablourile nu au elemente comune \n"). . i < n.j. Scrieţi un program care citeşte 10 numere întregi şi pentru fiecare din acestea calculează numărul biţilor ce au valoarea 1.i. 3.a[i]). gasit: printf (" a[%d]=b[%d]=%d\n". adăugând la sfârşitul liniei suma lor. j++) if (a [i] == b [j] ) go to gasit. Scrieţi un program care citeşte un număr hexazecimal (ca o succesiune de caractere). printf("Tablourile nu au elemente comune\n"). GRIGORAŞ Programare C for (i = 0. 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. . verifică validitatea sa şi efectuează conversia în zecimal. 36 .a [i]). . i++) for (j = 0. Se va utiliza o iteraţie pentru citirea caracter cu caracter a numărului. int gasit = 0. . Scrieţi un program care să realizeze următoarele: . i < n && ! gasit. Exerciţii 1. Scrieţi un program care citeşte n valori întregi şi le afişează câte l pe fiecare linie. se va face apel la o funcţie ce numără biţii 1. 4.afişarea rezultatului. 2.9. i++) for (j=0. 5. 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). j < m &&! gasit.citirea unui număr natural (dat de utilizator).

pi = &i. incorectă (semantic) în limbaj C. dereferenţiere.5. 6. Varianta corectă este: void schimbare_corecta (int *p. Utilizarea operatorului "*" este destul de delicată. aplicat unei variabile. permite obţinerea adresei acesteia: int i. 6. Operatorul unar "*". b = temp.) nu are nici un efect asupra variabilelor x. 6. GRIGORAŞ Programare C Cap 6 TABLOURI ŞI POINTERI 6.1. este: void schimbare (int a. int *q){ int temp. char *pc. prin declaraţiile: int *pi. 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. în limbajul C transferul implicit al parametrilor este prin valoare. permite derefenţierea unui pointer. O aplicaţie importantă privind utilizarea pointerilor este transferul prin referinţă al parametrilor unei funcţii. Exemplul clasic pentru acest procedeu este acela al funcţiei de interschimbare a două variabile. se specifică faptul că pi este un pointer la o variabilă de tip întreg iar pc un pointer la o variabilă de tip "char". Un pointer este o variabilă ce conţine adresa unei alte variabile.y (actuale) pentru că funcţia lucrează cu variabilele ei locale a şi b. int b) { int temp = a. 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.10.12. 37 . 6. 6.1. furnizând valoarea variabilei pointate de acesta: int x. 6. 6. trebuie transmisă funcţiei adresa variabilei iar în corpul funcţiei să se folosească operatorul de dereferenţiere. 6.4.Gh. } Un apel al acestei funcţii (prin schimbare (x. 6. y).6. x = *pi este echibvalent cu x = i.2. Operatorul unar "&" numit operator de referenţiere sau adresare. Dacă se doreşte modificarea unui parametru formal. Forma "clasică" a funcţiei.3. De exemplu. 6. Aşa cum s-a precizat la capitolul "Funcţii".9.7. 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. indirectare.8.11. 6. uneori periculoasă: acesta permite citirea sau scrierea în orice zonă de memorie. a = b. Pointeri tip *identificator.

GRIGORAŞ Programare C temp = *p.2. char s[4] = {'a'. care se închid între acolade. 'b'.. 2. Iniţializarea unui tablou Iniţializarea unui tablou se face prin semnul "#" după declaraţia acestuia urmat de o listă de valori iniţiale. Un tablou este o colecţie de elemente(date) de acelaşi tip. 'c'. Tabloul va avea structura: 38 . tip nume[dim] = { valoare_1. vector[9] La declararea unui tablou se alocă o zonă de memorie contiguă care să stocheze toate elementele tabloului.Gh. y = 2. 4}. }.1. 3. 6. Adresa primului element (cel de indice 0) este şi adresa tabloului iar celelalte elemente se raportează la acesta.care trebuie să aibă valoarea între 0 şi dimensiune . Tablouri cu o dimensiune tip nume[dimensiune]. '\0'}. Primul element al tabloului este cel de indice 0 iar ultimul este cel de indice dimensiune -1. valoare_2. în declaraţia de mai sus. 6.2. Referenţierea unui element al tabloului Elementele unui tablou pot fi accesate prin numele tabloului urmat de o expresie între paranteze pătrate . separate prin virgule. # define marime 5.2.precum şi dimensiunea sa.1. schimbare_corecta (&x.numită expresie indice . o declaraţie de tablou cu o dimensiune conţine tipul tabloului (tip).care este un identificator .2. int a[4] = {1. cele 10 elemente ale tabloului vector sunt: vector[0]. &y). întreg. tab[1] = tab[2] = 'b'. vector[1]. Dimensiunea este o constantă întreagă şi specifică numărul elementelor tabloului. *q = temp. } Apelul corespunzător este: int x = 1. 6. *p = *q. tab[3] = tab[4] = 'd'. tab[0] = 'a'. 2. char tab[marime].. dimensiune. Aşadar.…. defineşte un tablou cu numele "vector" care are 10 elemente de acelaşi tip. fiecare din acestea put=nd fi accesibilă. . Exemple: 1. Declaraţia int vector[10]. numele tabloului (nume) .

sunt echivalente. atunci celelalte valori se iniţializează cu zero. acest caracter notează sf=rşitul şirului( vezi paragraful 6. ultimul element al tabloului este caracterul ‘ă0’. i++) { scanf (" %f". 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. '\0'}. este echivalentă cu: char s[6] = {'a'. Spre exemplu dacă se declară int a[3]. i < 20.5). 'd'. b[1]. } 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. 'e'. aceasta se determină de către compilator: float x[ ] = {2. 'b'. în general "*(pa+i)" este totuna cu tab[i].1] = 'e'. este echivalentă cu: char s[ ] = {'a'. GRIGORAŞ Programare C 'a' 'b' 'b' 'd' 'd' tab[marime . declaraţia: char s[6] = "abcdef" .2. float x [20]. c[3]. 'c'. 0. 'f'}. 1}. Spre exemplu int a[10] = {5.2 3 4 0 În cazul tablourilor de caractere. în sensul că ele reprezintă adresa aceleiaşi zone de memorie. De notat că în cazul sirurilor de caractere ce se memorează într-un tablou. 3. 'b'. 4. int *pa. 6. for (i=0. 'f'. iniţializarea se poate face şi cu şiruri de caractere incluse între ghilimele. Notaţia "*pa" este echivalentă cu notaţia tab[0] iar notaţia "*(pa+1)" este echivalentă cu notaţia tab[1]. tab[marime/2] = 'c'. 2. c[-2]. 'e'. iar declaraţia: char s[ ] = "abcdef". 'd'. b[3].Gh. 5. Aşadar. 0}. Ultima instrucţiune asignează variabilei pointer pa adresa primului element al tabloului tab.3. poate fi omisă dimensiunea. Următoarele secvenţe for sunt echivalente: 39 . 2 0 5. 'c'. Relaţia între pointeri şi tablouri Să considerăm următoarea secvenţă: int tab[10]. ş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]. Iniţializarea unui tablou se poate face şi prin citirea succesivă a tuturor elementelor sale: int i. pa = &tab[0]. are ca efect crearea tabloului: 5 2 3 1 0 0 0 0 0 0 În cazul unui tablou care se iniţializează. 3. &x[i]).

Să urmărim următorul exemplu: float ftablou[10]. pa = &tab[0]. Notaţia pointer Notaţia tablou ptr = tablou i=0 ptr1 = ptr + 5 i=i+5 ptr + i tablou [i] ptr + 4 tablou [4] ptr . i < 10. Prin convenţie.". i++. echiv. float *pf = ftablou. Trebuie precizat însă că unitatea care intră în discuţie nu este octetul ci adresa obiectului pointat. for ( i = 0. Şiruri de caractere În limbajul C.3 tablou [-3] ptr1 . se utilizează tablourile de caractere. nu există un tip de bază pentru şiruri (lanţuri) de caractere. i++) În limbajul C. i < 10. cu declaraţiile de mai sus. i++) *(pa+i) = 100.5. i < 10. după incrementare. for ( i = 0. Nu sunt valide expresiile: tab = pa.Gh. este perfect validă asignarea pa = tab. echivalenţa între elementele unui tablou şi numele său se exprimă prin: tablou[i] &tablou[i] echiv. ultimul caracter dintr-un astfel de lanţ este caracterul NULL (' \0'). tab ++. numele unui tablou poate fi utilizat ca pointer (constant) la adresa de început a tabloului. *(pa++) = 100. Aşadar se poate scrie şi: for ( i = 0. pa++) *pa = 100. În rezumat.ptr j-i ptr ++ i ++ 6. Pentru a reprezenta un şir de caractere. În acest exemplu.4. i++) tab[i] = 100. incrementarea/decrementarea (++. pa = tab. Tabloul de caractere (pe care-l numim mesaj): 's' 'a' 'l' 'u' 't' '!' ' @o' 40 . pf++. --) şi scăderea a doi pointeri de acelaşi tip. pf pointează la cel de-al doilea element al tabloului ftablou. GRIGORAŞ Programare C for ( i = 0. *(tablou + i) tablou + i 6. care este echivalentă cu "pa=&tab[0]. Operaţii aritmetice cu pointeri Operaţiile aritmetice permise asupra pointerilor sunt adunarea/ scăderea unei constante. pa = &tab[0]. i < 10. De aceea. Iată şi alte exemple: în partea stângă se descrie o expresie în care apar pointeri iar în dreapta expresia echivalentă în termenii tabloului.

char mesaj[ ] = "salut!". &mesaj[0]).. În această declaraţie. int t[3][4]. Pentru a accesa un element dintr-un tablou există mai multe moduri de notare. Tablouri cu mai multe dimensiuni Declararea unui tablou cu N dimensiuni se face prin: tip nume[dim_1][dim_2]. 6.. datorită numerotării indicilor de la 0).'a'.Gh. Î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] 41 . Există o bibliotecă de funcţii standard pentru manipularea şirurilor de caractere care se prezintă în anexă. Un tablou declarat în acest mod se compune din dim_1*dim_2*. în concordanţă cu modul de stocare a elementelor tabloului în memorie. .'t'. 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). char mesaj[7] = "salut!". mesaj[0]).[dim_n]. printf("%s"..6. O primă viziune a reprezentării acestuia în memorie este cea naturală a unei matrice: 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] Elementeul haşurat din acest tablou se referenţiază prin t[1][2] (este elementul de pe linia 2 şi coloana 3 din matrice.'u'. 'ăo'}. dim_1. 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).'l'. Citirea respectiv tipărirea acestui şir se face prin instrucţiunile: scanf("%s". mesaj).. mesaj). GRIGORAŞ Programare C poate fi declarat prin una din următoarele trei instrucţiuni: char mesaj[7] = {'s'.'!'. dim_n trebuie să fie constante.. scanf("%s"..*dim_n elemente de acelaşi tip (declarat) stocate într-o zonă contiguă de memorie. printf("%s".. dim_2.

Să exemplificăm aceste moduri cu referiri la acelaşi tablou pe care l-am reprezentat mai sus. 11. Aşa cum referenţierea unui element al tabloului poate fi făcută în mai multe moduri. 7.Gh. for (i = 0. "sambata". 8. O problemă importantă relativ la tablourile cu mai multe dimensiuni este cea a iniţializării acestora. 3. t [1][0] cu 5 iar t [1][1] cu 6. 4}. Tablouri de pointeri Pointerii fiind variabile. 7. • iniţializarea fiecărei dimensiuni de tablou. i < n. 8}. zi[0] 'l' 'u' 'n' 'i' zi[1] 'm' 'a' 'r' 't' 'i' zi[2] 'm' 'i' 'e' 'r' 'c' 'u' 'r' 'i' zi[3] 'j' 'o' 'i' zi[4] 'v' 'i' 'n' 'e' 'r' 'i' zi[5] 's' 'a' 'm' 'b' 'a' 't' 'a' zi[6] 'd' 'u' 'm' 'i' 'n' 'i' 'c' 'a' Să considerăm un tablou de pointeri de tip char. }. • iniţializarea numai a unor elemente din tablou: int t[3][4] = {{1}. 5.{9. şi iniţializarea unui tablou se poate face în moduri diferite (sintactic vorbind). "marti". 4. separat: int t[3][4] ={{1. pot intra în componenţa unui tablou. "miercuri".{5. char *zi[7] = {"luni". 10. "duminica"). restul elementelor sunt iniţializate cu zero.{5. int n) { int i. "joi". 6. 10. Astfel t [0][0] va fi iniţializat cu 1. 3. 2. 9. GRIGORAŞ Programare C 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ă). t[3][4]: • iniţializarea prin enumerarea tuturor elementelor tabloului ( în ordinea liniilor dacă-i vorba de matrice): int t[3][4] = {1. 6. 6. Un tablou unidimensional de pointeri corespunde la un tablou (cu două dimensiuni) de elemente de tipul pointat. i++) if (tabp [i] != NULL) printf ( "%săn". cu numele tabp. 12}}. 2. Iată cum se declară un tablou de pointeri cu 7 componente ce se iniţializează cu adresele şirurilor constante ce reprezintă numele zilelor dintr-o săptăm=nă.7. tabp[i] ). "vineri". 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 [ ]. 12}. 11. 6}}. } Apelul acestei funcţii pentru scrierea zilelor săptămânii este: 42 .

"maloc" şi "free" joacă un rol important pentru că ele permit alocarea / dealocarea în mod dinamic a spaţiului din memorie. De pildă. GRIGORAŞ Programare C scrie (zi. . Alocarea dinamică a memoriei este deosebit de utilă atâta timp cât programatorul foloseşte structuri de date pentru care nu ştie dimensiunea la scrierea programului. pozitiv dacă s1 > s2 şi zero dacă s1 = s2.. element *ptr. tabp [i+1]) > 0) { temp =tabp [i].8. . char *temp. după utilizarea memoriei tampon şi ptr (în exemplul de mai sus) se scrie (în program) 43 . i++) if (strcmp (tabp [i]. Pentru specificarea mărimii unui tip particular. Dacă nu mai este memorie disponibilă (nu se poate face alocarea) funcţia malloc returnează valoarea NULL. sortat = 0. tabp [i] = tabp [i+1]. /* s-a alocat o memorie tampon de 512 caractere*/ ptr = malloc(32*sizeof(element)). 7).. # include <stdlib. 7). rezultatul este negativ dacă s1 < s2. Iată un exemplu: char *tampon. sortat = 0. Să mai dăm un exemplu de funcţie care sortează lexicografic (alfabetic) şirurile pointate de tabp: void sortare (char* tabp[ ].Gh. 6. Funcţia malloc are ca argument numărul de octeţi doriţi a fi alocaţi şi întoarce ca rezultat un pointer către zona de memorie liberă alocată de sistem.. se poate utiliza operatorul "sizeof" (vezi secţiunea următoare). în corpul funcţiei sortare s-a folosit un apel la funcţia strcmp care compară 2 şiruri de caractere s1 şi s2. int n){ int i. i < n-1.. for (i = 0.h> void *malloc (size_t dim). în cursul execuţiei programul poate aloca memorie în limitele impuse de sistem. while (! sortat) { sortat = 1. /*s-a alocat o zona de 32 de "elemente"*/ if ((tampon == NULL || ptr == NULL)) { printf ("Alocare imposibila ! \n"). Funcţia "free" permite dealocarea spaţiului de memorie alocat în prealabil de malloc. Pointeri şi alocarea memoriei Printre funcţiile de alocare a memoriei (vezi Anexa ). } } } Apelul pentru sortarea alfabetică a zilelor săptămânii este sortare(zi. tabp [i+1] = temp. /*presupunem ca tipul element este definit undeva */ -------tampon = malloc(512). Pointerul returnat este de tip "void" ce corespunde unui pointer la o variabilă de tip oarecare. Acest spaţiu este posibil de accesat cu ajutorul variabilelor de tip pointer. void free(void *ptr).

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*”. prin urmare operatorul sizeof nu oferă dimensiunea memoriei ocupată de tablou ci dimensiunea tipului pointer. De exemplu declaraţia: char* (*p)(char*. free (((char*)ptr). Să notăm în acelaşi timp că. celălalt de tipul const char*. Acest lucru se ilustrează în exemplul următor: double tab [100]. De această dată parantezele sunt obligatorii. 6.const char*). Declaraţia unui pointer la o funcţie este de forma: tip (*nume)(lista_arg). Este posibil. atunci c=nd este parametrul unei funcţii. void f(double[ ]). precizează că p este un pointer la o funcţie de tip char* (pointer la tipul char). /* i este initializat cu sizeof(double)*/ } Pentru a obţine dimensiunea memoriei ocupată de un tip anume se foloseşte: sizeof(tip). Dacă se aplică unui tablou. /* i este initializat cu 100 * sizeof(double)*/ f(tab).. de a defini o variabilă de tip pointer către o funcţie. nume: este identificatorul pointerului la funcţie. /*declaratia este echivalenta cu void f(double*)*/ int main( ){ int i = sizeof tab. Operatorul “sizeof” sizeof expresie. tip: este tipul returnat de funcţia pointată. operatorul sizeof oferă dimensiunea în octeţi a memoriei ocupată de întreg tabloul.9.10. are doi parametri.Gh. . pe de altă parte. 6. } void f(double t[ ]){ int i = sizeof t. Pointeri la funcţii O funcţie are un nume care este identificator dar acesta nu constituie o variabilă în C. Operatorul “sizeof” oferă dimensiunea în octeţi a tipului expresiei considerate(tipului respectiv). un tablou este considerat ca şi un pointer. Apelul sizeof(long).. unul de tip char*. oferă numărul de octeţi utilizaţi pentru a memora un întreg “long” (este incorect a scrie sizeof long). 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 ): 44 . Pentru a obţine numărul elementelor tabloului t se utilizează expresia “sizeof t” sau “sizeof tş0ţ”. lista_arg : este lista argumentelor funcţiei pointate. sizeof(tip). GRIGORAŞ Programare C free (tampon).

for(i = 0. în anexă sunt date funcţiile 45 . "Vasilescu Maria". int(*cmp)(const void* val1.Gh. Aşa cum s-a mai precizat.tab_nume[i]). comp_int).h> enum {n_max = 10}. "Constantiniu Calin". for(i = 0. printf("Lista initiala de nume : \n"). sizeof(int). return 0. "Vasiliu Catalina". printf("Nume ordonate lexicografic: \n"). /* tab_int este tabloul ce contine lungimile liniilor ce trebuie sortat */ char* tab_nume[n_max] = { "Ionescu Ion". qsort(tab_int.h> # include <stdlib.*(char**)p2)). i++) printf("%2d: %d\n". const void* n2) { return(*(int*)n1. i < n_max. i++) printf("%2d: %s\n".i. for (i = 0.i. "Vasilescu Cristi" }. "Constantinescu Ioana". /* tab_nume este tabloul ce trebuie ordonat lexicografic */ /* se definesc functiile de comparatie */ int comp_int (const void* n1.tab_int[i]). for(i = 0. } int main(void) { int i. const void* p2) { return(strcmp(*(char**)p1. i < n_max . Exerciţii 1. qsort(tab_nume. "Ion Gheorghe". } int comp_nume (const void* p1. i++) tab_int[i] = strlen(tab_nume[i]). "Constantin Vasile". în C.sizeof(char*).h> # include <string. 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. "Constantin Mircea". "Vasile Radu". size_t n. int tab_int[n_max]. n_max. i < n_max.*(int*)n2).comp_nume). GRIGORAŞ Programare C void qsort(const void* baza. i++) printf("%2d: %s\n". size_n t.n_max. } 6. şirurile de caractere sunt memorate în tablouri în care ultimul element este un delimitator de sfârşit de şir: caracterul NULL(‘\0’).i. printf("Lungimile numelor sortate:\n").tab_nume[i]). Iată un program ce utilizează funcţia qsort : # include <stdio. i < n_max.11.

dacă s1 = s2 se returnează 0 şi dacă s1 > s2 se returnează o valoare pozitivă. Să se scrie programe C care să implementeze funcţiile: int strlen(const char*s) : funcţie ce returnează dimensiunea şirului s. char* strcpy(char* dest. Dacă are loc relaţia s1 < s2.const char* sursa): funcţie ce concatenează şirul sursa la sfârşitul şirului dest şi returnează valoarea lui dest. 46 . char* strcat(char* dest.Gh. const char* s2) : funcţie ce compară şirurile s1 şi s2 conform ordinii lexicografice. GRIGORAŞ Programare C a) b) c) d) din biblioteca standard care se aplică şirurilor de caractere. int strcmp(const char* s1. se returnează o valoare negativă.const char* sursa) : funcţie ce copie şirul sursa în şirul dest şi returnează dest.

2 7. Aici “tip” este tipul ce se doreşte a fi redenumit iar nume_nou este numele ce se dă acestuia. De pildă un tablou permite a regrupa o mulţime de elemente de acelaşi tip. Sintaxa ei este: typedef tip nume_nou.7 Redenumirea unui tip Tipul structură Accesul şi iniţializarea câmpurilor unei structuri Structuri autoreferenţiate Tipul enumerare Uniuni Exerciţii În capitolul al doilea au fost introduse tipurile de bază ale limbajului C. În instrucţiunea de mai sus “nume” este facultativ. 47 . GRIGORAŞ Programare C Cap 7 CONSTRUIREA DE NOI TIPURI 7. Instrucţiunea permite redenumirea atât a tipurilor de bază (deja existente) cât şi a tipurilor construite.3 7.b. Iată câteva exemple: typedef long Mare. Tipul structură Instrucţiunea pentru definirea structurilor este: struct nume{ declaraţii }. 7. O variabilă de tipul “ struct nume” poate fi definită în aceeaşi instrucţiune în care este definită structura sau separat Iată câteva exemple: struct Persoana{ char nume[20]. Luna ianuarie = "ianuarie". char prenume[20]. typedef char Luna[10]. typedef char Mesaj[49]. Structura este o colecţie de una sau mai multe variabile (numite membri) grupate sub un singur nume. De asemenea există posibilitatea de a renumi (redenumi) un anume tip.4 7. Mesaj salut="Bine a-ti venit la cursul<Limbajul C!". Î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.Gh.1 7.6 7.1.2. 7. În anumite programe este util să regrupăm diferite variabile pentru a exprima o anume relaţie ce le caracterizează. Fiecare linie din partea “declaraţii” defineşte un câmp al structurii. Redenumirea unui tip Instrucţiunea typedef permite redenumirea unui tip.5 7. el este totuşi util pentru a identifica structura definită. Mare a.

short luna. char *grupa. struct Persoana colegul. }. variabilele “ionescu” şi “dan” sunt definite ca fiind de tip Persoana . urmată de numele câmpului. int an. avem: ionescu. 7. struct Student{ char *nume. } Persoana. short luna. popescu. Accesul la câmpurile unei astfel de variabile(pointer la o structură) se face adăugând după numele varibilei pointer o “săgeată” (caracterele – şi > ) .”) şi de numele câmpului selecţionat.3. } amicul. ionescu. urmărind exemplele din paragraful precedent.varsta este de tip int. Exemplu: necunoscut = &ionescu. 48 .data_nasterii. Elementul desemnat în acest mod este echivalent cu o variabilă de acelaşi tip cu câmpul definit în structură. Să observăm că structurile se pot defini fără nume. } struct Student ionescu. typedef struct{ short ziua. int varsta. } Data. Accesul şi iniţializarea câmpurilor unei structuri Accesul la câmpurile unei variabile de tip structură se face utilizând numele unei variabilei urmată de punct (“. popescu. amicul. short anul.nume este de tip char*. de pildă.Gh. Aici. iar necunoscut este o variabilă de tip pointer la structura Persoana . short anul. prin typedef li se atribuie un nume. struct Data data_nasterii. char *prenume. char prenumeş20ţ Data data_nasterii. GRIGORAŞ Programare C int varsta. *necunoscut. Să considerăm acum declaraţia: typedef struct{ char nume[40] . dan. typedef struct { char numeş20ţ. Persoana ionescu. Persoana ionescu. char prenume[40]. Iată.ziua este de tip short. } Persoana. care este un tip structură. struct Data{ short ziua.

Prin aceasta. 42 }. } 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.nume. fiecare nod este compus dintr-o informaţie (valoarea nodului) şi doi pointeri. câmpurile din variabila “ionescu” sunt copiate în câmpurile corespunzătoare din variabila “necunoscut”.h> # include <stdlib.nume = necunoscut->nume.. " " . unul la subarborele stâng altul la cel drept. typedef struct Arbore{ valoare val. dan. arbori etc. # include <stdio. struct Arbore *drept } Arbore. Are sens asfel: necunoscut = ionescu .4.varsta = (*necunoscut). Iată cum poate fi definită o astfel de structură: typedef struct { . Arbore* s. O formă echivalentă este următoarea: (*necunoscut). Structuri autoreferenţiate Utilizarea structurilor permite construirea de structuri de date evoluate ca: fişiere. struct Arbore* stang.. De pildă. Arbore* d). struct Arbore *stang.. într-un arbore binar. struct Arbore* drept. GRIGORAŞ Programare C necunoscut -> varsta = 41.varsta = 41. Iniţializarea unei variabile de tip structură se poate face în maniera în care se iniţializează un tablou: se descrie cu ajutorul unei liste de valori iniţiale câmpurile structurii: Persoana ionescu = { "Ionescu".h> typedef int valoare. "Vasile" . O atribuire de structuri este o copiere bit cu bit a elementelor corespunzătoare. /* s-a definit tipul "valoare" pentru informatia din nodurile arborelui*/ typedef struct Arbore{ valoare val. Două structuri de acelaşi fel pot să apară într-o expresie de atribuire. } valoare. Din acest motiv structura are un nume: Arbore.Gh. void afisare (Arbore* a). 0}. încât o astfel de expresie are sens doar dacă structurile sunt de acelaşi tip. Arbore* adauga(valoare v. 7. liste. Persoana necunoscut = { " " . 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”). int main( ){ 49 . dan.

să definim un tip care nu poate lua valori decât într-o mulţime precizată de valori constante. afisare(a2). afisare(a1). mai. NULL. NULL)). oct.Gh. enum Bool {nu. Primul identificator constant din lista de identificatori capătă valoarea 0. NULL. printf (")"). printf("Al doilea arbore este: \n"). Arbore* a2. Un tip enumerare permite ca. NULL). etc. adauga(20. } void afisare (Arbore* a){ if (a != NULL) { printf ("(%d". } else printf ("nil"). printf("\n"). Aici da are valoarea 0 iar nu are valoarea 1. adauga(3. enum Bool {da=1. mar. a->val . NULL)). dec}. c->val = v. nu}. GRIGORAŞ Programare C } Arbore* adauga(valoare v. Arbore* d){ Arbore* c. a2=adauga(10. return c. într-o maniera elegantă. " "). al doilea valoarea 1. printf("\n"). Exemple: enum Bool {da. afisare (a->drept). afisare (a->stang). c= (Arbore*)malloc (sizeof (Arbore)). adauga(4. 7. NULL). april. august.5. iulie. NULL. printf("Primul arbore este: \n"). nu=0}. nov. Arbore* s. enum Luna {ian=1. da} în ambele situaţii. c->drept = d. c->stang = s. sept. } 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)) Arbore* a1. printf (" "). a1=adauga(1. nu are valoarea 0 iar da are valoarea 1. adauga(2. Tipul enumerare Definirea unui tip enumerare se face prin instrucţiunea: enum nume { lista_de_identificatori}. feb. return 0. iunie. cu excepţia cazului în care programatorul oferă valori iniţiale acestor identificatori. 50 . a1.

În exemplul următor se specifică faptul că un punct în plan poate fi definit fie prin coordonatele sale carteziene fie prin cele polare. cum sunt de pildă p1 şi p2.p2.. float pi=3. printf("%f%f\n".ro. .. defineşte o listă de alegeri posibile. este vorba despre zone de memorie care.pol=2. Uniunea trebuie privită ca o structură în care membrii ocupă aceeaşi zonă de memorie.nec=0} TipCoord. Declaraţiile precedente pot fi modificate astfel (pentru a memora în variabilă însăşi tipul de reprezentare ales) : typedef enum {cart=1.. 3. Sintaxa este similară cu cea care defineşte o structură: union nume { declaraţii }. dimensiunea memoriei alocată pentru o variabilă de tip uniune este dimensiunea celui mai mare membru al uniunii. printf("%d%d\n". nu} Bool Bool raspuns=nu.1415926.cart. } Coordonate. Similar cu definirea variabilelor de tip structura se definesc variabilele de tip enumerare: enum Bool raspuns. 7.ordonata). Lista de declaraţii. 12. 51 . typedef union { TipCoord tip. obiecte de tipuri diferite. enum Bool spune=da. la un moment dat doar un membru al uniunii poate ocupa memoria.p2. } cart. long ordonata. Uniuni Uniunile sunt structuri care pot conţine (la momente de timp diferite). Coordonate p1. .Gh.abscisa..p1.pol.cart. celelalte luni vor avea respectiv valorile 2.6. long abscisa.. struct { TipCoord tip.teta). pentru că lui ian i s-a dat valoarea iniţială 1. GRIGORAŞ Programare C Aici. struct { float ro. O variabilă de tip Coordonate. poate conţine fie valori întregi reprezentând coordonatele carteziene fie valori flotante reprezentănd coordonatele polare. De fapt. float teta. la un moment dat conţin un anume tip de variabilă iar la alt moment acest tip se poate schimba. } pol. care definea câmpurile acesteia. spre deosebire de cea de la structură. p2. Desigur că în acest caz se presupune că prin program se află că p1 este de tip cart iar p2 este de tip pol. Să notăm că.p1. Evident se poate redenumi şi tipul enumerare: Typedef enum {da. typedef union { struct { long abscisa.pol.

val.val. } val.tip=2 7. } pol. GRIGORAŞ Programare C long ordonata. Iată un exemplu: andrei 1427 anca 2130 ana 2131 barbu 2140 bogdan 1430 … … ştefan 2143 victor 2114 zeno 1442 b. float ro. 52 . } cart. Exerciţii 1. d.tip sau p1. Câmpul “tip” este prezent în toate alternativele uniunii şi el poate fi consultat prin referinţa p1. iniţializarea unui tablou de elemente structurate prin citirea fişierului ce reprezintă agenda telefonică. union { struct { long abscisa.ordonata .tip sau p1. crearea programului care realizează următoarele: c.cart.tip=1 p2.abscisa p1. } cart. dacă p2.Gh. } pol.tip. float teta.pol.cart. } Coordonate. struct { TipCoord tip. long ordonata.cart. Scrieţi un program pentru consultarea unei agende telefonice la nivelul personalului unei societăţi.val. struct { float ro.teta .ro p2.val. Dezavantajul este că numirea coordonatelor se face printr-o “cascadă” mai mare : p1.pol. Acest fişier va conţine o mulţime de linii. dacă p1. Se mai poate elimina şi această redondanţă prin următoarea declaraţie: typedef struct { tipCoord tip.7.pol. } Coordonate. Pentru aceasta va trebui să se realizeze următoarele: a. crearea agendei telefonice sub forma unui fişier text. 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. consultarea agendei: la furnizarea unui nume programul oferă numărul de telefon corespunzător (dacă acesta există). float teta.

3. b. Coada. completarea tabloului de structuri cu noi adrese. alcătuirea unei liste de adrese care foloseşte un tablou de structură pentru stocarea informaţiilor legate de adrese (nume.: 2143 2.: 2130 anuta persoană necunoscută ştefan tel. codul poştal). strada. quit Să se scrie un program care să realizeze: a. oraş. GRIGORAŞ Programare C De exemplu: anca tel. Stiva. 53 .Gh. c. Să se scrie programe C pentru implementarea tipurilor abstracte de date: LLin. afişarea pe ecran a listei de adrese.

1. o variabilă este identificată printr-un nume (identificator).5 8. • automată.1 8. în acelaşi timp însă.6 8.Gh. static.2 8. Categoria de memorare (mem) căreia-i aparţine variabila va determina: vizibilitatea variabilei: identificarea părţii din program capabilă să folosească acea variabilă. 8.3 8.7 8. în memoria disponibilă. posedă un anume tip şi este administrată de un anume mecanism de memorare.4 8. interpretarea conţinutului variabilei atunci când ea este supusă unor instrucţiuni. extern. Vom trece în revistă aceste mecanisme de memorare. Compilatorul asociază în mod automat unei variabile un tip de memorare care corespunde locului unde s-a făcut declararea. identificator – este numele variabilei. unde: mem –este unul din cuvintele cheie: auto. Memorarea poate fi: • statică. 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. GRIGORAŞ Programare C Cap 8 GESTIUNEA MEMORIEI 8. durata de viaţă a variabilei: perioada de timp în care variabila este accesibilă. Aşadar. în segmentul datelor programului.8 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 Limbajul C oferă programatorului posibilitatea de a preciza mecanismul pe care-l doreşte pentru memorarea unei variabile. Precizarea unui mecanism de memorare Programatorul are posibilitatea să precizeze un mecanism de memorare prin instrucţiunea: mem tip identificator. Acest mecanism se referă la gestionarea memoriei în cursul execuţiei programului. register. 54 . iniţializarea variabilei: conţinutul implicit (dacă există unul) al variabilei la crearea sa. tip – este cuvântul cheie ce defineşte tipul variabilei. în general programatorul nu trebuie să se gândească explicit la un mecanism anume de memorare. • dinamică. în stiva de execuţie. Tipul variabilei va determina: dimensiunea zonei de memorie alocată pentru a reprezenta variabila. • într-un registru al calculatorului.

55 . nedefinită. Utilizarea acestei clase de memorare trebuie văzută ca o directivă de optimizare dată compilatorului. GRIGORAŞ Programare C 8. char mesajş ţ = "Mesaj initial". Exemple: auto float x. Gestionarea statică a memoriei static tip identificator . /*cuvantul auto a fost omis. Cum numărul de regiştri este limitat şi depinde de tipul de maşină pe care rulează programul. 8.3. Iniţializarea unei variabile “auto” se va efectua la fiecare intrare în bloc. Declaraţia “register” trebuie să fie plasată în interiorul unui bloc. o variabilă definită într-un bloc va fi o variabilă cu gestiune automată. va fi nedefinită. este imposibil a se obţine adresa unei astfel de variabile. Exemple de utilizare a unor astfel de variabile sunt în soluţia exerciţiului 2 din capitolul precedent. Gestiunea automată a memoriei Variabilele cu gestiune automată corespund variabilelor alocate în stivele de execuţie. Declararea se face prin: auto tip identificator . în lipsa acestuia. Cuvântul cheie “auto” nu este obligatoriu. Să mai notăm că limbajul C standard (ISO) permite iniţializarea tablourilor şi structurilor în cazul gestiunii automate a memoriei. Valoarea iniţială a unei variabile registru este.el este implicit */ /* x are o valoare nedefinita pe cand p este un **pointer care are ca valoare adresa lui x */ 8. Cuvântul cheie “register” permite programatorului să indice faptul că variabila ce se defineşte se memorează în unul din regiştrii calculatorului. auto float *p = &x. pointeri. cerinţa aceasta este satisfăcută în mod real doar dacă este posibilă. Durata de viaţă şi vizibilitatea unei variabile “auto” vor fi limitate la interiorul blocului în care este definită. în plus. Ea este efectivă doar pentru variabilele care pot fi codificate pe un cuvânt-maşină: caractere.4. implicit.2. valoarea iniţială. dacă nu este dată explicit. începând cu locul de declarare. întregi. şi trebuie să fie totdeauna în interiorul unui bloc. Gestionarea “register” a memoriei register tip identificator .Gh. acesta ţine cont de ea atâta timp cât există regiştri disponibili în maşină.

Gh. GRIGORAŞ

Programare C

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.

8.5. Variabile globale
O variabilă globală este o variabilă statică care poate fi referenţiată în orice loc al programului. O astfel de variabilă se declară prin: tip identificator ;

în exteriorul oricărui bloc; prin urmare nu se poate defini o variabilă globală în corpul unei funcţii. Ea este definită începând cu declaraţia sa, până la sfârşitul fişierului sursă. Pentru a defini o variabilă globală în alt fişier, se foloseşte cuvântul cheie “extern”. Este uşor de înţeles că, utilizarea unei variabile globale vine în contradicţie cu principiile programării modulare. în acelaşi timp ele permit: evitarea transmiterii unor argumente la funcţii apelate, oferindu-le posibilitatea să acceadă la aceste argumente, declarate în alte fişiere, şi având în fişierul local specificarea extern. Atenţie însă că o variabilă globală poate fi modificată indirect pe perioada apelului la o funcţie.

8.6. Declararea variabilelor externe
O variabilă externă poate fi declarată în orice punct al programului prin: extern tip identificator ; Această declaraţie permite declararea locală a unei variabile definită în alt fişier(cu acelaşi nume). Această facilitate este legată de faptul că limbajul C permite compilarea şi legarea separată a diferitelor module de program: prin declararea unei variabile extern se comunică tuturor fişierelor variabilele globale necesare programului. Cuvântul rezervat extern indică compilatorului faptul că tipurile şi numele variabilelor care urmează au fost declarate în altă parte şi nu mai alocă din nou spaţiu de memorie pentru acestea. Exemple: Fişier 1 int x,y; char c; main(void){ ... } ...

Fişier 2 extern int x, y; extern char c; fun1(void){...x=...}; fun2(void){...y=...}; ... ..

Fişier 2 extern int x,y; extern char c; funct1(void) { x=..

} î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);

56

Gh. GRIGORAŞ

Programare C

în cazul funcţiilor important este să se descrie signatura sa, adică tipul rezultatului şi al parametrilor de apel.

8.7. Gestiunea dinamică a memoriei
Gestiunea dinamică a memoriei permite crearea de noi variabile pe parcursul execuţiei programului. Această gestiune se efectuează în memoria disponibilă care se numeşte “grămadă” (heap). Durata de viaţă a unei variabile alocată dinamic este explicită: -variabila este creată în urma execuţiei operaţiei de alocare a memoriei (funcţia “malloc” în C şi operatorul “new” în C++); -variabila dispare în urma execuţiei operaţiei de restituire a memoriei (funcţia “free” în C şi operatorul “delete” în C++). Să notăm că o variabilă alocată dinamic nu are un nume explicit. Ea este referenţiată folosind o variabilă pointer (vezi cap. 6) care conţine adresa sa. De aceea, vizibilitatea unei variabile alocată dinamic este legată de tipul de gestiune al pointerilor care referenţiază indirect acea variabilă. De exemplu: Persoana *ptr; ptr=malloc(sizeof(Persoana)); Avem aici de-a face cu o variabilă alocată dinamic care este referenţiată cu ajutorul pointerului ptr prin *ptr.

8.8. Exerciţii
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. 1. 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); 2. 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ă: 2.1. citirea unui şir de şiruri de caractere care se vor “împinge” în stivă citirea se va face până la întâlnirea sfârşitului de fişier sau până la umplerea stivei; 2.2. scoate şirurile din stivă şi le afişează

57

Gh. GRIGORAŞ

Programare C

Cap 9 CONVERSIA DE TIP
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.

9.1. Mecanisme de conversie
Se disting trei cazuri în care într-un program C se aplică un mecanism de conversie: 1. asignarea cu tipuri diferite ale variabilelor termeni ai asignării: float f; int i=10; f=i; 2. evaluarea unei expresii aritmetice sau logice în care componentele expresiei au tipuri diferite: float pi=3.14; int i=2; ...... if (pi>2) pi=pi/i; 3. utilizarea operatorilor de conversie: float f1=(float)14/5; /* f1=2.8 */ float f2=14/5; /* f2=2.0 */ apelul unei funcţii: 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.

9.1.1 Conversia la asignare
În conversia la asignare, când apar două tipuri diferite, lucrurile se petrec după cum urmează: 1. asignări în care intervin două tipuri întregi: (a) reprezentarea tipului “sursă” este mai mare ca dimensiune decât reprezentarea tipului “destinatar”: char=short/int/long short=int/long int=long În acest caz biţii de rang superior din valoarea din partea dreaptă a asignării sunt suprimaţi încât

58

În rezumat. 3. GRIGORAŞ Programare C există.1. bitul semn în schimb nu se mai “transferă”: unsigned long=int/short/char unsigned=short/char unsigned short=char 2. etc.2 Evaluarea expresiilor aritmetice Faza I: tipurile întregi mai mici decât int sunt convertite la int iar tipul float este convertit la double. asignarea unei valori reale în dublă precizie la o variabilă reală în simplă precizie: float=double În acest caz există o pierdere a preciziei reprezentării valorii reale. (b) reprezentarea tipului “destinatar” este mai mare ca dimensiune decât cea a tipului “sursă”: long=int/short/char int=short/char short=char În acest caz nu există pierdere de informaţie: biţii sursei sunt completaţi cu zero (păstrânduse eventualul bit semn). potenţial. o pierdere de informaţie. Poate apărea şi aici eroarea “Floating point exception” dacă partea exponent este prea mare pentru o variabilă reală în simplă precizie.Gh. lucrurile se petrec astfel: 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 + + c (char) int int + b) (float) double double Celălalt operand se converteşte la: double unsigned long long unsigned Rezultatul este: double unsigned long long unsigned Faza 1: Faza 2: Rezultatul: int (a (long) long double int double double double double double 59 . Dacă valoarea părţii întregi a sursei este prea mare pentru a fi reprezentată într-un întreg. Faza II:se alege tipul de evaluare: întreg. apare o eroare (Floating point exception). fără semn. asignarea unei valori reale la o valoare întreagă: char/short/int/long=float/double În acest caz sunt suprimate cifrele de la partea zecimală: este conversie prin trunchiere şi nu prin rotunjire. mecanismul este acelaşi. Dacă destinatarul este un tip fară semn. 9. real.

Aceştia pot fi utilizaţi: • pentru forţarea conversiilor în expresii. /* f=5. • pentru transmiterea valorilor cu tipurile cerute ca parametri în funcţii. Evaluarea expresiilor logice Rezultatul evaluării unei expresii logice va fi 0 (reprezentând “false”) sau 1 (reprezentând “true”). int *ptr. De aceea evaluarea unei expresii logice se face prin: evaluarea operanzilor: dacă valoarea aritmetică este 0 atunci aceasta se interpretează prin “false”.. Aceşti operatori de conversie permit programatorului să specifice o conversie care nu se face implicit de către compilator.Gh. • schimbarea tipului valorii returnate de o funcţie... b=4.5 Conversie de pointeri. Operanzii ce apar în această expresie rămân neschimbaţi.0. */ . Să considerăm de pildă următoarea secvenţă: char *alocare(int dim). valoarea asignată acesteia este 0 (1). float f. ptr = alocare(100). 9.3. r=sqrt(double(a)). /* f5. rezultatul expresiei este convertit la tipul indicat. Pointerul universal Conversia între pointeri la tipuri diferite nu este implicită. i se decrementează. etc. GRIGORAŞ Programare C 9.. b=(x>0) && (x<31). Compilatorul forţează conversia şi afişează un mesaj de avertisment (“warning”) întotdeauna când într-un program apare o astfel de conversie. asignarea valorilor 0-1: dacă evaluarea expresiilor este “false” (true)...-“. . f=a/b. i are valoarea 10 deci expresia capătă valoarea “true”.25.. } înainte de prima evaluare a expresiei “i. după care. /* b capata valoarea 0 */ i=10 while (i--) { . Exemple: x=50. a=(int)ceil(r).1. */ f=a/(float)b..1. Exemple: int a=21.1. în caz contrar prin “true”...25. */ f=(float)a/b. /* f=5. 60 . double r.4 Conversii explicite Conversiile explicite se exprimă prin: (tip) expresie sau tip ( expresie ) Atâta timp cât conversia este permisă. /*functia ceil returneaza o valoare de tip intreg*/ 9.

Pentru exemplul precedent se procedează astfel: void* alocare(int dim). Asignările pentru ptr1. o valoare de tip void* este implicit convertită la o valoare pointer de un tip oarecare. ptr=(int*) alocare(100).Gh. 61 . O a doua soluţie constă în a utiliza tipul pointer universal void*. Tipul pointer universal void* este folosit frecvent în funcţiile disponibile din bibliotecile limbajului C (Anexa) în scopul de a realiza conversii implicite între pointeri de tipuri diferite. Această incompatibilitate se poate evita în două moduri.. ptr2=alocare(20).. O primă soluţie constă în a utiliza o conversie explicită: .. GRIGORAŞ Programare C În cea de-a treia linie a secvenţei de mai sus apare o asignare între pointeri incompatibili. O variabilă pointer de tip void* poate primi ca valoare pointer la un tip oarecare. ptr2 sunt corecte: tabloul de pointeri alocare este de tip void* încât aceşti pointeri pot fi asignaţi la pointeri de orice tip. Reciproc. int* ptr1 double* ptr2 ptr1=alocare(10).

Gh.1. La apariţia acestui identificator în program. Programatorul asociază o secvenţă de instrucţiuni unui identificator.2 Macroinstrucţiuni 10. 10. • definirea macroinstrucţiunilor.2.1.3 Compilare condiţionată 10.1. GRIGORAŞ Programare C Cap 10 PREPROCESORUL 10. • compilări condiţionate conform unor parametri (valori) transmise în comanda de compilare. 62 . Precompilarea este o fază de prelucrare.2. preprocesorul substituie secvenţa corespunzătoare de instrucţiuni în locul acestui identificator. acestea sunt directive către preprocesor.1 Directive preprocesor Preprocesorul tratează doar liniile ce încep prin caracterul “#”.2 Includerea fişierelor 10. O directivă este de forma: # instrucţiune argumente Principalele instrucţiuni pentru preprocesor sunt: define if elif line ifndef else include error undef endif ifdif pragma De notat că un rând în fişierul sursă C nu poate conţine mai mult de o directivă.2 Macroinstrucţiuni tip funcţie În codul sursă al unui program C se pot include instrucţiuni destinate compilatorului. Rezultatul activităţii preprocesorului este un fişier sursă în care sunt tratate toate directivele de precompilare şi care este transmis compilatorului. acestea se numesc directive pentru preprocesor. independentă de compilarea propriu-zisă.1 Directive preprocesor 10.1 Macroinstrucţiuni predefinite 10.4 Directiva #error 10. • definirea unor constante simbolice. ea face parte integrantă din procesul de compilare: apelul compilatorului pentru un program execută mai întâi încărcarea preprocesorului. Preprocesorul realizează o fază de precompilare care are ca obiect: • includerea unor fişiere (specificate) în program.1.1 Constante simbolice. În acelaşi timp însă. Directiva #define 10.

Directiva #define O constantă simbolică asociază unui identificator un şir de caractere. o declaraţii de funcţii incluse într-o bibliotecă. GRIGORAŞ Programare C 10. 63 . cu excepţia cazului când în acesta apare o directivă #undefine pentru identificatorul corespunzător. prin şirul de caractere asociat. Aceste fişiere conţin: o constante simbolice şi macroinstrucţiuni standard..1.h’). Preprocesorul înlocuieşte fiecare apariţie a identificatorului.1 Constante simbolice. • a defini existenţa unei constante simbolice: # define ident • a suprima definiţia şi existenţa unei constante simbolice: # undef ident Vizibilitatea constantelor simbolice începe cu definirea lor (directiva #define) şi ţine până la sfârşitul fişierului. 10.. Constanta simbolică se defineşte cu directiva “define”: # define identificator şir_de_caractere Exemple: # define NULL 0L # define TRUE 1 # define FALSE 0 # define EOF (-1) Utilizarea constantelor simbolice are diverse scopuri.Gh.2 Includerea fişierelor Directiva #include cere compilatorului să ia în considerare şi un alt fişier sursă în afară de cel în care se află directiva. Acest mecanism permite: • a asigna unui identificator un şir de caractere. (double)n).h care conţine declararea acestor funcţii: # include <math.1. Exemplu Pentru a folosi funcţiile din biblioteca matematică se include fişierul math. • a insera fişiere “header” (care au extensia ‘. în textul sursă. o definiţii de tipuri. val=pow(M_PI.. Forma acestei directive este: # include <nume_fisier> /* se caută fişierul începand cu directoarele standard */ sau # include ″nume_fisier″ /* se caută fişierul în directorul curent sau cel indicat prin cale */ Includerea fişierelor poate fi utilizată pentru: • a insera fişiere sursă din biblioteci C care pot conţine module de aplicaţii sau definiţii de funcţii.h> .

10. Aceste directive sunt: # # # # # # if expr_constanta else endif elif expr_constanta ifdef identificator ifndef identificator Directiva “if“ prin “expr_constantă” care urmează după ea permite includerea porţiunii de cod între “if” şi “endif” în procesul de compilare. este asemănătoare instrucţiunii if-else din limbajul C. Are forma: # error mesaj La întâlnirea directivei este întreruptă compilarea şi se afişează “mesaj”-ul ce este scris în linia directivei.h se află definiţia constantei M_PI şi a funcţiei pow( ). • optimizarea unor coduri utilizând avantajele specifice fiecărei maşini. # elif expresie1 cod1. # elif expresie2 cod2. # elif expresien codn.1. 64 . 10. doar dacă expresia este evaluată la “true” (în caz contrar această porţiune de cod este ignorată de compilator). Directiva “else” oferă o alternativă în cazul în care “if” eşuează. GRIGORAŞ Programare C /* se calculeaza puterea a n-a a lui π */ în fişierul math. compilarea condiţionată este folosită pentru: • scrierea de programe portabile: constantele simbolice ce se testează pot reprezenta tipul şi caracteristicile maşinilor utilizate pentru o anume porţiune de cod.2 Macroinstrucţiuni Prin acest mecanism programatorul asociază o secvenţă de instrucţiuni unui identificator.Gh. ea poate determina un lanţ de if-else-if: # if expresie cod.. Ambele directive pot folosi o alternativă de forma #else dar nu şi #elif. • traseul de execuţie al programelor în faza de test..3 Compilare condiţionată Directivele de compilare condiţionată permit compilarea selectivă a unor porţiuni din programul sursă. 10..4 Directiva #error Directiva #error cere compilatorului să sisteze compilarea. Directiva “elif” are sensul de “else if”. # endif Directivele “ifdef” (if defined) şi “ifndef” (if not defined) sunt utilizate pentru compilarea condiţionată de definirea anterioară a lui “identificator” într-o directivă de tipul #define. . Se utilizează la depanarea programelor.1. În concluzie.

dacă dimensiunea macroinstrucţiunii tip funcţie este mare. va fi înlocuit cu “secvenţă_caractere” care poate însemna o secvenţă de instrucţiuni C (vezi şi 10. an sunt compuse din c=te două cifre ce reprezintă în ansamblu data conversiei fişierului sursă în cod obiect. datorită codului care apare de mai multe ori. Macroinstrucţiunea _STDC_ conţine constanta 1 scrisă în baza 10. _STDC_. GRIGORAŞ Programare C 10. zi. acesta devine noua valoare a lui _LINE_ în punctul în care se înt=lneşte directiva. 10. # line număr “nume_fişier”. Ora la care a fost executată această conversie este conţinută în _TIME_ sub forma şirului ora/minute/secunde.ABS(1)).1.2 Macroinstrucţiuni tip funcţie Aceste macroinstrucţiuni utilizator se definesc cu directiva #define: # define nume_macro secvenţă_caractere Aici. Conţinutul acestor doi identificatori poate fi modificat prin directiva #line: # line număr.h> # define ABS(a) (a)<0?-(a):(a) void main(void){ printf(″Valoarea absoluta a lui -1 si 1:%d%d″.1). acest supliment de viteză este “anihilat” de creşterea dimensiunii programului. pe de o parte. utilizat într-un text sursă. Acesta. 65 . implementarea nu este în variantă standard. Macroinstrucţiunea _DATE_ conţine un şir de forma “lună/zi/an” în care lună.1 Macroinstrucţiuni predefinite Limbajul standard ANSI C defineşte cinci nume de macroinstrucţiuni predefinite. Dacă _STDC_ nu-i definită sau conţine altceva dec=t 1.ABS(-1).2. Exemplu: # define <stdio. _FILE_. } Folosirea funcţiilor definite ca macroinstrucţiuni are. încorporate în compilator. 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. 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ă. Aceasta semnifică faptul că implementarea este conform standardului ANSI C. iar “nume_fişier” este un identificator valid de fişier care devine noua valoare a lui _FILE_. Pe de altă parte însă. _TIME_. Identificatorul _LINE_ conţine numărul liniei de cod la compilare iar _FILE_ este un şir care conţine numele fişierului sursă compilat. “nume_macro” poate fi un identificator ce reprezintă numele macroinstrucţiunii. # line “nume_fişier”. Acestea sunt: _LINE_. Aici “număr” este orice întreg pozitiv. _DATE_.2.Gh.

c care să citească niste numere reale şi.90.04.90.98 este sortată astfel: 45. De exemplu lista: 2. 23. 2. 9.crt. Scrieţi apoi şi testaţi varianta iterativă a funcţiei. Suma şi Media fiind suma (media) numerelor citite până în acel moment. b. k a unui număr n. Să se scrie un program numit sumedia. 4.04. 10. 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 folosească cele două funcţii pentru a verifica dacă al n-ulea termen din şirul lui Fibonacci este număr prim ( n = 1. } Scrieţi un program pentru testarea acestei funcţii precum şi pentru calculul celui mai mare divizor comun a n numere date. 45.01. determinarea valorilor maxim şi minim dintre cele n numere citite.bţ : Orice număr par n mai mare ca 2 este suma a două numere prime. r). 0. …. citirea unui intreg pozitiv n. 123. Să se scrie şi apoi să se testeze funcţii pentru adunarea a doi vectori şi pentru adunarea a două matrice.43. …. int compare(const void *. ….Gh. const void *)) pentru a sorta o listă de numere reale în ordinea crescătoare relativ la părţile fracţionare a acestor numere. cel mai mare divizor comun a două numere întregi pozitive p şi q: int cmmdc_r(int p. citirea a n numere reale. cu numele : int cmmdc_i (int p. recursiv. 3. if (( r = p % q ) == 0 ) return q. etc. 2. 6. Să se folosească aceste funcţii într-un program principal. Să se scrie funcţii recursive pentru calculul factorialului unui număr dat n. 23. literele mari . 12). a puterilor 1. else return cmmdc_r(q.01. De exemplu: 700 = 17 + 683 .c care să execute : a. int q) { int r. a sumei numerelor naturale m şi n. Folosind funcţia “este_prim( … )” de la exerciţiul precedent.98 1. 0. Descrieţi şi implementaţi trei algoritmi de sortare. pentru fiecare să scrie un rând într-un tabel ce are forma: Nr. Număr Min Max Suma Media … … …. 702 = 11 + 691. 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 un program în care să se folosească funcţia sistemului : void qsort(void *array. 66 .size_t el_size. int q) 8. Următoarea funcţie calculează. 7. 123. GRIGORAŞ Programare C Cap 11 EXERCIŢII Să se scrie un program numit maxmin.2 …. unde Min(Max) înseamnă cel mai mic(mare) dintre numerele citite până în acel moment. 704 = 3 + 701. Să se scrie o funcţie “ fibonacci ( int n) “ care să returneze al n-ulea termen al şirului lui Fibonacci. scrieţi un program ce să verifice conjectura lui Goldbach pentru toate numerele pare dintr-un interval dat şa.43. size_t n_els. cifrele şi celelalte caractere(la un loc) dintr-un text(fişier). Să se scrie un program care să numere literele mici. c. Să se testeze această funcţie pentru un şir anume(fişier). 5. 11.

A.. Sa se ordoneze crescator fiecare linie a matricii. Cluj-Napoca. 14. Ed.1992. Bucuresti. bababbcbbabab. Limbajul C standard. cerc.Gh. E. Liviu Negrescu Limbajele C si C++ pentru incepatori. bbcbb sunt palindroame pe când şirurile: aabbcbb. triunghi etc. etc). calculeaza data calendaristica care precede o anumita data. vol I. Scrieti un program care. Teora 1998. a unui triunghi. 2.Manual Complet.Kalisz. c este palindrom cu centrul ‘c’: şirurile: aabacabaa . 13. C. 3.V. • • calculeaza data calendaristica care urmeaza unei anumite date. Proiectati structuri de date pentru reprezentarea unui punct. pentru un triunghi dat. aca. b.Scrieti o procedura similara pentru cerc. Proiectati o structura de date pentru reprezentarea datei calendaristice si scrieti subprograme care: • verifica daca valoarea unei variabile din structura este o data valida.Pănsiu.Giumale. 1999.III. Scrieti proceduri de citire si scriere a unui punct. determină cel mai lung subsir comun si pozitiile la care acesta incepe in fiecare dintre cele doua siruri. aaaaaaaacaaaabb. Herbert Schildt C . dreptunghi. Să se scrie funcţiile care implementează o stivă în C(push. Editura Teora. 16. Scrieti o procedură care determină. numărul punctelor de coordonate întregi aflate în interiorul sau. pop. apoi sa se rearanjeze liniile astfel incat suma elementelor de pe diagonala sa fie minima. aaaa nu sunt palindroame. Cristea. acb. 15. având la intrare doua siruri de caractere. BIBLIOGRAFIE 1. Să se folosească acestea pentru a verifică dacă un şir de caractere format numai din literele a. 67 . Se da o matrice patratica. top. GRIGORAŞ Programare C 12. ccab. c.