111
Adrian DEACONU PROGRAMARE PROCEDURALĂ 2009 - 2010

Adrian Deaconu Programare Procedurala

Embed Size (px)

DESCRIPTION

Manual de Programare procedurala

Citation preview

  • Adrian DEACONU

    PROGRAMARE PROCEDURAL

    2009 - 2010

  • Cuvnt nainte Cartea de fa se dorete a fi, n principal, un ghid pentru studenii din domeniul

    Informatic, dar, evident, ea poate fi util tuturor celor care vor s nvee s programeze

    procedural n limbajul C.

    La fiecare capitol, sunt date exemple sugestive, care ilustreaz din punct de vedere

    practic elemente de noutate. Este bine ca aceste exemple s fie nelese i, acolo unde este

    nevoie, s fie scrise i rulate de ctre cititor. Programele din aceast carte nu conin erori,

    deoarece ele au fost nti testate i abia apoi introduse n lucrare.

    n general, tot ceea ce este prezentat n aceast carte (teorie i aplicatii) este recunoscut

    att de compilatorul C al firmei Borland, ct i de compilatorul Visual C al companiei

    Microsoft.

    Autorul.

  • 2

    Introducere Limbajul C a fost lansat n anul 1972 n laboratoarele Bell de ctre Dennis Ritchie pentru sistemul de operare Unix. n anul 1973 limbajul C a ajuns suficient de puternic nct mare parte din sistemul Unix a fost rescris n C.

    Limbajul C s-a extins rapid pe mai multe platforme i s-a bucurat nc de la nceput de un real succes datorit uurinei cu care se puteau scrie programe.

    La sfritul anilor 1980 a aprut limbajul C++ ca o extensie a limbajului C. C++ preia facilitile oferite de limbajul C i aduce elemente noi, dintre care cel mai important este noiunea de clas, cu ajutorul creia se poate scrie cod orientat pe obiecte n adevratul sens al cuvntului.

    n anul 1989 este finalizat standardul ANSI C. Standardul ANSI (American National Standards Institute) a fost adoptat n 1990 cu mici modificri i de ctre ISO (International Organization for Standardization).

    Exist multe compilatoare de C/C++. Dintre acestea, de departe cele mai cunoscute sunt compilatoarele medii de programare integrate produse de firmele Borland i Microsoft.

    Prima versiune a bine-cunoscutului mediu de programare Turbo C a fost lansat de ctre firma Borland n anul 1987. Standardul ANSI C / ISO C a constituit baza elaborrii de ctre firma Borland a diferitelor sale versiuni de medii de programare, dintre care ultima a fost Borland C++ 5.02. n prezent firma Borland dezvolt mediul de programare Borland C++ Builder i mediile Turbo C++ Professional i Explorer, dintre care ultimul este gratuit ncepnd cu anul 2006.

    n anul 1992 firma Microsoft a lansat prima versiune a mediului su de programare Visual C++, iar n anul 2002 a fost lansat prima versiune Visual C++ pentru platforma .NET.

    n aceast carte vor fi prezentate limbajele C i C++ dintr-o perspectiv att Borland, ct i Microsoft. Pe alocuri vor fi punctate micile diferene dintre cele dou compilatoare.

  • 3

    Obiectivele cursului Cursul intitulat Programare procedural are ca obiectiv principal familiarizarea studenilor cu modul de gndire orientat pe proceduri n general i cu programare din C n particular.

    Resurse Parcurgerea unitilor de nvare aferente ambelor module necesit instalarea unui mediu de programare C, este de preferat Visual C 2008.

    Structura cursului Cursul este alctuit dintr-un singur modul, care cuprinde patrusprezece uniti de nvare. La rndul ei, fiecare unitate de nvare cuprinde: obiective, aspecte teoretice privind tematica unitii de nvare respective, exemple, teste de autoevaluare precum i probleme propuse spre discuie i rezolvare. La sfritul unitilor de nvare sunt indicate teme de control. Rezolvarea acestor teme de control este obligatorie. Temele vor fi trimise de ctre studeni prin e -mail.

    Modulul 1. Programarea n C Cuprins Introducere ................................ ................................ ................................ ................................ ...

    Competene ................................ ................................ ................................ ................................ ..

    U1. Structura unui program C

    U2. Tipuri numerice de date

    U3. Funcii de scriere i citire n C

    U4. Instruciuni de decizie, instruciuni repetitive, tipul char

    U5. Pointeri, tablouri de elemente

    U6. Funcii n C

    U7. String-uri

    U8. Structuri i pointeri ctre structuri

    U9. Uniuni i seturi de constante

    U10. Fiiere n C

    U11. Fluxuri standard n C i variabile statice

    U12. Funcii cu list variabil de argumente

  • 4

    U13. Utilizarea modului text de afiare

    U14. Grafic n C

    Introducere n acest modul vom face o preentare a limbajului C. Temele atinse in acest modul: 1. Structura unui program C 1.1. Includeri

    1.2. Constante. Macrocomenzi

    1.3. Asocieri de nume unor tipuri de date

    1.4. Funcia main

    2. Tipuri numerice de date 2.1. Tipuri ntregi de date

    2.2. Operatorii din C pentru valori ntregi

    2.3. Tipuri reale de date

    2.4. Operatorii C pentru valori reale

    2.5. Ali operatori n C

    3. Funcii de scriere i citire n C 3.1. Funcia printf

    3.2. Funcia scanf

    4. Instruciuni de decizie 4.1. Instruciunea if

    4.2. Instruciunea switch

    5. Instruciuni repetitive 5.1. Instruciunea for

    5.2. Instruciunea while

    5.3. Instruciunea do while

    6. Tipul char

    7. Pointeri. Tablouri de elemente 7.1. Alocarea static a memoriei

    7.2. Alocarea dinamic a memoriei

    8. Funcii n C

    9. String-uri

    10. Structuri

    11. Pointeri ctre structuri. Liste nlnuite.

    12. Uniuni

  • 5

    13. Seturi de constante (tipul enum)

    14. Fiiere n C 14.1. Funcii de lucru cu fiiere

    15. Fluxuri standard n C

    16. Variabile statice

    17. Funcii cu list variabil de argumente

    18. Utilizarea modului text de afiare al adaptorului video

    19. Grafic n C 19.1. Iniializarea modului grafic

    19.2. Instruciuni pentru desenare de linii drepte

    19.3. Instruciuni pentru desenare de curbe eliptice

    19.4. Instruciuni pentru afiare de texte n modul grafic

    19.5. Instruciuni pentru desenare de figuri umplute

    19.6. Instruciuni pentru paleta de culori

    19.7. Pagini grafice

    19.8. Citire / scriere zon de memorie video

    19.9. Ferestre grafice

    De asemenea, la sfaritul modului sunt atasate dou anexe (utilizarea mouse-ului i, respectiv, urmrirea execuiei unei aplicaii pas cu pas) i o bibliografie. Aceste elemente ale limbajului C vor fi prezentate pe larg n cele ce urmeaz.

    Competene La sfritul acestui modul studenii vor: i vor crea un mod de gndire orientat pe proceduri; fi familiarizai cu noiunea de pointer; fi familiarizai cu alocarea dinamic a memoriei; fi familiarizai cu iruri NULL-terminate; fi familiarizai cu transmiterea parametrilor n funcii ; ti s lucreze cu fiiere; fi familiarizai cu limbajul C n general; stpni elemente avansate de programare.

    Unitatea de nvare M1.U1. Structura unui program C

    Cuprins Obiective ................................ ................................ ................................ ................................ ...........

  • 6

    Introducere ................................ ................................ ................................ ................................ .......

    U1.1. Includeri................................ ................................ ................................ ................................ ...

    U1.2. Constante. Macrocomenzi. ................................ ................................ ................................ ......

    U1.3. Asocieri de nume unor tipuri de date ................................ ................................ ......................

    U1.4. Funcia main ................................ ................................ ................................ ............................

    Obiectivele unitii de nvare Ne propunem s vedem cum se scrie un program n limbajul C, ce seciuni apar, unde i cum se redacteaz.

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere Un program C se salveaz de obicei cu extensia C i, implicit, compilarea se va face cu compilatorul C. Dac, ns, programul este salvat cu extensia CPP, atunci compilarea se va face folosind compilatorul C++. Un program C obinuit are urmtoarea structur: /* includeri definitii macrocomenzi definitii de tipuri de date declaratii de variabile globale definitii de constante globale definitii de functii sau/si descrierea unor functii */ void main() {

    /* corpul functiei principale */ }

    /* descriere functii care au definitia (antetul) deasupra functiei main */

    Ordinea de apariie a includerilor, a definiiilor de macrocomenzilor, a tipurilor de date, a variabilelor i a constantelor globale este opional. Mai mult, pot aprea mai multe grupuri de includeri, macrocomenzi, definiii de tipuri de date, variabile i constante. Astfel, de exemplu, putem avea declaraii de variabile, apoi includeri, apoi iar declaraii de variabile etc.

    De regul la nceputul unui program C se pun includerile, dar, dup cum am spus, nu este obligatoriu.

  • 7

    S facem observaia c ntre /* i */ n C se pun comentariile (aa cum se poate vedea n programul de mai sus). Cu alte cuvinte, tot ce apare ntre aceste semne nu este luat n considerare la compilare.

    Spre exemplificare prezentm un program simplu care afieaz un mesaj de salut: # include /* includerea fiierului antet stdio.h */

    void main() {

    printf("HELLO WORLD!"); /* functia printf are antetul in fisierul stdio.h */

    }

    Facem observaia c n C se face distincie ntre litere mari i mici, spre deosebire de limbajul Pascal, de exemplu, unde nu conteaz dac redactm codul cu litere mari sau cu litere mici. Astfel, n programul de mai sus, dac scriam funcia printf cu litere mari, ea nu era recunoscut la compilare i obineam un mesaj de eroare. U1.1. Includeri La nceputul unui program C sau C++ se includ de obicei fiiere antet. Un fiier antet se recunoate uor prin faptul c are de obicei extensia h. Se pot include i fiiere cu extensia C sau CPP, care conin cod C, respectiv cod C++.

    Fiierele antet conin n general numai definiiile unor funcii a cror implementare (descriere) se regsete separat n fiiere cu extensiile C, CPP, obj sau lib. Fiierele *.obj conin cod C sau C++ n forma compilat cu compilatorul Borland C/C++. Fiierele *.lib conin biblioteci de funcii C i C++. Odat cu mediul de programare C/C++ se instaleaz i o mulime de biblioteci de funcii mpreun cu fiiere antet ce conin definiiile acestor funcii.

    Cele mai des incluse fiiere antet din C sunt stdio.h i conio.h, care conin definiiile funciilor standard de intrare / ieire (citiri, scrieri), respectiv definiii de funcii consol I/O (de exemplu funcia getch(), care ateapt apsarea unui buton de la tastatur).

    Includerea unui fiier ncepe cu semnul # (diez), urmat de cuvntul include i de numele fiierului care este dat ntre semnele < (mai mic) i > (mai mare), sau ntre ghilimele. Numele fiierului inclus poate fi precedat de eventuala cale unde se gsete el pe disc.

    Dac fiierul ce se include nu este precedat de cale i numele su este dat ntre ghilimele, atunci el este cutat n calea curent sau n directorul cu fiierele antet ce se instaleaz odat cu mediul de programare.

    Dac fiierul inclus nu este precedat de cale i numele lui este dat ntre semnele < i >, atunci el este cutat numai n directorul cu fiierele antet ale mediului de programare C/C++.

    Pentru limbajul C o parte dintre funcii sunt incluse implicit (compilatorul C le cunoate fr a fi nevoie includerea vreunui fiier). Totui, n funcie de compilator, pot fi generate mesaje de atenionare (warning) dac nu facem includerile. n exemplul de mai sus dac se salveaz programul cu extensia C (i implicit se folosete compilatorul C), atunci includerea fiierului antet stdio.h nu este neaprat necesar. Dac salvm ns fiierul cu extensia CPP, atunci includerea lui stdio.h este obligatorie.

    Iat n final i cteva exemple de includeri: # include

    # include "conio.h" # include "c:\bc\test\test.cpp"

  • 8

    U1.2. Constante. Macrocomenzi. n C o constant obinuit se poate defini folosind cuvntul rezervat const astfel: const tip_date c=expresie_constanta; Dac lipsete tipul de date de la definirea unor constante, se consider implicit tipul int. Iat i cteva exemple: const int x=1+4;

    const a=1,b=2*2; /* tot constante intregi ! */ const double pi=3.14159

    Macrocomanda reprezint o generalizare a conceptului de constant, n sensul c putem defini expresii constante, care se nlocuiesc n codul executabil n momentul compilrii. Definirea unei macrocomenzi ncepe cu semnul # (diez) urmat de cuvntul define.

    Dm cteva exemple de macrocomenzi: # define N 100

    # define PI 3.14159 # define suma(x,y) x+y # define alipire(a,b) (a##b)

    Primele dou macrocomenzi definesc constante obinuite, N este o constant de tip ntreg, iar PI este una de tip real.

    Ultimele dou macrocomenzi de mai sus definesc cte o expresie constant. ntr-un program care cunoate a treia macrocomand, un apel de forma suma(1.7, a) se va nlocui la compilare cu 1.7+a. nlocuirea se face n fiecare loc n care este apelat macrocomanda. De aceea, macrocomanda poate fi considerat o generalizare a conceptului de constant, dei apelul ei seamn cu cel al unei funcii.

    Comportamentul macrocomenzilor poate conduce la erori de programare pentru cei care nu cunosc modul lor de funcionare. De exemplu, valoarea expresiei suma(1,2)*5 este 11 i nu 15, deoarece nlocuirea direct a apelului suma(1,2) la compilare cu 1+2 conduce la expresia 1+2*5, care are evident valoarea 11 i nu la expresia (1+2)*5 cu valoarea 15. n exemplul de mai sus la ultima macrocomand s-a folosit operatorul ## care alipete (concateneaz) doi tokeni. Astfel, un apel de forma alipire(x,yz) se nlocuiete cu xyz, care poate fi o variabil, numele unei funcii etc.

    Este important de observat c datorit comportamentului diferit de cel al funciilor, macrocomenzile sunt contraindicate pentru a fi folosite prea des ntr-un program, deoarece fiecare apel de macrocomand se nlocuiete n memorie cu corpul macrocomenzii, ceea ce conduce la mrirea codului executabil. n concluzie, cnd se lucreaz cu macrocomenzi codul C sau C++ se poate reduce ca lungime, dar codul n form compilat poate crete.

    Frumuseea macrocomenzilor const n faptul c nu lucreaz cu tipuri de date prestabilite. Astfel, n exemplul de mai sus macrocomanda suma va putea fi folosit pentru orice tip de date, atta timp ct ntre x si y se poate aplica operatorul +. Din cauz c la definire nu se specific tipul de date al parametrilor, macrocomenzile pot fi vzute ca un rudiment de programare generic oferit de limbajul C. n limbajul C++ pot fi scrise funcii i clase ablon pentru care unul sau mai multe tipuri de date sunt nespecificate, identificarea acestor tipuri fcndu-se n momentul compilrii

  • 9

    programului. abloanele reprezint un suport pentru programare generic n adevratul sens al cuvntului. Asupra abloanelor vom reveni. U1.3. Asocieri de nume unor tipuri de date n C putem asocia un nume unui tip de date cu ajutorul cuvntului rezervat typedef astfel: typedef tip_de_date nume_asociat;

    Dm n continuare dou exemple ilustrative:

    typedef int intreg; typedef struct nume_structura nume_structura;

    Tipului numeric int i se asociaz numele intreg. Dup aceast asignare, putem folosi cuvntul intreg, n loc de int. n al doilea exemplu tipului de date de tip structur struct nume_structura (care trebuie s existe anterior definit) i se asociaz denumirea nume_structura (fr struct). Acest lucru este des folosit n C pentru simplificarea scrierii tipului structur. Aceast asociere nu este necesar n C++, unde tipul struct nume_structura poate fi folosit i fr cuvntul struct, fr o definire prealabil cu typedef. Asocierile de nume unor tipuri de date se pot face att n exteriorul, ct i n interiorul funciilor. 1.4. Funcia main n orice program C/C++ trebuie s existe o unic funcie main (principal), din interiorul creia ncepe execuia aplicaiei. Pentru a nelege mai bine modul de definire al funciei main vom prezenta pe scurt cteva generaliti legate de modul de definire al unei funcii oarecare. n C i C++ nu exist proceduri, ci numai funcii. n definiia unei funcii tipul returnat se pune naintea numelui funciei. Dac n momentul definirii funciei se omite tipul returnat, nu este greit i se consider implicit tipul returnat ca fiind int. O funcie care are ca tip returnat void (vid) se apeleaz ca o procedur. Dac o funcie nu are parametri, dup nume se pun paranteze rotunde sau se pune cuvntul rezervat void ntre paranteze rotunde n momentul definirii ei. Funcia main poate avea ca tip returnat void sau int (care se pune naintea cuvntului main). Dac tipul returnat este int, atunci n interiorul funciei main pot aprea instruciuni de forma return val_int care au ca efect ntreruperea execuiei programului i returnarea ctre sistemul de operare a valorii ntregi val_int. n aceast situaie, ultima instruciune din funcia main este de obicei return 0, ceea ce nseamn c programul s-a terminat cu succes. Dac apare vreo eroare pe parcursul execuiei programului care nu poate fi tratat (memorie insuficient, imposibilitate de deschide a unui fiier etc.), programul se prsete n general cu o instruciune de forma return val_int, unde val_int este o valoare ntreag nenul. Astfel, semnalm sistemului de operare faptul c execuia programului s-a ncheiat cu insucces (cu eroare). Funcia main poate s nu aib nici un parametru, sau poate avea 2 parametri. Dac funcia main are doi parametri, atunci primul este de tip int i reine numrul de parametri n linie de comand cu care s-a executat programul, iar al doilea este un ir de string-uri, n care sunt

  • 10

    memorai parametrii de apel n linie de comand. n primul string (pe poziia 0 n vector) se reine numele executabilului (al aplicaiei), dup care urmeaz parametrii de apel n linie de comand. Presupunem c avem programul C test care are urmtoarea funcie main: void main(int narg,char argv *arg[]) { /* .... */ } Apelm programul test n linie de comand cu doi parametri: test param1 param2 n aceast situaie, n funcia main a programului test vom avea:

    narg va avea valoarea 3 arg[0] va fi test.exe arg[1] va fi param1 arg[2] va fi param2.

    Asupra modului de definire i descriere a funciilor o s revenim.

    Rezumat

    naintea funciei main pot aprea niciuna, una sau mai multe seciuni de tipul: - includeri. Pot fi incluse n general fiiere antet (cu extensia .h), dar pot aprea i fiiere cu

    extensia .c sau .cpp. Aceste fiiere conin n general antete (definiii) de funcii, definiii de tipuri de date i constante, macrocomenzi, dar pot aprea i implementri de funcii, variabile globale etc.

    - definiii de macrocomenzi. Macrocomanda reprezint o generalizare a conceptului de constant. O macrocomand este o expresie constant. Fiecare apel de macrocomand este nlocuit la compilare cu corpul macrocomenzii. O macrocomand se descrie dup #define.

    - definiii de tipuri de date. Unui tip nou de date definit de programator i se poate da un nume folosind declaraia care incepe cu cuvntul rezervat typedef.

    - declaraii de variabile. - definiii de constante globale. Constantele n C se definesc dup cuvntul rezervat const. O

    constant obinuit poate fi definit i ca o macrocomand. - definiii sau/i descrieri de funcii. Funciile programului C se declar deasupra funciei

    main i se implementeaz dup funcia main, sau se descriu n ntregime deasupra funciei main.

    Funcia main poate avea ca tip returnat tipul void sau tipul int. Funcia main poate s nu

    aib nici argumente sau poate avea dou argumente de apel, argumente care memoreaz parametrii de apel n linie de comand ai aplicaiei.

    Teme de control

  • 11

    1. Folosind operatorul ?: (vezi subcapitolul 2.5) scriei o macrocomand pentru maximul dintre

    dou numere. 2. Scriei o macromand pentru maximul dintre trei valori. 3. Folosind prima macrocomand scriei o macromand pentru maximul dintre patru valori. 4. Ce trebuie s conin parametrii narg i argv pentru o aplicaie care ar calcula media

    aritmetic n linie de comand a orictor valori reale ? Unitatea de nvare M1.U2. Tipuri numerice de da te

    Cuprins Obiective ................................ ................................ ................................ ................................ ...........

    Introducere ................................ ................................ ................................ ................................ ........

    U2.1. Cum se scrie un program n C ................................ ................................ ................................

    U2.2. Includeri ................................ ................................ ................................ ................................ ...

    U2.3. Constante. Macrocomenzi. ................................ ................................ ................................ ......

    U2.4. Asocieri de nume unor tipuri de date ................................ ................................ .......................

    U2.5. Funcia main ................................ ................................ ................................ ............................

    Obiectivele unitii de nvare Ne propunem s facem cunotiin cu tipurile numerice de date ale limbajului C, modul lor de reprezentare n memorie, domeniul de valori, operatorii specifici acestor tipuri de date i ali operatori pe care i ntlnim n C.

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere n C i C++ avem dou categorii de tipuri numerice de date: tipuri ntregi i reale. Cu cele dou clase de tipuri numerice se lucreaz diferit (la nivel de procesor). Reprezentarea informaiei n memorie este diferit, avem operatori diferii. U2.1. Tipuri ntregi de date n tabelul de mai jos sunt prezentate tipurile numerice ntregi cu semn (signed) i fr semn (unsigned) din C. Facem observaia c numrul de octei pe care se reprezint n memorie

  • 12

    valorile ntregi i implicit domeniile de valori din tabelul urmtor sunt cele pentru Windows, mai exact, cele din Visual C:

    Denumire tip Numr octei Domeniu de valori

    (signed) char 1 -128 la 128 unsigned char 1 0 la 255

    enum 2 -32.768 la 32.767 short (signed) (int) 2 0 la 65.535 short unsigned (int) 2 -32.768 la 32.767

    (signed) int 4 -2.147.483.648 la 2.147.483.647

    unsigned int 4 0 la 4.294.967.295 (signed) long 4 -2.147.483.648 la

    2.147.483.647 unsigned long 4 0 la 4.294.967.295

    S facem cteva observaii:

    i. n Visual C tipul enum coincide cu tipul short int, iar tipul int coincide cu long. ii. Tot ceea ce apare ntre paranteze rotunde n tabelul de mai sus este opional.

    iii. Tipurile numerice de date au o dubl utilizare n C i C++: pentru valori numerice (numere) i pentru valori booleene. Astfel, o valoare numeric nenul (ntreag sau real) corespunde n C/C++ valorii booleene de adevrat (true), iar o valoare numeric nul corespunde valorii booleene de fals (false)

    iv. Tipurile char i unsigned char n C i C++ au o tripl ntrebuinare: pentru valori ntregi pe un octet, pentru valori booleene i pentru caractere. Valoarea ntreag de tip char reprezint codul ASCII al unui caracter. Astfel, constanta de tip char a reprezint valoarea ntreag 97, care este de fapt codul ASCII al caracterului a. Cu alte cuvinte, n C a este acelai lucru cu 97 !

    v. n Visual C pentru caractere Unicode este definit tipul wchar_t pe doi octei. U2.2. Operatorii din C pentru valori ntregi Operatorul = (egal) este folosit pentru atribuirea valorii unei expresii ntregi unei variabile ntregi: i = expresie;

    Operatorul returneaz valoarea atribuit variabilei i, valoare rezultat n urma evalurii expresiei i conversiei la tipul variabilei i, n cazul nostru fiind vorba de tipul int. Astfel, de exemplu i = 1 + sqrt(2) este o expresie care are valoarea 2. Pentru tipurile ntregi de date sunt definii operatorii aritmetici binari: + (adunare), - (scdere), * (nmulire), / (ctul mpririi), % (restul mpririi), +=, -=, *=, /=, %=, unde expresia x += y este echivalent cu x = x+y. Operatori aritmetici unari definii pentru tipurile ntregi de date sunt: ++ i --. Ei realizeaz incrementarea, respectiv decrementarea unei variabile ntregi. Astfel, x++ este echivalent cu x = x+1, iar x-- cu x = x-1. Cei doi operatori unari se folosesc sub dou forme: x++, x--, adic forma postincrementare, respectiv postdecrementare, iar ++x, --x sunt sub forma preincrementare, respectiv predecrementare. S explicm acum care este diferena dintre cele dou forme de utilizare a operatorului de incrementare.

  • 13

    Presupunem c variabila y reine valoarea 1. Pentru o expresie de forma x = ++y, nti se incrementeaz y i apoi noua valoare a lui y se atribuie lui x i, n consecin, x devine 2. Dac operatorul ++ se folosete n forma postincrementat, adic x = y++, atunci lui x nti i se atribuie valoarea variabilei y i abia dup aceea se face incrementarea lui y. n aceast situaie variabila x va fi iniializat cu valoarea 1. Operatorul de decrementare -- funcioneaz n mod similar ca i ++, tot sub dou forme, predecrementare i postdecrementare. Operatorii relaionali sunt: < (mai mic), (mai mare), >= (mai mare sau egal), == (egalitate), != (diferit). Cu alte cuvinte, cu ajutorul acestor operatori, dou valori ntregi se pot compara. Operatorul ! (semnul exclamrii) aplicat unei valori numerice are efectul not, adic din punct de vedere boolean i schimb valoarea (din False n True, respectiv din True n False). Astfel, !x are valoare 0 dac i numai dac x este nenul. Operatori la nivel de bit aplicabili numai pe valori ntregi sunt: & (i pe bii), | (sau pe bii), ^ (sau exclusiv), >> (shift-are bii la dreapta), > 1 = (0,1,0,1,1,0,1,1), a > 1 =

    (1,1,0,1,1,0,1,1), deoarece cel mai semnificativ bit (cel mai stnga) reprezint semnul numrului ntreg i n consecin dup shift-are la dreapta rmne tot 1 (corespunztor semnului minus). S observm c shift-area cu n bii a unei valori ntregi este echivalent cu o mprire la 2n. n cazul exemplului nostru a >> 1 este echivalent cu a = a / 2.

    Pentru operatorii pe bii avem i variantele combinate cu atribuire: &=, |=, ^=, =, unde x &= y este echivalent cu x = x & y etc. U2.3. Tipuri reale de date n tabelul de mai jos sunt trecute tipurile numerice reale (n virgul mobil) din C (denumirea, numrul de octei pe care se reprezint n memorie i domeniul de valori n valoare absolut):

    Denumire tip Numr octei Domeniu de valori n valoare absolut float 6 3,410-38 la 3,41038

    double 8 1,710-308 la 1,710308 long double 10 3,410-4932 la 1,1104932

    Desigur, este important i precizia cu care lucreaz fiecare din tipurile numerice reale de mai sus (numrul maxim de cifre exacte). Precizia cea mai bun o are tipul long double i cu cea mai sczut precizie este tipul float. U2.4. Operatorii C pentru valori reale Pentru tipurile reale de date n C sunt definii operatorii:

  • 14

    - Operatorul de atribuire =. - Operatorii aritmetici binari: + (adunare), - (scdere), * (nmulire), / (mprire). - Operatorii aritmetici binari combinai cu atribuire +=, -=, *=, /=. - Operatorii relaionali, not i de incrementare, respectiv decrementare

    funcioneaz i pentru valori reale. Operatorii pe bii nu funcioneaz pentru valori reale! U2.5. Ali operatori n C Operatorii logici: && (i), || (sau), ! (not). Dm un exemplu de expresie logic: (a && !(b%2) && (a>b || b sunt pentru acces la membrul unei structuri, uniuni sau clase (al doilea pentru cazul n care se lucreaz cu pointeri).

    Operatorul sizeof d dimensiunea n octei pe care o ocup n memorie valoarea rezultat n urma evalurii unei expresii. De exemplu, expresia sizeof 1+2.5 are valoarea 6, deoarece rezultatul expresiei 1+2.5 este o valoare de tip float, care se reprezint n memorie pe 6 octei. n C exist i funcia sizeof(tip) care returneaz numrul de octei necesari pentru stocarea n memorie a unei valori de tipul tip. De exemplu, sizeof(short) returneaz valoarea 2. float x;

    char s[100]; printf("%d == %d\n",sizeof x,sizeof(float)); printf("%d != %d\n",sizeof s,sizeof(char*));

    n exemplul de mai sus sizeof x i sizeof(float) au aceeai valoare, adic 6, deoarece amndou reprezint numrul de octei pe care este reprezentat n memorie un numr real de tip float. n schimb, cu toate c char* este tipul variabilei s, sizeof s returneaz numrul de octei rezervai pentru irul s, adic 100, iar sizeof(char*) returneaz valoarea 4, adic numrul de octei pe care se reprezint n memorie o adres (un pointer) ctre tipul char.

    Operatorul # (diez) este folosit pentru directive preprocesor (a se vedea discuia referitoare la macrocomenzi).

    Operatorul ## realizeaz alipirea a doi tokeni (a fost menionat tot la discuia legat de macrocomenzi).

    Operatorul , (virgul) se aplic ntre dou expresii care se evalueaz dup regula: se evalueaz nti prima expresie i apoi a doua (cea din dreapta), rezultatul returnat de operator fiind valoarea ultimei expresii. De exemplu, expresia x = 2, x 4 are valoarea 2. De obicei, o expresie n care apare operatorul virgul se pune ntre paranteze pentru eliminarea eventualelor ambiguiti. Dac folosim de mai multe ori operatorul virgul: expr1, expr2, , exprn, atunci

  • 15

    execuia se face de la stnga spre dreapta. De exemplu, expresia x=1, x+4, x*2 are valoarea 2, iar expresia x=1, x+=4, x*2 are valoarea 10.

    Rezumat Tipurile ntregi de date ale limbajului C sunt: (signed) char, unsigned char, enum, short (signed) (int), short unsigned (int), (signed) int, unsigned int, (signed) long i unsigned long. De departe cel mai cunoscut i utilizat tip de date din C este int, care se reprezint pe doi sau patru octei, n funcie de compilator i de sistemul de operare. Operatorii aritmetici binari pentru valori ntregi sunt: + (adunare), - (scdere), * (nmulire), / (ctul mpririi) i % (restul mpririi). Tipurile ntregi de date pot fi folosite i pentru a reine caractere. Practic, valoarea numeric ntreag n C se identific cu caracterul avnd codul ASCII acea valoare numeric. Tipurile reale de date din C sunt: float, double i long double. Operatorii aritmetici binari pentru valori reale sunt: + (adunare), - (scdere), * (nmulire) i / (mprire). Tipurile numerice de date (ntregi i reale) n C sunt i tipuri booleane. Orice valoare numeric diferit de zero corespunde valorii booleane de adevrat, iar orice valoare numeric nul (zero) corespunde valorii booleane de fals.

    Teme 1. Folosind operatorul sizeof calculai media artmetic a numrului de octei pe care se

    reprezint cele nou tipuri ntregi de date. 2. Aceeai problem pentru cele trei tipuri reale de date. Unitatea de nvare M1.U3. Funcii de scriere i citire n C

    Cuprins Obiective ................................ ................................ ................................ ................................ ...........

    Introducere ................................ ................................ ................................ ................................ ........

    U3.1. Funcia printf ................................ ................................ ................................ ..........................

    U3.2. Funcia scanf ................................ ................................ ................................ ...........................

    Obiectivele unitii de nvare Ne propunem s vedem cum putem n C afia mesaje pe ecran i cum putem prelua date de la tastatur pentru variabile.

  • 16

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere Definiiile funciilor pentru scrierea i citirea datelor n limbajului C le gsim n fiierul antet stdio.h. U3.1. Funcia printf Funcia printf este folosit pentru afiarea formatat a unui mesaj pe ecranul monitorului. Ea are urmtoare structur: printf(constanta_sir_de_caractere, lista_expresii);

    Funcia printf afieaz pe ecranul monitorului constanta_sir_de_caractere n care toi descriptorii de format se n locuiesc cu valorile expresiilor, nlocuirea fcndu-se de la stnga spre dreapta.

    Dm un exemplu: # include

    void main() {

    int n=10; float x=5.71; printf("Un nr. intreg: %d si un nr. real: %f\n",n,x);

    }

    n C, n momentul declarrii variabilelor, ele pot fi i iniializate (aa cum am procedat n

    exemplul de mai sus pentru n i x). %d i %f sunt specificatori (descriptori) de format pentru tipurile de date int i respectiv float. Descriptorii specific faptul c n locul n care se afl n irul de caractere ce se afieaz pe ecran vor aprea valori de tipul indicat, valori ce vor fi luate din lista de expresii aflat dup irul de caractere. n exemplul nostru lista de expresii este alctuit din valorile n i x.

    nlocuirea descriptorilor n constanta de tip ir de caractere (primul parametru al funciei printf) se face de la stnga spre dreapta. n exemplul de mai sus %d va fi nlocuit cu valoarea reinut n variabila n, adic 10, iar %f se nlocuiete cu valoarea lui x, adic 5.71. Dac tipurile expresiilor ce se nlocuiesc nu corespund cu descriptorii de format, sau dac numrul descriptorilor nu este egal cu cel al expresiilor ce se nlocuiesc, nu se semnaleaz eroare, nici mcar atenionare la compilare, dar afiarea va fi eronat! Dm n continuare lista cu descriptorii de format sau descriptorii de tip, cum se mai numesc: %c caracter (char) %s ir de caractere ncheiat cu caracterul \0 (string)

  • 17

    %d ntreg cu semn (int) %u ntreg fr semn (unsigned int) %ld ntreg lung (pe 4 octei) cu semn (long) %lu ntreg lung (pe 4 octei) fr semn (unsigned long) %x ntreg n baza 16 fr semn (int) (cifrele n hexazecimal pentru 10, 11, 12, 13, 14, 15

    sunt litere mici, adic a, b, c, d, e i f) %X la fel ca %x, numai ca cifrele n hexazecimal pentru sunt litere mari %o ntreg n baza 8 fr semn (int) %f real pe 6 octei (float), notaie zecimal (fr exponent) %e real pe 6 octei (float), notaie exponenial, tiinific (litera e de la exponent este mic) %E la fel ca %e, numai c pentru litera de la exponent este mare %g real pe 6 octei (float), notaie zecimal sau exponenial, care este mai scurt, iar dac

    se afieaz exponenial, atunci litera de la exponent este e %G real pe 6 octei (float), notaie zecimal sau exponenial, care este mai scurt, iar dac

    se afieaz exponenial, atunci litera de la exponent este E %lf real pe 8 octei (double), notaie zecimal %le real pe 8 octei (double), notaie exponenial (litera exponent e este mic) %lE la fel ca la %le, numai litera exponent E este mare %lg real pe 8 octei (double), notaie zecimal sau exponenial, care e mai scurt, dac e

    cazul se folosete liter mic pentru exponent %lG real pe 8 octei (double), notaie zecimal sau exponenial, care e mai scurt, dac e

    cazul se folosete liter mare pentru exponent %Lf real pe 10 octei (long double), notaie zecimal %Le real pe 10 octei (long double), notaie exponenial (litera de la exponent este mic,

    adic e) %LE real pe 10 octei (long double), notaie exponenial (litera de la exponent este mare,

    adic E) %Lg real pe 10 octei (long double), notaie zecimal sau exponenial, care este mai scurt,

    liter mic pentru exponent %LG real pe 10 octei (long double), notaie zecimal sau exponenial, care este mai scurt,

    liter mare pentru exponent %p adresa n hexazecimal (pentru pointeri). La afiarea folosind funcia printf se pot face formatri suplimentare, pornind de la un descriptor elementar. Formatrile se refer la numrul de caractere pe care se face afiarea unei valori, tipul alinierii, caracterele ce se completeaz n locurile libere (spaii, zerouri) etc. Dm n acest sens cteva exemple: %50s realizeaz afiarea unui string pe 50 caractere cu aliniere la dreapta, iar %-50s face acelai lucru, dar cu aliniere la stnga. De obicei string-urile se aliniaz la stnga. %4d realizeaz afiarea unui numr ntreg pe 4 caractere cu aliniere la dreapta, iar %-4d face acelai lucru, dar cu liniere la stnga. De obice i valorile numerice se aliniaz la dreapta. %10.2f realizeaz afiarea unui numr real pe 10 caractere cu 2 cifre exacte dup virgul, cu aliniere la dreapta, iar %-10.2f face acelai lucru, dar cu aliniere la stnga. %010.2f realizeaz afiarea unui numr real pe 10 caractere cu 2 cifre exacte dup virgul, cu aliniere la dreapta, iar spaiile goale (din faa numrului) se completeaz cu zerouri. De exemplu, numrul 1.4 se va afia sub forma 0000001.40. Dac nu se pune 0 n faa lui 10.2f, atunci n loc de zerouri se completeaz implicit spaii. n programul de mai sus (afiarea unui ntreg i a unui numr real), dup afiarea pe ecran a mesajului, se sare la nceputul liniei urmtoare, deoarece irul s-a ncheiat cu \n. Combinaia de caractere \n este una dintre aa numitele secvene escape.

  • 18

    Lista secvenelor escape recunoscute de limbajul C este:

    \n salt la linie nou (nu neaprat la nceputul lui) \r salt la nceput de rnd \t deplasare la dreapta (tab) \b deplasare la stnga cu un caracter (backspace) \f trecere pe linia urmtoare (formfeed) \ apostrof \ ghilimele \\ backslash \xcc afiarea unui caracter avnd codul ASCII n hexazecimal dat de cele dou cifre de

    dup \x (fiecare c reprezint o cifr n baza 16). De exemplu, \x4A este caracterul cu codul ASCII 4A(16) = 74(10), adic litera J. Este bine de reinut faptul c ntr-un ir constant de caractere, semnul \ (backslash) trebuie dublat. Asta se ntmpl mai ales cnd este vorba de ci de fiiere. De asemenea, semnele (ghilimele) i (apostrof) trebuiesc precedate de semnul \ (backslash).

    n C pentru a trimite un mesaj n fluxul standard de erori stderror folosim funcia perror, n loc de printf. Mesajul de eroare ajunge tot pe ecranul monitorului, dar pe alt cale. Datorit faptului c funcia perror nu suport formatare aa cum face printf, n exemplele pe care o s le dm n aceast carte vom folosi funcia printf pentru afiarea mesajelor de eroare. U3.2. Funcia scanf Funcia scanf este folosit pentru preluarea formatat de la tastatur a valorilor pentru variabile. Funcia are urmtoarea structur: scanf(const_sir_caractere, lista_adrese_var_citite); constanta_sir_caractere este un string care conine numai descriptorii de tip ai variabilelor ce se citesc. Descriptorii sunt cei elementari pe care i-am prezentat mai sus (vezi funcia printf), adic: %c, %s, %d, %u, %ld, %lu, %x, %X, %o, %f, %e, %E, %g, %G, %lf, %le, %lE, %lg, %lg, %Lf, %Le, %LE, %Lg, %LG, %p. n exemplul urmtor apar cteva citiri de la tastatur: # include

    void main() {

    int n; float f; char s[10]; /* sir de caractere */ scanf("%d%f%s",&n,&f,s); printf("Am citit: %d, %f,%s",n,f,s);

    }

    Facem observaia c prin &var se nelege adresa la care se afl reinut n memorie variabila var. n C valorile se returneaz prin adres prin intermediul parametrilor unei funcii.

    Variabila s este deja un pointer ctre zona de memorie n care se memoreaz un ir de caractere, aadar nu este necesar s punem semnul & (i) n faa lui s la citire. Dac se pune

  • 19

    totui semnul & n faa variabilei s la citire, nu se semnaleaz eroare nici mcar atenionare, pleonasmele n C n general sunt ignorate de compilator. Vom reveni cu mai multe explicaii cnd vom vorbi despre pointeri i tablouri (iruri).

    Pentru funciile printf i scanf exist i variantele pentru scriere, respectiv citire n/dintr-un string sau fiier text.

    Numele funciilor pentru string-uri ncep cu litera s (sprintf, respectiv sscanf) i funcioneaz absolut la fel ca cele obinuite numai c destinaia scrierii, respectiv sursa citirii este un string. n consecin mai apare un parametru suplimentar (variabila ir de caractere) n faa celor pe care i au funciile printf i scanf.

    Numele funciilor de scriere i citire pentru fiiere text ncep cu litera f (fprintf, respectiv fscanf). Aceste funcii au un parametru suplimentar (variabila pointer ctre tipul FILE).

    Asupra acestor funcii o s revenim atunci cnd o s discutm despre string-uri, respectiv fiiere. n Borland C pentru DOS exist variantele cprintf, respectiv cscanf ale funciilor printf, respectiv scanf pentru scriere i citire formatat ntr-o fereastr text (creat cu funcia window). Facem observaia c schimbarea culorii textului i a fundalului pe care se afieaz un text are efect n cazul utilizrii funciei cprintf, dar nu are efect atunci cnd se folosete funcia printf. De asemenea, pentru salt la nceputul unei linii trebuie s punem pe lng \n i secvena escape \r, ca s se revin la nceputul rndului, ceea ce nu era necesar n cazul funciei printf, secvena escape \n fiind suficient.

    Trebuie precizat faptul c funcia printf nu ine cont de marginile ferestrei text definite cu window. Ca parametri, funcia window primete cele 4 coordonate ecran text ale colurilor stnga-sus i respectiv dreapta-jos ale ferestrei.

    Rezumat Afiarea formatat a mesajelor pe ecran se face cu ajutorul funciei printf, iar preluarea formatat a datelor de la tastatur se face folosind funcia scanf. Pentru a utiliza aceste funcii trebuie s includem fiierul antet stdio.h.

    Teme 1. Afiai pe ecranul monitorului cele dou tabele cu tipurile numerice de date de la capitolul

    anterior. 2. De la tastatur citii datele unor persoane (nume i prenume, vrst i salariu). Afiai tabelar

    aceste date pe ecran dup modelul urmtor: --------------------------------------------------------------- |Nr. | NUMELE SI PRENUMELE |Varsta|Salariu| |crt.| | | | |----+-----------------------------------------+------+-------| | 1|Ionescu_Monica | 19| 520.17| | 2|Aionesei_Adrian_Ionel | 23| 884.25| | 3|Popescu_Gigel | 19| 443.10| | 4|Popescu_Maria | 28|1155.00| |----------------------------------------------+------+-------| | Medie | 22.25| 750.63|

  • 20

    --------------------------------------------------------------- Unitatea de nvare M1.U4. Instruciuni de decizie, instruciuni

    repetitive, tipul char

    Cuprins Obiective ................................ ................................ ................................ ................................ ..........

    Introducere ................................ ................................ ................................ ................................ .......

    U4.1. Instruciunea if ................................ ................................ ................................ ........................

    U4.2. Instruciunea switch ................................ ................................ ................................ ................

    U4.3. Instruciunea for ................................ ................................ ................................ .....................

    U4.4. Instruciunea while ................................ ................................ ................................ .................

    U4.5. Instruciunea do while ................................ ................................ ................................ .......

    U4.6. Tipul char ................................ ................................ ................................ ...............................

    Obiectivele unitii de nvare Ne propunem s vedem cum se redactez o instruciunile de decizie i cele repetitive n C. De asemenea, ne propunem s vedem ce funcii avem n C pentru prelucrarea caracterelor.

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere n C exist dou instruciuni de decizie: if else i instruciunea de decizie multipl (cu oricte ramuri) switch case. De asemenea, exist trei instruciuni repetitive: for, while si dowhile. U4.1. Instruciunea if n C o instruciune de tipul dac atunci altfel are urmtoarea structur: if (conditie) {

    /* grupul de instructiuni 1 */

  • 21

    } else {

    /* grupul de instructiuni 2 */ } Semnificaia instruciunii este: dac este adevrat condiia, atunci se execut grupul de instruciuni 1, altfel se execut grupul de instruciuni 2. Ramura else poate lipsi.

    Un grup de instruciuni n C se delimiteaz cu ajutorul acoladelor {}. Dac un grup este format dintr-o singur instruciune, atunci acoladele pot lipsi. Dm un exemplu ilustrativ pentru instruciunea if : float x=2.2, y=4;

    int a=25, b=85; if (x>y) max=x; else max=y; if (a) {

    printf("Restul impartirii este: %d\n",b%a); printf("Catul impartirii este: %d",b/a);

    } else perror("Impartire prin 0!");

    U4.2. Instruciunea switch switch este o instruciune decizional multipl (cu mai multe ramuri). Structura ei este urmtoarea: switch (expresie_intreaga) {

    case val1: /* grupul de instructiuni 1 */ break;

    case val2: /* grupul de instructiuni 2 */ break;

    /* .... */ case valn:

    /* grupul de instructiuni n */ break;

    default: /* grupul de instructiuni n+1 */

    } Semnificaia instruciunii este: Se evalueaz expresia expresie_intreaga care trebuie sa aib o valoare ntreag. Avem urmtoarele situaii:

    Dac valoarea expresiei este egal cu val1, atunci se execut grupul de instruciuni 1. Dac valoarea expresiei este egal cu val2, atunci se execut grupul de instruciuni 2.

    . Dac valoarea expresiei este egal cu valn, atunci se execut grupul de instruciuni n.

  • 22

    Dac valoarea obinut n urma evalurii expresiei nu este egal cu nici una dintre valorile val1, , valn, atunci se execut grupul de instruciuni n+1.

    S facem cteva observaii:

    1) La instruciunea switch grupurile de instruciuni de pe ramuri nu trebuiesc

    delimitate cu acolade. Nu este greit ns dac le delimitm totui cu acolade. 2) Dup fiecare grup de instruciuni punem n general instruciunea break. n lipsa

    instruciunii break se execut i instruciunile de pe ramurile de mai jos pn la sfritul instruciunii switch (inclusiv cele de pe ramura default) sau pn se ntlnete primul break. Instruciunea break ntrerupe execuia instruciunii switch i a celor repetitive (for, while i do ... while).

    3) Ramura default poate lipsi. 4) Dac exist ramura default, nu este obligatoriu s fie ultima. 5) Valorile val1, val2, , valn trebuie s fie constante ntregi i distincte dou cte

    dou.

    Spre exemplificare considerm o secven de program pentru calcularea ntr-un punct a valorii unei funcii f cu trei ramuri:

    21,1

    1,20,1

    )(,:xixdacx

    xdacxdac

    xfRZf .

    int x;

    printf("Dati un numar ntreg: "); scanf("%d",&n); printf("Valoarea functiei este f(%d)=",x); switch (x) {

    case 0: printf("-1"); break;

    case 1: printf("-2"); break;

    default: printf("%d",x+1); break;

    }

    Rezumat n C avem dou instruciuni de decizie: instruciunea if, care are una sau dou ramuri (ramura else poate lipsi) i instruciunea switch (cu oricte ramuri). Expresia dup care se face selecia ramurii case trebuie s aib o valoare ntreag. De asemenea, de reinut este i faptul c n principiu, fiecare ramur a instruciunii switch se termin cu un apel break.

  • 23

    U4.3. Instruciunea for n C, instruciunea repetitiv for este destul complex. Ea are urmtoarea structur: for (expresie_1; expresie_2; expresie_3) {

    /* grup de instructiuni */ } Semnificaia instruciunii este:

    1) Se evalueaz expresie 1. Aceast expresie conine n general iniializri de variabile. 2) Ct timp valoarea expresiei 2 este adevrat (nenul), se execut grupul de

    instruciuni. Aceast expresie reprezint condiia de oprire a ciclrii. 3) Dup fiecare iteraie se evalueaz expresie 3. Aceast expresie conine n general

    actualizri ale unor variabile (incrementri, decrementri etc.).

    Prezentm cteva caracteristici ale instruciunii for:

    1) Oricare dintre cele 3 expresii poate lipsi. Lipsa expresiei 2 este echivalent cu valoarea 1 (de adevr). Prsirea ciclului for n aceast situaie se poate face n general cu break.

    2) n prima expresie pot aprea mai multe iniializri, care se dau cu ajutorul operatorului virgul (vezi subcapitolul dedicat operatorilor). De asemenea, ultima expresie poate conine mai multe actualizri desprite prin virgul.

    Ca aplicaie pentru instruciunea for, calculm suma primelor n numere ntregi pozitive

    (valoarea lui n este citit de la tastatur) i cel mai mare divizor comun pentru dou numere ntregi (preluate tot de la tastatur) folosind algoritmul lui Euclid: long s,i,n,a,b,x,y;

    printf("n="); scanf("%ld",&n); for (s=0,i=0; i

  • 24

    Semnificaia instruciunii este: ct timp condiia este adevrat, se execut grupul de instruciuni. Prsirea ciclrii se poate face forat cu break. Calcularea celui mai mare divizor comun a dou numere ntregi folosind instruciunea while poate fi scris astfel: long a,b,x,y,r;

    printf("Dati doua numere ntregi: "); scanf("%ld%ld",&a,&b); x=a; y=b; while (y) { r=x%y; x=y; y=r; } printf("Cmmdc(%ld,%ld)=%ld\n"),a,b,x); printf("Cmmmc(%ld,%ld)=%ld\n"),a,b,a/x*b);

    U4.5. Instruciunea do while Are urmtoarea structur: do {

    /* grup de instructiuni */ } while (conditie); Semnificaia instruciunii este: se execut grupul de instruciuni ct timp condiia este adevrat. Prsirea ciclrii se poate face forat de asemenea cu break. Instruciunea dowhile din C este echivalent instruciunii repetitive repetpn cnd din pseudo-cod, deosebirea esenial const n faptul c la instruciunea din C ciclarea se oprete cnd condiia devine fals, pe cnd la instruciunea din pseudo-cod ciclarea se ncheie cnd condiia devine adevrat. Cu alte cuvinte condiia din instruciunea dowhile este negaia condiiei de la instruciunea repetpn cnd. Lum ca exemplu sortarea cresctoare unui ir prin metoda bulelor (BubbleSort). Despre tablouri de elemente (vectori, matrici) o s vorbim mai trziu. Ceea ce ne intereseaz acum, pentru a nelege exemplul de mai jos, este faptul c indicii vectorilor se dau ntre paranteze ptrate [], iar n momentul declarrii unui vector putem enumera elementele sale ntre acolade. int ok, n=5, a[5]={4, 2, 5, 7, 0};

    do {

    ok=1; for (i=0;ia[i+1]) {

    aux=a[i]; a[i]=a[i+1]; a[i+1]=aux; ok=0;

    } } while (!ok);

  • 25

    Rezumat n C exist trei intruciuni repetitive: for, while i do.while. Implementarea instruciunii for este destul de complex, n sensul c putem face mai multe iniializri, putem avea o condiie complex de terminare a ciclrii i putem avea mai multe actualizri. Aici se utilizeaz practic operatorul virgul.

    Instruciunea do.while este cu test final. Cu ajutorul ei putem implementa o instruciune pseudocod repeat.until. Teme 1. Se citete de la tastatur un numr ntreg. S se verifice dac este prim. 2. Afiai primele n numere naturale prime, unde n este citit de la tastatur. 3. Se citete de la tastatur un numr natural. S se verifice dac este palindrom. Un numr

    natural este palindrom dac cifrele lui citite de la stnga spre dreapta sunt aceleai cu situaia n care le citim de la dreapta spre stnga.

    4. De la tastatur se citesc dou numere naturale n i k. S se calculeze i s se afieze pe ecran valoarea expresiei:

    U4.6. Tipul char Tipul char a fost prezentat la capitolul dedicat tipurilor ntregi de date. Informaia reinut ntr-o valoare de tip char poate fi interpretat n C ca un caracter al tabelei ASCII. De acest lucru o s ne ocupm n acest capitol. Definiiile principalelor funcii de lucru cu caractere n C le gsim n fiierul antet ctype.h. Dintre aceste funcii o s le prezentm numai pe cele mai importante i mai utile. Facem observaia c toate funciile de mai jos returneaz o valoare de tip int i primesc ca argument un caracter:

    1) isalnum(c) returneaz valoare de adevrat (nenul) dac c este un caracter alfa-numeric (litera mic, liter mare sau cifr), altfel se returneaz 0.

    2) isalpha(c) returneaz valoare de adevrat (nenul) dac c este o liter, altfel se returneaz 0.

    3) isdigit(c) returneaz valoare de adevrat (nenul) dac c este o cifr, altfel returneaz 0.

    4) isxdigit(c) returneaz valoare de adevrat (nenul) dac c este cifr hexazecimal (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, a, b, c, d, e, f), altfel se returneaz 0.

    5) islower(c) returneaz valoare de adevrat (nenul) dac c este liter mic, altfel returneaz 0.

    6) isupper(c) returneaz valoare de adevrat (nenul) dac c este liter mare, altfel returneaz 0.

    .),(1

    n

    i

    kiknS

  • 26

    7) tolower(c) returneaz transformarea caracterului c n litera mic dac este liter mare, altfel se returneaz valoarea lui c nemodificat.

    8) toupper(c) returneaz transformarea caracterului c n litera mare dac este liter mic, altfel se returneaz valoarea lui c nemodificat.

    i n fiierul antet conio.h ntlnim cteva funcii legate de tipul char:

    1) getch() citete un caracter de la tastatur, fr ecou pe ecranul monitorului. Funcia returneaz caracterul citit fr a-l afia pe ecran.

    2) getche() citete un caracter de la tastatur, se returneaz caracterul citit dup ce este afiat pe ecran (cu ecou).

    3) putch(c) afieaz pe ecran caracterul c primit ca argument de funcie. Se ine cont de eventuala fereastr text definit cu funcia window.

    4) kbhit() verific dac n buffer-ul de intrare de la tastatur exist caractere. Cu alte cuvinte se verific dac s-a apsat un buton de la tastatur. Dac buffer-ul este nevid, atunci se returneaz o valoare nenul.

    Programul urmtor afieaz codul ASCII pentru o liter mic preluat de la tastatur: char c;

    c=getch(); if (97

  • 27

    Introducere ................................ ................................ ................................ ................................ ........

    U1.1. Alocarea static a memoriei ................................ ................................ ................................ ...

    U1.2. Alocarea dinamic a memoriei ................................ ................................ ...............................

    Obiectivele unitii de nvare Ne propunem s nelegem n acest capitol cea mai important noiune a limbajului C. Este vorba de pointer.

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere Pentru a stpni modul de funcionare al limbajului C, trebuie neleas foarte bine noiunea de pointer. O variabil de tip pointer reine o adres de memorie la care se afl o informaie (un caracter, un ntreg, un ir etc.). O variabil de tip pointer se declar sub forma: tip *numepointer. De exemplu, o variabil de tip pointer ctre tipul int se declar astfel: int *pint. Variabila pint va reine adresa de memorie la care se afl stocat o dat de tip int. Revenind la declaraia general tip *numepointer, numepointer este o variabil de tipul tip* i memoreaz o adres de memorie, la care este reinut o informaie de tipul tip. O adres se memoreaz pe 4 octei i n consecin sizeof(tip*) este 4. Putem aplica operatorul * unei valori de tip pointer pentru a afla valoarea care se afl la adresa reinut de pointer. Astfel, *numepointer reprezint valoarea de tipul tip aflat n memorie la adresa numepointer.

    Adresa la care se afl zona de memorie rezervat unei variabile se poate afla folosind operatorul &. Cu alte cuvinte, operatorul & este inversul operatorului *. n consecin, pentru o variabil var de tipul tip are sens o atribuire de forma numepointer=&var. De asemenea, se poate face atribuirea var=*numepointer. Operatorul * anuleaz pe & i & anuleaz pe *, adic *&var este totuna cu var, iar &*numepointer este acelai lucru cu numepointer. Pentru valori de tip pointer funcioneaz operatorii: =, +, -, +=, -=, ++, -- i !. De exemplu putem scrie numepointer+=10, sau numepointer=&var-1. Dup aplicarea operatorului += pointerului numepointer sub forma numepointer+=10, pointerul va reine adresa aflat cu 10 csue mai la dreapta n memorie fa de adresa iniial. O csu de memorie are sizeof(tip) octei, unde tip este tipul de dat ctre care pointeaz numepointer. Cu alte cuvinte, n urma atribuirii numepointer+=10, variabila numepointer va reine adresa de memorie aflat cu 10*sizeof(tip) octei la dreapta adresei de memorie iniiale din numepointer.

    De reinut este i faptul c pentru valori de tip pointer nu funcioneaz operatorii: *, /, %, adic pointerii nu se pot nmuli, mpri cu alte valori.

  • 28

    U5.1. Alocarea static a memoriei n C exist posibilitatea alocrii memoriei att mod static, ct i dinamic. Prin alocare static nelegem faptul c memoria este alocat automat nc de la pornirea execuiei instruciunilor unei funcii, iar eliberarea memoriei se face tot automat la prsirea funciei. Cu alte cuvinte, cnd se intr ntr-o funcie (inclusiv funcia main) se face alocarea static a memoriei, iar eliberarea se face la prsirea acelei funcii. Lungimea zonei de memorie alocate static este constant, adic de fiecare dat cnd se intr cu execuia ntr-o funcie, se aloc automat acelai numr de octei. Spre deosebire de alocarea static, n cazul alocrii dinamice stabilim noi momentul alocrii memoriei i lungimea zonei de memorie alocate. De asemenea, putem decide momentul n care s se fac eliberarea memoriei. Pentru un vector putem aloca memorie n mod static sub forma tip numevector[lungimev], unde lungimev este o constant numeric care reprezint numrul de elemente (csue de memorie) al vectorului.

    Dm cteva exemple de alocri statice de memorie pentru vectori: # define N 100

    .... float a[10],b[N]; int i,n,vect[N+1];

    Elementele unui vector n C au indici ntre 0 i lungimev-1. De exemplu, vectorul a definit mai sus are elementele: a[0], a[1], , a[9].

    Dac se trece de capetele 0 i lungimev-1 ale vectorului a, n C nu se semnaleaz n general eroare. Astfel se poate accesa elementul a[-1] (zona de memorie aflat cu o csu n stnga lui a[0]) sau se poate accesa a[lungime]. Acest lucru poate conduce la blocarea programului sau la alterarea altor date reinute n memorie la adresele respective, pe care le-am accesat greit! n declaraia numevector[lungimev], numevector este un pointer care reine adresa de nceput a zonei de memorie alocate static pentru vector (elementele vectorului se rein n csue succesive de memorie). Cu alte cuvinte, pointerul a reine adresa la care se afl memorat primul element a[0] al vectorului a, iar a+1 este adresa la care se afl elementul a[1], a+2 este adresa lui a[2] etc. De asemenea, *a este tot una cu a[0], *(a+1) este a[1] etc. n general, a[k] reprezint o scriere simplificat pentru *(a+k). U5.2. Alocarea dinamic a memoriei Alocarea dinamic a memoriei n C se poate face cu ajutorul funciilor malloc, calloc i realloc, ale cror definiii le gsim n fiierul antet malloc.h. Cele 3 funcii pentru alocare dinamic returneaz toate tipul pointer ctre void, adic void*. De aceea, n momentul folosirii acestor funcii trebuie fcut o conversie de tip. Oricare dintre cele trei funcii de alocare returneaz valoarea NULL (constanta NULL are valoarea 0) n cazul n care alocarea de memorie nu s-a putut face (nu este suficient memorie liber sau lungimea zonei de memoriei ce se dorete a fi alocat nu este valid, adic este negativ). Funcia malloc primete un parametru de tip ntreg, care reprezint numrul de octei de memorie ce se dorete a fi alocai. De exemplu, pentru un vector de numere ntregi scurte trebuie s definim un pointer ctre tipul int, pointer care va reine adresa ctre zona de memorie alocat:

  • 29

    int i,*a,n,m; printf("Dati lungimea vectorului: "); scanf("%d",&n); a=(int*)malloc(n*sizeof(int)); /* alocare memorie pentru n elemente de tip int */ if (a==NULL) {

    perror("Memorie insuficienta!"); /* mesaj eroare */ exit(1); /* parasire program cu cod de eroare */

    } for (i=0;i

  • 30

    Alocarea memoriei pentru o matrice se poate face, de asemenea, att static ct i dinamic. Varianta static este: tip a[nl][nc];, unde nl reprezint numrul de linii, iar nc este numrul de coloane al matricii. n continuare prezentm dou moduri n care se poate face alocare dinamic de memorie pentru o matrice a de valori reale (float) de dimensiuni m i n. n C, o matrice alocat dinamic este un vector (de lungime m) de vectori (fiecare de lungime n). Pentru aceasta trebuie s definim variabila a care va fi de tipul float**, adic pointer ctre pointer ctre tipul float. Pointerul a va fi adresa ctre zona de memorie n care se reine vectorul cu adresele de nceput ale fiecrei linii ale matricii. nti va trebui s alocm memorie pentru vectorul de adrese de lungime m. Apoi vom aloca memorie necesar stocrii celor m x n elemente ale matricii. n final vom face legturile ntre vectorul de adrese i zona de memorie unde vor fi stocate elementele matricii. n figura 1 este prezentat schema de alocare dinamic de memorie descris:

    Codul C de alocare dinamic a memoriei pentru o matrice este: int i,j,m,n;

    float **a; printf("Dati dimensiunile matricii: "); scanf("%d%d",&m,&n); a=(float**)calloc(m,sizeof(float*)); if (a==NULL) {

    printf("Memorie insuficienta!"); exit(1);

    } a[0]=(float*)malloc(m*n,sizeof(float)); if (a[0]==NULL) {

    printf("Memorie insuficienta!"); exit(1);

    } for (i=1;i

  • 31

    adresa ctre zona de memorie aflat cu n csue (fiecare de lungime sizeof(float)) dup a[0] etc. Cu alte cuvinte la adresa a[0] se gsesc elementele primei linii, apoi n continuare elementele celei de-a doua linii care ncep s fie memorate de la adresa a[1], apoi a treia linie la adresa a[2] .a.m.d., iar la sfrit la adresa a[n-1] avem elementele ultimei linii. Eliberarea zonei de memorie ocupate de matricea a se face n doi pai: nti se elibereaz vectorul cu cele m x n elemente de tip float (aflat la adresa a[0]), iar apoi se elibereaz memoria ocupat cu vectorul de adrese (vector aflat la adresa a). Dezavantajul alocrii dinamice de mai sus const n faptul c se ncearc alocarea unei zone continue de memorie de lungime total m x n x sizeof(float) i s-ar putea s nu dispunem de o zon continu de memorie liber de o asemenea dimensiune. Acest dezavantaj n prezent nu poate fi considerat major, pentru c astzi calculatoarele dispun de memorie RAM de capaciti mari. Marele avantaj al alocrii dinamice de mai sus este dat de viteza mare de execuie datorat faptului c se fac numai dou alocri i tot attea eliberri de memorie.

    O alternativ la alocarea de mai sus este alocarea separat a memoriei pentru fiecare linie a matricii: int i,j,m,n;

    float **a; printf("Dati dimensiunile matricii: "); scanf("%d%d",&m,&n); a = (float**)calloc(m,sizeof(float*)); if (a==NULL) {

    printf("Memorie insuficienta!"); exit(1);

    } for (i=0;i

  • 32

    S facem observaia c n urma alocrii statice sau a oricrei alocri dinamice de mai sus, referirea la elementul matricii a aflat pe linia i i coloana j se face sub forma a[i][j].

    n continuare dm o secven de cod n care citim elementele unei matrici alocate static

    sau dinamic: for (i=0;i

  • 33

    } printf("Dati numarul de elemente al fiecarui vector:\n"); for (i=0;i

  • 34

    alocat anterior dinamic pentru a reine elementele matricei i apoi eliberm memoria ocupat de vectorul de adrese de nceput a liniilor matricei.

    Teme 1. De la tastatur se citete un numr natural nenul n. S se aloce dinamic memorie pentru o

    matrice triunghiular de numere reale (prima linie are un element, a doua are dou elemente, a treia trei elemente .a.m.d.). S se citeasc de la tastatur elementele matricei, s se calculeze i s se afieze mediile aritmetice pe linii. n final se va elibera memoria alocat dinamic pentru stocarea matricei.

    2. De la tastatur se citesc trei numre naturale nenule m, n i p. S se aloce dinamic memorie pentru o matrice tridimensional de dimensiuni m, n i p. S se citeasc de la tastatur elementele matricei. S se gseasc cel mai mare i cel mai mic element al matricei. n final s se elibereze memoria alocat dinamic pentru stocarea matricei.

    3. Construii dinamic un vector n care s depunei primele n numere naturale prime, unde n este citit de la tastatur.

    4. De la tastatur se citesc dou numre naturale nenule m i n. S se aloce dinamic memorie pentru o matrice de dimensiuni m i n. S se citeasc de la tastatur elementele matricei. S se depun elementele matricei luate n spiral ntr-un vector alocat dinamic. n final s se elibereze toate zonele de memorie alocate dinamic.

    De exemplu, pentru matricea:

    vectorul rezultat este (1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7). Unitatea de nvare M1.U6. Funcii n C

    Cuprins Obiective ................................ ................................ ................................ ................................ ..........

    Introducere ................................ ................................ ................................ ................................ .......

    U1.1. Funcii n C ................................ ................................ ................................ .............................

    Obiectivele unitii de nvare Ne propunem s vedem cum se redacteaz o funcie n C i cum se transmit parametrii funciei.

    121110987654321

  • 35

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere n C, C++ i Java (limbajele ce au la baz standardul ANSI C) ca subprograme nu avem dect funcii, o procedur putnd fi implement ca o funcie care returneaz tipul void (vid). n C, o funcie care returneaz alt tip dect void poate fi apelat att ca o funcie (n cadrul unei expresii), ct i ca o procedur (ignorndu-se valoarea returnat). De exemplu, funcia printf returneaz valoarea int, care reprezint numrul de octei care s-au afiat pe ecran. De cele mai multe ori (dup cum am vzut i n exemplele de mai sus) funcia printf este apelat ca o procedur, ignorndu-se valoarea returnat.

    U6.1. Funcii n C Dup cum am mai spus i n primul capitol, funciile pot fi descrise att la nceputul programului, deasupra funciei principale, ct i dup aceasta, situaie n care definiiile funciilor trebuie totui s apar deasupra funciei principale.

    Structura unei funcii n C este urmtoarea: tip nume_functie(argumente) {

    /* corpul functiei (instructiuni) */ } Definiia funciei ncepe cu tip, care este tipul valorii pe care o returneaz funcia. Dac tip nu este void, atunci funcia va conine de regul cel puin o instruciune return expresie. Dac execuia programului ajunge la o astfel de instruciune, atunci se evalueaz expresia a crei valoare trebuie s fie de tipul tip sau unul compatibil i valoarea obinut se returneaz, execuia programului continund cu instruciunile de dup locul n care a fost apelat funcia.

    Dac o funcie ce returneaz o valoare de un tip diferit de void nu se termin cu o instruciune return, atunci la compilare vom obine mesajul de atenionare Warning 5: Function should return a value.

    Spre exemplificare vom scrie o funcie care returneaz ca valoare a funciei media aritmetic a dou numere reale primite ca parametri: float medie(float x,float y)

    { return (x+y)/2;

    } void main() {

    float x,y; printf("Dati doua numere reale: "); scanf("%f%f",&x,&y); printf("Media aritmetica: %f",medie(x,y)); /* apel

  • 36

    functie */ }

    Rescriem programul de mai sus cu descrierea funciei medie dup funcia principal:

    float medie(float,float);

    void main() {

    float x,y; printf("Dati doua numere reale: "); scanf("%f%f",&x,&y); printf("Media este: %f",medie(x,y));

    } float medie(float x,float y) {

    return (x+y)/2; }

    Dup cum se poate observa mai sus, cnd descriem funcia medie dup funcia principal, la definiie nu este obligatoriu s dm numele argumentelor funciei, iar definiia se ncheie cu caracterul punct i virgul. n C parametrii ce se returneaz din funcie (variabilele de ieire) se transmit prin adres. n acest sens dm un exemplu n care media aritmetic este returnat printre parametrii funciei: void medie2(float x,float y,float *m)

    { *m=(x+y)/2;

    } void main() {

    float x,y,med; printf("Dati doua numere reale: "); scanf("%f%f",&x,&y); medie2(x,y,&med); /* apel functie */ printf("Media este: %f",med);

    }

    La apelul unei funcii valorile parametrilor de intrare, cei transmii prin valoare (aa cum este cazul parametrilor x i y), sunt copiate n zone de memorie noi i de aceea, dac le modificm, la prsirea funciei, valorile modificate se pierd, zonele de memorie alocate pentru aceti parametri n funcie fiind eliberate. Parametrul m al funciei medie2 este transmis prin adres. Mai exact, n funcie se transmite adresa la care este alocat memorie pentru variabila med din funcia principal. n funcia medie2 se modific valoarea de la adresa m, adres care coincide cu cea a variabilei med i de aceea valoarea calculat rmne n variabila med dup ce se prsete funcia.

  • 37

    Dac vrem ca o variabil transmis prin adres s nu poat fi modificat, punem n faa ei cuvntul rezervat const. n general, dac se ncearc modificarea unei variabile declarate cu const (a unei constante), atunci se semnaleaz eroare la compilare. Variabilele de tip tablou sunt pointeri, de aceea valorile de la adresele indicate de ele se transmit prin adres i eventualele modificri n interiorul funciei asupra elementelor tabloului pstrndu-se i la prsirea funciei. n continuare prezentm o funcie care calculeaz media aritmetic a elementelor unui ir de numere reale: float mediesir(int n,const float *a)

    { int i; float s=0;

    for (i=0;i

  • 38

    void AlocVector2(float **a,int n) /* corect */ { *a=(float*)malloc(n*sizeof(float)); } float* AlocVector3(int n) /* adresa noua returnata ca val. a fct. */ { return (float*)malloc(n*sizeof(float)); } int main() { int n; float *a;

    printf("n="); scanf("%d",&n); AlocVector1(a,n); /* nu se returneaza adresa noua */ AlocVector2(&a,n); /* pointerul a transmis prin adresa */ a=AlocVector3(n); if (a==NULL) {

    perror("Memorie insuficienta!"); return 1; /* parasire program cu cod de eroare */

    } /* .... */

    free(a); return 0; /* executia programul s-a incheiat cu succes */

    }

    Cnd ntr-o funcie se modific i se dorete a fi returnat o singur adres, din comoditate i pentru a fi mai uor de utilizat i de neles, noua adres se returneaz ca valoare a funciei. Aa este cazul funciilor de alocare dinamic de memorie din fiierul antet alloc.h (malloc, calloc, realloc). Fiecare dintre aceste funcii returneaz adresa zonei de memorie nou alocate ca valoare a funciei. Rescriem i noi funcia de alocare dinamic de memorie pentru un vector de elemente reale cu returnarea adresei spre noua zon de memorie ca valoare a funciei: #include

    #include float* AlocVector3(int n) /* adresa noua returnata ca val. a fct. */ { return (float*)malloc(n*sizeof(float)); } int main() { int n; float *a;

    printf("n=");

  • 39

    scanf("%d",&n); a=AlocVector3(n); if (a==NULL) {

    perror("Memorie insuficienta!"); return 1;

    } /* .... */

    free(a); return 0;

    }

    Rezumat Definiia unei funcii n C ncepe cu tipul returnat de ctre funcie, urmat de numele funciei i de lista de argumente dat ntre paranteze. Dac o funcie are tipul returnat void, atunci ea nu returneaz nici o valoare i se apeleaz practic ca o procedur. Parametrii de intrare n funcie se transmit prin valoare, iar cei de ieire se transmit prin adres.

    Teme 1. Scriei o funcie pentru cutare binar a unei valori ntr-un ir de numere reale sortat

    cresctor. 2. Scriei o funcie pentru cutare secvenial a unei valori ntr-un ir de numere ntregi. Funcia

    returneaz numrul de apariii a unei valori ntregi n ir. 3. Scriei o funcie care sorteaz cresctor un ir de numere reale folosind metoda BubbleSort. 4. Aceai problem folosind metoda QuickSort. 5. Aceai problem folosind metoda MergeSort. Unitatea de nvare M1.U7. String-uri

    Cuprins Obiective ................................ ................................ ................................ ................................ ...........

    Introducere ................................ ................................ ................................ ................................ ........

    U7.1. Funcii de lucru cu string-uri n C ................................ ................................ ..........................

    Obiectivele unitii de nvare Ne propunem s studiem modul n care se memoreaz i cum se lucreaz cu un string n limbajul C.

  • 40

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

    Introducere n C, un ir de caractere care se termin cu caracterul NULL sau \0 sau pur i simplu 0 (de unde i denumirea de ir de caractere NULL terminat) este echivalentul noiunii de string din alte limbaje (cum ar fi de exemplu limbajul Pascal). n consecin, n C o variabil string este de fapt un pointer de tipul char. Fiind vorba de un ir, alocarea memoriei pentru un string se poate face att static ct i dinamic. n fiierul antet stdio.h avem definite funciile gets i puts pentru citirea unui string de la tastatur, respectiv afiarea pe ecran a unui string. Ambele funcii primesc ca parametru o variabil de tip pointer ctre tipul char. Funcia gets, spre deosebire de scanf poate citi string-uri care conin spaii. n cazul funciei scanf se consider ncheiat introducerea unei valori pentru o variabil cnd se ntlnete caracterul spaiu sau unde s-a apsat Enter. De exemplu, dac se citete Popescu Ioan pentru un string s de la tastatur cu gets(s), atunci la adresa s se depun caracterele Popescu Ioan\0. Dac citirea se face ns cu scanf(%d,s), atunci la adresa s se depun caracterele Popescu\0 (citirea lui s se ncheie la apariia spaiului). Dac se afieaz un string s cu puts(s); atunci dup afiarea irului de la adresa s se face salt la nceputul liniei urmtoare, ceea ce nu se ntmpl dac afiarea se face cu printf(%s,s). U7.1. Funcii de lucru cu string-uri n C Fiierul antet string.h conine definiiile funciilor C de lucru cu string-uri:

    1) strcat(s1, s2) la sfritul string-ului s1 se adaug s2 i caracterul \0. 2) strchr(s,c) returneaz adresa de memorie ctre prima apariie a caracterului c n

    string-ul s. Dac nu exist caracterul n string, se returneaz adresa nul (NULL). 3) strcmp(s1,s2) returneaz o valoare ntreag care dac este 0, nseamn c irurile sunt

    identice ca i coninut. Dac valoarea returnat este negativ, atunci nseamn c string-ul s1 este naintea lui s2 din punct de vedere lexicografic (alfabetic). Dac ns valoarea returnat este pozitiv, atunci nseamn c string-ul s1 este dup s2 din punct de vedere lexicografic.

    4) stricmp(s1,s2) face acelai lucru ca i strcmp, cu deosebirea c nu se face distincie ntre litere mari i mici.

    5) strcnmp(s1,s2,n) realizeaz o comparaie ntre primele cel mult n caractere ale string-urilor s1 i s2.

    6) strcnmpi(s1,s2,n) realizeaz o comparaie ntre primele cel mult n caractere ale string-urilor s1 i s2 fr a se face distincie ntre litere mari i mici.

    7) strcpy(s1,s2) copiaz caracterele de la adresa s2 (pn se ntlnete inclusiv caracterul \0) la adresa s1. Cu alte cuvinte, se copiaz coninutul string-ului s2 peste string-ul s1.

    8) strlen(s) returneaz lungimea string-ului s (numrul de caractere pn la \0). 9) strlwr(s) transform literele mari din string-ul s n litere mici.

  • 41

    10) strupr(s) transform literele mici din string-ul s n litere mari. 11) strncat(s1,s2,n) adaug la sfritul string-ului s1 cel mult n caractere din string-ul s2,

    dup care se adaug caracterul \0. 12) strncpy(s1,s2,n) copiaz cel mult n caractere de la nceputul string-ului s2 la adresa

    s1, dup care se adaug la sfrit caracterul \0. 13) strnset(s,c,n) seteaz primele n caractere ale string-ului s la valoarea primit n

    parametrul c. 14) strpbrk(s1,s2) returneaz adresa de memorie la care apare prima dat un caracter din

    string-ul s2 n string-ul s1. 15) strchr(s,c) returneaz adresa la care apare ultima dat caracterul c n string-ul s. 16) strrev(s) inverseaz caracterele string-ului s (primul se schimb cu ultimul, al doilea

    cu penultimul etc.). 17) strset(s,c) seteaz toate caracterele string-ului s (pn la \0) la valoarea primit n

    parametrul c de tip char. 18) strstr(s1,s2) returneaz adresa de memorie la care apare prima dat string-ul s2 n s1.

    Dac s2 nu apare n s1, atunci se returneaz adresa nul NULL. 19) strxfrm(s1,s2,n) nlocuiete primele cel mult n caractere ale string-ului s1 cu primele

    cel mult n caractere din string-ul s2. Ca aplicaii pentru string-uri propunem:

    1. O funcie care returneaz printre parametri ei toate poziiile pe care apare un string s2 ntr-un string s1.

    2. Funcie pentru nlocuirea primei apariii n s1 a string-ului s2 cu string-ul s3. 3. Funcie pentru nlocuirea tuturor apariiilor lui s2 n s1 cu string-ul s3.

    Pentru aceste aplicaii vom folosi funcii din fiierul antet string.h, dar va trebui i s

    lucrm direct cu adrese de memorie i asta din cauz c un string este de fapt un pointer ctre tipul char. #include

    #include #include #include int strnfind(char *s1,char *s2,int *a) /* cautarea tuturor aparitiilor */ { char *s,*poz; int n=0; s=s1; /* s ia adresa la care se afla s1 */ poz=strstr(s,s2); /* cautare daca s2 apare in s */ while (poz!=NULL) /* daca s2 apare in s */ { a[n]=poz-s1+1; /* se memoreaz pozitia aparitiei */ s=poz+strlen(s2); n++; poz=strstr(s,s2); /* se cauta o noua aparitie */ } return n; /* se returneaza numarul de aparitii */ } int strreplace1(char*s1,char*s2,char*s3) /* inlocuirea primei aparitii */ {

  • 42

    char *poz,*s4; long l; poz=strstr(s1,s2); /* se cauta prima aparitie a lui s2 in s1 */ if (poz!=NULL) /* daca s2 apare in s */ { l=poz-s1; /* l este pozitia aparitiei (poate fi si 0) */ s4=(char*)malloc(strlen(s1)+1); /* aloc. mem. pt s4 */ strcpy(s4,s1); /* se copiaza s1 in s4 */ strncpy(s1,s4,l); /* se copiaza in s1 l caract din s4 */ s1[l]='\0'; /* se transforma s1 in string */ strcat(s1,s3); /* se adauga s3 la sfarsitul lui s1 */ strcat(s1,poz+strlen(s2)); /* adaugare la sf. lui s1 */ free(s4); /* ramase in string-ul initial dupa s2 */ return l+1; /* se returneaza pozitia inlocuirii */ } return 0; } int strreplace(char*s1,char*s2,char*s3) /* inloc. tuturor aparitiilor */ { int n=0; char *poz; poz=strstr(s1,s2); /* se cauta s2 in s1 */ while (poz!=NULL) /* daca exista s2 in s1 */

    { n++; strreplace1(s1,s2,s3); /* se inloc. s2 cu s3 in s1 */ poz=strstr(s1,s2); /* se cauta dinnou s2 in s1 */

    } return n; /* se returneaza numarul de inlocuiri efectuate */ } void main(void) /* testare functii */ { char s1[100],s2[100],s3[100]; int i,n,a[20]; strcpy(s1,"Acesta este un text si numai unul !"); strcpy(s2,"un"); /* string-ul care se inlocuieste */ strcpy(s3,"1"); /* string-ul cu care se face inlocuirea */ n=strnfind(s1,s2,a); if (n) { printf("'%s' apare in '%s' de %d ori, pe poz.:\n",s2,s1,n); for (i=0;i

  • 43

    Rezumat String-ul se memoreaz n C ntr-un ir de caractere obinuit. Caracterul \0 (caracterul cu codul ASCII 0, adic NULL) marcheaz sfritul string-ului. De aceea, despre string n C se spune c este un ir NULL terminat. n fiierul antet string.h gsim definiiile funciilor de lucru cu string-uri n C. Teme 1. Folosind funcia strrev scriei o funcie care verific dac un numr ntreg de tip unsigned

    long primit ca parametru este palindrom. Un numr ntreg este palindrom dac cifrele lui citite de la stnga spre dreapta sunt aceleai cu situaia n care sunt citite de la dreapta spre stnga.

    2. Scriei o funcie care returneaz numrul de cuvinte dintr-un string primit ca parametru. Cuvintele se consider a fi grupuri de caractere desprite prin spaii.

    Unitatea de nvare M1.U8. Tipul struct i l iste nlnuite

    Cuprins Obiective ................................ ................................ ................................ ................................ ...........

    Introducere ................................ ................................ ................................ ................................ ........

    U8.1. Structuri ................................ ................................ ................................ ................................ ..

    U8.2. Pointeri ctre structuri ................................ ................................ ................................ .............

    Obiectivele unitii de nvare Vom studia n acest capitol tipul struct, adic tipul compus sau nregistrare. Ne propunem, de asemenea, s vedem cum se implementeaz o list nlnuit cu ajutorul tipului struct.

    Durata medie de parcurgere a unitii de nvare este de 2 ore.

  • 44

    Introducere Pentru nceput, vom prezenta din punct de vedere teoretic noiunea de list nlnuit i principale structuri de date de tip list nlnuit, dup care vom vedea cum putem implementa listele nlnuite cu ajutorul pointerilor ctre structuri. O list nlnuit este o structur format dintr-o mulime de aa numite noduri legate ntre ele. n fiecare nod se memoreaz nite informaii (numere, caractere, string-uri etc.) i una sau mai multe adrese ale altor noduri din list sau adrese nule (legturile). Cele mai folosite liste nlnuite sunt cu una sau dou legturi (adrese ctre alte noduri). Listele cu dou legturi se numesc dublu nlnuite. Fiecare nod memoreaz informaia, iar fiecare dintre cele dou legturi este ctre un alt nod ale listei sau este o legtur vid. Iat un posibil exemplu de list dublu nlnuit: De departe cea mai folosit list dublu nlnuit este cea de tip arbore binar. Exist un nod special denumit rdcin. Nici un nod nu are legtura ctre rdcin. Pornind din rdcin se poate ajunge prin intermediul legturilor la orice alt nod al arborelui. Arborele binar nu are cicluri, adic pornind dintr-un nod x oarecare nu se poate ajunge prin intermediul legturilor din nou la nodul x . O list simplu nlnuit este format din noduri ce conin (fiecare) o informaie i o legtur ctre un alt nod sau o legtur nul. O list liniar simplu nlnuit are proprietatea c exist un nod denumit cap care are o legtur nul sau este legat de un alt nod al listei, care la rndul lui are legtura nul sau este legat de alt nod etc. Lista are proprietatea c pornind de la nodul cap se poate ajunge prin legturi la orice alt nod al listei i pentru fiecare nod x diferit de cap exist un singur nod y care este legat de x. Dup modul de introducere i de scoatere a informaiilor ntr-o list liniar avem liste de tip stiv sau coad. O stiv este o list liniar de tip LIFO (Last In First Out), adic ultimul intrat primul ieit. O stiv (dup cum sugereaz i numele) ne-o putem imagina cu nodurile stivuite unul peste cellalt. Introducerea unui nou nod se face peste ultimul introdus, iar scoaterea unei informaii se face din ultimul nod introdus n stiv. O stiv poate fi memorat printr-o list liniar simplu nlnuit n care fiecare nod reine adresa nodului de sub el (ca legtur). Primul nod introdus n stiv are legtura nul. Pentru o stiv se reine n permanen ntr-o variabil de tip pointer (denumit de obicei cap) adresa ctre ultimul nod al stivei. O stiv este goal (vid) cnd variabila cap reine adresa vid.

    info

    info info

    info info

    info info

    NULL

    NULL

  • 45

    Coada este tot o list liniar, dar de tipul FIFO (First In First Out), adic primul intrat primul ieit. Dup cum sugereaz i numele, nodurile ni le putem imagina aranjate n linie, unul dup altul, introducerea unui nou nod se face dup ultimul introdus anterior, de scos, se scoate primul nod introdus n coad. Coada poate fi i ea memorat cu ajutorul unei liste liniare simplu nlnuite n care introducerea unui nod se face ntr-o parte a listei, iar scoaterea se face din cealalt parte. Se rein n permanen adresa (ntr-o variabil de tip pointer denumit de obicei ultim) ultimului nod introdus n list i pe cea a primului introdus (ntr-o variabil de tip pointer denumit de obicei prim). Pentru a funciona eficient (s putem scoate rapid un nod), fiecare nod al cozii reine adresa nodului din spatele lui (cel introdus imediat dup el n coad). Este evident c primul nod are legtura nul. O coad special este aceea n care primul nod n loc s aib legtura nul, el este legat de ultimul nod al cozii. Aceast structur de date se numete coad circular.

    U8.1. Structuri Cu ajutorul structurilor putem defini tipuri noi de date, mai complexe pornind de la tipuri de date deja existente, care alctuiesc aa numitele cmpuri ale structurii. O st ructur se definete astfel: struct nume {

    tip1 camp1,camp2,...,campm1; tip2 camp1,camp2,...,campm2;

    /* .... */ tipn camp1,camp2,...,campmn;

    };

    Dup acolada care ncheie definirea structurii trebuie neaprat s punem semnul punct i virgul.

    ntre acolada } ce ncheie definirea structurii i semnul ; (punct i virgul) putem declara variabile care vor fi evident de tipul structurii respective.

    n C denumirea tipului structur definit n exemplul de mai sus este struct nume (denumirea structurii este precedat de cuvntul rezervat struct). n C++ nu este obligatoriu ca nainte de nume s punem cuvntul rezervat struct.

    Adesea se prefer (mai ales n C) definirea unei structuri n combinaie cu typedef. Pentru a ilustra acest lucru definim o structur pentru memorarea unui numr complex ca o pereche de numere reale: typedef struct

    { double re,im; } complex;

    n final pentru a exemplifica modul de lucru cu structuri vom citi datele despre o persoan ntr-o structur, dup care le vom afia: struct tpers

    { char nume[50],adresa[100]; int varsta;

  • 46

    }; typedef struct tpers tpers; void main() {

    struct tpers pers; puts("Dati datele despre o persoana:"); printf("Nume: "); gets(pers.nume); printf("Adresa: "); gets(pers.adresa); printf("Varsta: "); scanf("%d",&pers.varsta); puts("Am citit:"); printf("Nume: %s\n",pers.nume); printf("Adresa: %s\n",pers.adresa); printf("Varsta: %d\n",pers.varsta);

    }

    Dup cum se poate observa mai sus, referirea la un cmp al unei structuri se face sub forma var_structura.camp.

    Rezumat Un tip de date compus pornind de la tipurile de date deja existente n C se definete cu ajutorul cuvntului rerervat struct. Accesul la membrul unei structuri se face ajutorul operatorului punct sub forma variabila_tip_struct.membru.

    Teme 1. De la tastatur citii stocul unei firme ntr-un ir de elemente de tip struct. n structur se vor

    reine: denumirea produsului, cantitatea i preul. Sortai cresctor irul dup denumirea produselor, dup care afiai tabelar produsele dup modelul:

    --------------------------------------------------------------- |Nr. | DENUMIRE PRODUS |Cantitate| Pret |Valoare| |crt.| | | | | |----+------------------------------+---------+-------+-------| | 1|Placa de baza | 2.00| 401.44| 802.88| | 2|Tastatura | 1.00| 25.11| 25.11| | 3|Mouse | 22.00| 14.00| 308.00| | 4|Cablu retea | 117.50| 0.23| 27.03| |-----------------------------------+---------+-------+-------| | Valoare totala stoc: 1163.02| ---------------------------------------------------------------

  • 47

    2. Rezolvai problema 2 de la sfritul capitolului 3 folosind un vector de structuri. U8.2. Pointeri ctre structuri

    Cu ajutorul pointerilor ctre structuri putem implementa n C listele nlnuite. Un pointer ctre o structur se definete astfel: struct nume {

    /* definiri de campuri impreuna cu tipurile lor */ }; /* .... */ struct nume *p; /* pointer catre structura */ Este important de reinut faptul c referirea la un cmp al unei structuri memorate la adresa dat de pointerul p se face sub forma p->camp, ceea ce este echivalent cu a scrie (*p).camp. Practic, n C pentru pointeri ctre structuri este introdus operatorul binar special -> (format din caracterele minus i mai mare).

    Ca aplicaie prezentm un program ce simuleaz lucrul cu o stiv de caractere folosind pointeri ctre o structur denumit struct tstiva. #include

    #include #include struct tstiva {

    char info; struct tstiva *leg;

    }*cap; /* cap este o variabila globala care va retine adresa capului stivei *