Sunteți pe pagina 1din 197

TESTE DE CONTROL 1.

1 Limbajul C a fost inventat de:

a) Niklaus Wirth b) Dennis Ritchie c) Brian Kernighan


1.2 Limbajul C este:

a) un limbaj de nivel cobort b) un limbaj de nivel mediu c) un limbaj de nivel nalt


1.3 n C, o functie:

a) nu se poate declara sau defini n interiorul alteia b) poate apela o alta functie c) este alcatuita din antet si un bloc de declaratii si instructiuni delimitat de cuvintele begin si end
1.4 Functia main()

a) poate sa lipseasca dintr-un program C b) este obligatorie si figureaza prima n program


c) este obligatorie si poate figura oriunde n program 1.5 n C, o functie:

a) poate include declaratia sau definitia altei functii


b) poate fi declarata sau definita n interiorul altei functii

c) se poate apela numai din main() d) se poate apela din orice alta functie
RASPUNSURI

1.1-b

1.2-b

1.3-a, b

1.4-c

1.5-d

TESTE DE CONTROL 2.1 Un identificator este:

a) o secventa de cifre, liniute de subliniere si litere b) o secventa de cifre, liniute de subliniere si litere, primul caracter din secventa fiind obligatoriu liniuta de subliniere sau litera c) o secventa de cifre, litere, spatii, liniute de subliniere
2.2 n C un identificator poate fi scris:

a) numai cu litere mici b) numai cu litere mari c) combinat, cu litere mici si litere mari
2.3 Daca ntr-un identificator C se nlocuieste o litera mica (mare) cu litera sa omoloaga mare (mica) atunci:

a) identificatorul obtinut este considerat identic cu primul b) se obtine un identificator diferit


2.4 n C exista:

a) patru tipuri de constante: ntregi, reale, caracter, sir b) patru tipuri de constante: naturale, reale, complexe, caracter c) cinci tipuri de constante: ntregi, reale, complexe, caracter, sir
2.5 n C secventa de cifre 0631 este interpretata ca:

a) o constanta n baza 10 b) o constanta n baza 8 c) o constanta n baza 16


2.6 Secventa de caractere 0xABC poate fi:

a) un identificator b) o constanta hexazecimala c) si una si alta


2.7 Cuvintele cheie:

a) au semnificatii date de programator b) au semnificatii prestabilite c) au semnificatii date de contextul n care sunt utilizate
2.8 Despre constantele reale 3.0E-2 si .03 se poate afirma ca:

a) sunt gresite deoarece contin punct n loc de virgula b) sunt corecte si reprezinta valori diferite c) sunt corecte si reprezinta aceeasi valoare

d) sunt gresite deoarece prima contine litera E, iar a doua nu are parte ntreaga
2.9 Secventa a reprezinta:

a) un sir b) un caracter
2.10 Secventa Citirea matricii A reprezinta:

a) un sir b) un comentariu c) nici una, nici alta


2.11 Constructiile a si a) a: reprezinta acelasi lucru

b) reprezinta un sir, respectiv un caracter c) reprezinta un caracter, respectiv un sir

RASPUNSURI

2.1-b 2.6-b 2.11-c

2.2-c 2.7-b

2.3-b 2.8-c

2.4-a 2.9-b

2.5-b 2.10-c

TESTE DE CONTROL 3.1 n C exista tipurile fundamentale de date: a) b) c) d) char, int, float, double, void char, integer, real, double, void char, int, float, double, nul, boolean character, string, real, void

3.2 Modificatorii de semn a) schimba semnul unei expresii b) schimba semnul numai pentru valori ntregi c) au ca efect interpretarea diferita din punct de vedere al semnului, a informatiei memorate ntr-o anumita zona 3.3 Declaratiile char x; si signed char x; a) sunt echivalente b) sunt gresite c) sunt corecte 3.4 Declaratiile short int x; si int x; a) sunt echivalente b) sunt gresite c) sunt corecte 3.5 Declaratia a) b) c) d) float x,y; este echivalenta cu float x;float y; este gresita este echivalenta cu x,y:float; este echivalenta cu real x,y;

3.6 Linia de program char ch=A,Z; are semnificatia: a) variabila ch ia valori de la A la Z b) variabila ch este de tip char si este initializata cu valoarea A, iar variabila Z este de tip char c) tipul de date char ia valori de la A la Z 3.7. Liniile de program const ore_zi=24; int ore_zi=24; a) sunt echivalente b) sunt corecte si compatibile c) sunt corecte si incompatibile 3.8 Secventa de program int x=10,y=20,z=5,w=7; printf(\n x=%i y=%i z=%d,x,y);

afiseaza: a) x=10 y=20 z=5 b) date de iesire nedefinite c) x=10 y=20 3.9 Secventa de program: int x=10,y=20,z=5,w=7; printf(\n x=%d y=%i,x,y,z); a) afseaza x=10 y=20 b) afiseaza x=10 y=20 z=5 c) este gresita 3.10 Instructiunea printf() de mai jos printf(\nuu!\taurul \n-are importanta!); afiseaza a) \nuu!\taurul\n-are importanta! b) taurul n-are importanta! c) uu!aurul -are importanta! d) uu! aurul -are importanta! 3.11 Secventa de program int i=10,j=20; printf(\n i=%i,j=%i,j,i); afiseaza a) i=10, j=20 b) i=20, j=10 c) i=10% j=20% 3.12 Secventa de program int i=10,j=20; printf(\n i=%i,j=%j,i,j); a) afiseaza i=10,j=20 b) este gresita c) afiseaza i=%10,j=%20 3.13 Secventa de program char a=q; printf(\n a=%d,a); a) b) c) d) este gresita deoarece %d este descriptor pentru tipul int, nu pentru tipul char este corecta si afiseaza codul ASCII al caracterului q este corecta si afiseaza codul ASCII al caracterului a este corecta si afiseaza caracterul q

3.14 Secventa de program float x=32.75; printf(\n x=%e,x=%f,x,x); a) este gresita deoarece argumentul x se repeta b) este corecta si va afisa x=32.75,x=32.75 c) este corecta si va afisa x=3.275000e+01,x=32.750000 3.15 Secventa de program int x=439; printf(\n %o,x); afiseaza: a) 439 b) numarul 439 scris n baza 8

c)

numarul 439 scris n baza 16 int x=1011; printf(\n %x,x);

3.16 Secventa de program

afiseaza: a) valoarea lui x n binar b) valoarea lui x n hexazecimal c) valoarea lui x n octal 3.17 Secventa de program int x=12; float y=31.42; printf(\n x=%f y=%d,x,y); a) afiseaza x=12 y=31.42 b) afiseaza x=12.0 y=31 c) este gresita 3.18 Secventa de program float x=10.5; printf(\n x=%-10.5f,x); a) afiseaza x=-10.5 b) afiseaza x=10.50000 c) este gresita 3.19 Secventa de program float x=10.5; printf(\n x=%10.5,x); a) afiseaza x=10.5 b) afiseaza x= 10.50000 c) afiseaza x=10.50000

3.20 Despre secventele de program float x; scanf(%f,x); =i se a) b) c) float x; scanf(%f,&x); poate afirma ca: sunt corecte si au acelasi efect prima secventa este corecta si a doua incorecta prima secventa este incorecta si a doua corecta

3.21 Secventa de program printf(%.3s,abcde); a) afiseaza abc b) afiseaza abcde c) este gresita 3.22 Daca sirul care trebuie citit de la tastatura este abcdef atunci secventa scanf(%3s,sir); a) este gresita, deoarece variabila sir nu e precedata de operatorul & b) este gresita, deoarece sirul de intrare are 6 caractere, iar descriptorul %s prevede doar 3 caractere

c)

este corecta, dar se citesc doar caracterele abc

3.23 Secventa de program scanf(%d;%f;%s,&x,&y,sir); a) este gresita, deoarece descriptorii de format sunt despartiti prin semnul ; b) este gresita, deoarece variabila sir nu e precedata de operatorul & c) este corecta si realizeaza corect citirea daca datele din fluxul de intrare sunt despartite prin semnul ; 3.24 Operatorii n C pot fi: a) unari, binari b) unari, binari, ternari c) unari, binali, termali 3.25 Operatorii + si - pot fi: a) numai unari b) numai binari c) unari sau binari 3.26 Secventa de program: float x; int i; x=34.21; i=x; a) este gresita deoarece se atribuie valoarea reala din x variabilei ntregi i b) este corecta c) este corecta, iar i va primi valoarea 34 3.27 Secventa de program int i; float x; i=34; x=i; a) este gresita, deoarece se atribuie valoarea ntreaga din i variabilei reale x b) este corecta c) este corecta, iar x va primi valoarea 34 convertita n virgula mobila. 3.28 Linia de program V=(A=B*b)*h; a) este eronata deoarece contine operatorul de atribuire = de doua ori b) este corecta c) este corecta si este echivalenta cu secventa de program A=B* b; V=A*h; 3.29 Linia de program a=b=c=1; a) este corecta si e echivalenta cu secventa a=1; b=1; c=1; b) este corecta si e echivalenta cu secventa 1=a=b=c;

c)

este gresita deoarece operatorul de atribuire apare de mai multe ori x+=1;

3.30 Expresia este gresita este corecta si echivalenta cu x=x+1; este corecta si echivalenta cu x++; este corecta si echivalenta cu ++x; y=--x; a) este gresita b) este corecta si echivalenta cu secventa x=x-1; y=x; c) este corecta si echivalenta cu secventa y=x; y=x-1; 3.32 Expresia y=x--; a) este gresita b) este corecta si echivalenta cu secventa x=x-1; y=x; c) este corecta si echivalenta cu secventa y=x; x=x-1; 3.33 n urma executiei secventei de program: int i,j; i=19; j=i/4; a) j ia valoarea 4 b) j ia valoarea 4.75 c) j ia valoarea 5 3.34 n urma executiei secventei de program: int i,j; i=19; j=i%4; a) j ia valoarea 3 b) j ia valoarea 4 c) j ia valoarea 4.75 3.35 Daca a,b,c,d sunt variabile numerice atunci expresia (a<b)||(c>d) se poate scrie: a) a<b||c>d b) a<(b||c)>d c) c>d||a<b

a) b) c) d)

3.31 Expresia

3.36 n secventa de program int x=3,y=4,a,b,z; scanf(%i %i,&a,&b); z=(y>x)||(a<b); variabila z va lua a) o valoare nedefinita b) valoarea 1 c) o valoare care depinde de a =i b 3.37 n secventa de program int x=3,y=4,a,b,z; scanf(%i %i,&a,&b); z=(x>y)&&(a<b); variabila z va lua a) o valoare nedefinita b) valoarea 0 c) o valoare care depinde de a =i b 3.38 n secventa de program int a=3,b=4,x,y,z; z=(x=a+b,y=x); a) x ia valoarea 7 b) y ia valoarea 7 c) z ia valoarea 7 3.39 Operatorii pe bit se aplica a) valorilor 0 sau 1 pe care le iau anumite variabile de tip int b) variabilelor de tip char =i int c) variabilelor de tip float =i double 3.40 Daca doi biti notati a si b au valorile a=1 si b=1 atunci: a) b) c) d) a|b ia valoarea 1 a&b ia valoarea 1 a^b ia valoarea 0 ~a ia valoarea 0 a|b ia valoarea 1 a&b ia valoarea 0 a^b ia valoarea 1 ~b ia valoarea 1 int x=192; printf(\n x=%, x<<1); afiseaza a) 91 b) 192 c) 384 3.43 Complementul fata de 1 al numarului ntreg i se obtine ca: a) ~i b) 1-i 3.44 Complementul fata de 2 al numarului ntreg i se obtine ca:

3.41 Daca doi biti notati a si b au valorile a=1 si b=0 atunci: a) b) c) d)

3.42 Secventa de program

a) ~i+1 b) ~i-1 c) 2-i 3.45 Operatia x<<3 echivaleaza cu: a) b) c) d) a) b) c) d) o o o o o o o o nmultire a lui x cu 3 mpartire a lui x la 3 nmultire a lui x cu 23 mpartire a lui x cu 23

3.46 Operatia x>>2 echivaleaza cu: nmultire a lui x cu 2 mpartire a lui x la 2 nmultire a lui x cu 22 mpartire a lui x cu 22 int i=7; float x; x=(float)i/4; printf(\n x=%f,x); ..... a) este gresita b) este corecta si afiseaza x=1.750000 c) este corecta si afiseaza x=1

3.47 Secventa de program

RASPUNSURI 3.1-a 3.6-b 3.11-b 3.16-b 3.21-a 3.26-b, c 3.31-b 3.36-b 3.41-a, b, c, d 3.46-d 3.2-c 3.7-c 3.12-a 3.17-c 3.22-c 3.27-b, c 3.32-c 3.37-b 3.42-c 3.47b 3.3-a, c 3.8-b 3.13-b 3.18-b 3.23-c 3.28-b, c 3.33-a 3.38-a, b, c 3.43-a 3.4-c 3.9-a 3.14-c 3.19-b 3.24-b 3.29-a 3.34-a 3.39-b 3.44-a 3.5-a 3.10-d 3.15-b 3.20-c 3.25-c 3.30-b, c, d 3.35-a, c 3.40-a, b, c, d 3.45-c

TESTE DE CONTROL 4.1 Secventa de program int x=-3,y=7;

if (x>0) printf(\n x=%i,x); else if (y) printf(\n y=%i,y); else printf(\n y=0); a) afiseaza y=7 b) este gresita deoarece o instructiune if contine o alta instructiune if c) afiseaza x=-3
4.2 Secventa de program if(x)

if(y) y=3; else y=5; este echivalenta cu a) if(x) if(y) y=3; else; else y=5; b) if(x&&y) y=3; else y=5; c) if(x) { if(y) y=3; } else y=5; d) nici una din secventele a), b), c)
4.3 Secventa de program

int x=1,y=2; if ((x=0)&&(y=4)) printf(\n x=%i y=%i,x,y); a) afiseaza x=1 y=2 b) afiseaza x=0 y=4 c) nu afiseaza nimic deoarece printf() nu se executa

4.4 Secventa de program

if(x) y=1/x; a) este gresita b) este echivalenta cu if (x!=0) y=1/x; c) este chivalenta cu if x!=0 y=1/x;
4.5 Secventa de program

if (x>=3) y=sqrt(x-3); else; a) este gresita deoarece contine semnul ; nainte de else b) este gresita deoarece dupa else lipseste o instructiune C c) este corecta si este echivalenta cu secventa if(x>=3) y=sqrt(x-3);
4.6 Secventa de program

int x=3,y=6; if (x>0) printf(\n x=%i,x); else y=x+3; printf(\n y=%i,y);


afiseaza

a) x=3 b) y=6 c) x=3 y=6


4.7 Secventa de program

int x=3,y=4; printf(\n %i,x>y?x:y); a) afiseaza 3 b) afiseaza 4 c) este gresita deoarece argumentul functiei printf() este o expresie, nu o variabila.
4.8 Secventa de program

char x=a; switch(x) { case a:printf(\n amic); case A:printf(\n Amare); } afiseaza

a) amic b) Amare c) amic Amare


4.9 Secventa de program

a) b) c) d)

int x=3; if (x=4) printf(\n x=%i,x); este gresita este corecta, dar nu afiseaza nimic este corecta si afiseaza valoarea 4 este corecta si afiseaza valoarea 3 char x=a; switch(x) { case a:printf(\n amic);break; case A:printf(\n Amare); } afiseaza amic afiseaza Amare afiseaza amic Amare este gresita deoarece al doilea case nu se termina cu break

4.10 Secventa de program

a) b) c) d)

4.11 Secventa de program

int i=3; while(i) i--; printf(%3i,i); a) afiseaza 2 1 0 b) contine o bucla eterna c) afiseaza 0
4.12 Secventa de program

int i=3; while(i) printf(%3i,i); i--; a) afiseaza valoarea 3 de 3 ori b) afiseaza valoarea 3 de o infinitate de ori (bucla eterna) c) afiseaza 3 2 1
4.13 Secventa de program

int i=3; while(i); printf(%3i,i);

a) b) c) d)

i--; afiseaza valoarea 3 de 3 ori afiseaza valoarea 3 de o infinitate de ori (bucla eterna) contine o bucla eterna si nu afiseaza nimic afiseaza 3 2 1

4.14 Secventa de program

int i=3; while(i) { printf(%3i,i); i--; } a) afiseaza 3 2 1 0 b) afiseaza 3 2 1 c) contine o bucla eterna
4.15 Secventa de program

int i=0; while(i) printf(%3i,i); a) afiseaza valoarea 0 b) afiseaza valoarea 0 de o infinitate de ori (bucla eterna) c) nu afiseaza nimic
4.16 Secventa de program

a) b) c) d) e) f)

int i=0; for (;i<3;i++); printf(%3i,i); este gresita sintactic deoarece lipseste componenta de initializare a instructiunii for afiseaza valorile 0 1 2 3 afiseaza valorile 0 1 2 afiseaza valoarea 3 contine o bucla eterna afisndu-se permanent valoarea 0 contine o bucla eterna si nu afiseaza nimic

4.17 Secventa de program

int i,j; for (i=1,j=10;i<=5;i++,j--) printf(\n %i %2i,i,j); a) este gresita deoarece instructiunea for contine doua variabile contor i,j b) este gresita deoarece componenta initializare a instructiunii for contine doua initializari

c) este corecta si afiseaza 1 10 2 9 3 8 4 7 5 6


4.18 Secventa de program

int i; for (i=1;-3<=i&&i<1;i--) printf(\n%3i,i); a) nu afiseaza nimic b) afiseaza 0 -1 -2 -3 c) contine o bucla eterna
4.19 n secventa de program

a) b) c) d)

int x=1,i; for(i=1;x<=3;i++) scanf(%i,&x); functia scanf() se executa de 3 ori functia scanf() se executa de o infinitate de ori instructiunea for este gresita deoarece contorul i nu se testeaza niciodata se iese din bucla numai dupa ce s-a citit o valoare x>3

4.20 Secventa de program

int i; for(i=1,i<=3;i++) printf(%3i,i) a) este gresita b) contine o bucla eterna c) afiseaza 1 2 3


4.21 Secventa de program

int i; for (i=1,i<=3;i++) printf(%3i,i) a) contine o instructiune for gresita sintactic b) afiseaza 1 2 3 c) contine o bucla for eterna
4.22 Secventa de program

for(); a) este o bucla for eterna b) este gresita sintactic c) apeleaza functia for()
4.23 Secventa de program

float x=-3.2; do scanf(%f,&x);

a) b) c) d)

while (x>0); citeste x pna cnd x>0 citeste x ct timp x>0 este gresita deoarece functia scanf() nu este pusa ntre acolade nu executa nici o citire a lui x int j; do j=3; while (j<=4); { printf(%3i,j); j++; } afiseaza valoarea 3 afiseaza valorile 3 4 contine o bucla do-while eterna contine o bucla while eterna este gresita

4.24 Secventa de program

a) b) c) d) e)

4.25 Secventa de program

int i=1; do printf(\n%i,i); i++; while(i<=2); a) afiseaza 1 2 b) este gresita deoarece instructiunile dintre do si while trebuie sa fie cuprinse ntre acolade
4.26 Secventa de program

int i,j; for(i=1;i<=2;i++) for(j=1;j<=2;j++) printf(\n i=%i j=%i,i,j);


afiseaza

a) i=1 b) i=1

j=1 i=2 j=1 i=1 i=2 i=2 j=1 i=2 i=1 i=2

j=2 j=2 j=1 j=2 j=1 j=2 j=2

c) i=1

4.27 Secventa de program

int i,j; for(i=1;i<=3;i++) { printf(\n); for(j=1;j<=i;j++) printf(%3i,j); } a) este gresita deoarece limitele de variatie ale contorului j depind de contorul i al primului ciclu b) este corecta si afiseaza 1 1 2 1 2 3 c) este corecta si afiseaza 1 2 3 1 2 1
4.28 Secventa de program

int i; for(i=1;i<=5;i++) { printf(%3i,i); if(i==3)break; } a) este gresita deoarece instructiunea break se foloseste numai asociata cu switch b) este corecta si afiseaza 1 2 3 c) este corecta si afiseaza 1 2 3 4 5 deoarece instructiunea break cuprinsa n corpul unui ciclu for nu are nici un efect
4.29 Secventa de program

int i,j; for(i=1;i-3;i++) for(j=0;j-9;j++) { printf(\n Sunt aici!); if (!j)break; } a) afiseaza mesajul Sunt aici! de 2 ori b) afiseaza mesajul Sunt aici! de 30 ori c) afiseaza mesajul Sunt aici! o data
4.30 Secventa de program

int s,i; for(s=0,i=10;i;i--) { s+=i;

if(i-5)continue; } a) calculeaza suma s=10+9+8+7+6+4+3+2+1 b) calculeaza suma s=10+9+8+7+6+5+4+3+2+1 c) calculeaza suma s=5
4.31 Secventa de program

int s,i; for(s=0;i=10;i;i--) { if(i-5)continue; s+=i; } a) calculeaza suma s=10+9+8+7+6+4+3+2+1 b) calculeaza suma s=10+9+8+7+6+5+4+3+2+1 c) calculeaza suma s=5
4.32 Secventa de program

int s,i; for(s=0;i=10;i;i--) { if(i==5)continue; s+=i; } a) calculeaza suma s=10+9+8+7+6+4+3+2+1 b) calculeaza suma s=10+9+8+7+6+5+4+3+2+1 c) calculeaza suma s=5
4.33 Secventa de program

int i=1; while(i<3) { if!(i==2)continue; printf(%3i,i); i++; } a) afiseaza 1 si intra ntr-o bucla eterna b) afiseaza 1 2 3 c) afiseaza 1 3
4.34 Secventa de program

int i=1,s=0; et1:if(i>10) goto et2; s+=i; i++; goto et1; et2:printf(\n s=%d,s); a) este gresita deoarece contine salturi napoi

b) este corecta si realizeaza o iteratie pentru calculul primelor 10 numere naturale c) este corecta si este echivalenta cu secventa d) este corecta si echivalenta cu secventa int i=1,s=0; while(i<11) { s+=i; i++; } printf(\n s=%d,s);

RASPUNSURI

4.1-a 4.6-c 4.11-c 4.16-d 4.21-a 4.26-b 4.31-c

4.2-d 4.7-b 4.12-b 4.17-c 4.22-b 4.27-b 4.32-a

4.3-c 4.8-c 4.13-c 4.18-a 4.23-b 4.28-b 4.33-a

4.4-b 4.9-c 4.14-b 4.19-d 4.24-c 4.29-a 4.34-b, c,

4.5-c 4.10-a 4.15-c 4.20-a 4.25-b 4.30-b d

TESTE DE CONTROL
5.1 Declaratia float x[100]; nseamna a) rezervarea n memorie a 100 locatii la adrese consecutive b) rezervarea n memorie a 100 locatii la adrese ntmplatoare c) rezervarea a 100 de octeti pentru variabila reala x 5.2 Secventa de program int x[m][n],m=3,n=2; a) este corecta deoarece dimensiunile m si n ale matricii sunt cunoscute b) este gresita deoarece m si n trebuia sa fie declarati nainte de x c) este gresita deoarece la declarare n main(), dimensiunile unui tablou trebuie sa fie constante si nu variabile 5.3 Secventa de program int y[10]; y[10]=7; a) atribuie componentei y[10] a tabloului y valoarea 7 b) este gresita deoarece componentele tabloului sunt y[0],y[1],y[2],...,y[9] c) este corecta deoarece componentele tabloului sunt y[1],y[2],...,y[10] 5.4 n secventa de program int x[3][2]; x[1,2]=5; a) atribuirea x[1,2]=5 este echivalenta cu x[1][2]=5 b) atribuirea x[1,2]=5 este corecta sintactic si nseamna x[2] 5.5 Urmatoarele trei declaratii ale tabloului x, int x[6]; int x[3][2]; int x[2][3]; a) sunt echivalente b) nu sunt echivalente 5.6 n urma initializarii tabloului a int a[3][2]={1,2,3,4,5,6}; componenta a[1][0] are valoarea a) 2 b) 3 c) alta valoare dect 2 sau 3

5.7 Initializarea int a[3][2]={1,2,3,4,5,6}; este echivalenta cu: a) int a[3][2]={ {1,2}, {3,4}, {5,6}, }; b) int a[3][2]={ {1,4}, {2,5}, {3,6}, }; 5.8 Initializarea float x[2][3]={1,2,3,4,5,6}; este echivalenta cu a) float x[][]={1,2,3,4,5,6}; b) float x[][3]={1,2,3,4,5,6}; c) float x[2][]={1,2,3,4,5,6}; 5.9 Secventa de program int x[10],i; for(i=1;i<=10;i++) scanf(%d,&x[i]); a) este gresita sintactic b) nu initializeaza toate componentele vectorului x c) produce eroare la executie 5.10 Secventa de program char x[]=abcd; este echivalenta cu: a) initializarea char x[]={a,b,c,d}; b) initializarea char x[4]={a,b,c,d}; c) initializarea char x[]={a,b,c,d,\0}; 5.11 Secventa de program char x[]=abcd; putch(x[1]); a) afiseaza caracterul a b) afiseaza caracterul b c) este gresita deoarece elementele dintr-un sir nu se pot referi indexat 5.12 Secventa de program char x[20]=abcd;

printf(\n Lungimea sirului=%i,strlen(x)); afiseaza mesajul a) Lungimea sirului=20 b) Lungimea sirului=4 c) Lungimea sirului=5 5.13 Secventa de program char x[]= rac; char y[]= arc; printf(%d,strcmp(x,y)); afiseaza o valoare a) egala cu zero b) mai mica strict dect zero c) mai mare strict dect zero 5.14 Secventa de program char x[]=ac; char y[]=ar; strcat(x,y); printf(\n %s,x); afiseaza a) acar b) arac 5.15 n secventa de program char x[20]; x=Ploiesti; atribuirea x=Ploiesti; a) este gresita b) este gresita, iar copierea sirului Ploiesti n variabila x se realizeaza prin strcpy(x,Ploiesti); c) este corecta

5.16 Secventa de program char x[20]; gets(x); puts(x); afiseaza acelasi sir daca nlocuim gets(x); cu: a) gets(&x); b) scanf(%s,&x); c) scanf(%s,x);

5.17 n secventa de program int i; char x[20]=xyzw; puts(x); apelul puts(x); poate fi nlocuit cu: a) printf(\n %s,x); b) for(i=0;i<5;i++) putch(x[i]);

RASPUNSURI 5.1-a 5.2-c 5.6-b 5.7-a 5.11-b 5.12-b 5.16-a, b, c 5.17-a, b 5.3-b 5.8-b 5.13-c 5.4-b 5.9-b 5.14-a 5.5-b 5.10-c 5.15-a, b

TESTE DE CONTROL 6.1 Un pointer este: a) o adresa de memorie b) o variabila care poate memora adrese de memorie c) o zona de memorie dinamica 6.2 n secventa de program int x,*p; p=&x; atribuirea este: a) corecta, deoarece se atribuie unui pointer o adresa de memorie b) incorecta, deoarece trebuia precedata de o atribuire pentru variabila x 6.3 n secventa de program int x=10,*p; p=x; atribuirea este: a) corecta, deoarece se atribuie lui p o variabila alocata static b) corecta, deoarece variabila x a fost initializata c) incorecta, deoarece pointerului p trebuie sa i se atribuie adrese de memorie 6.4 n secventa de program int *p,x; x=31; *p=x; atribuirea este: a) corecta b) incorecta, deoarece pointerul p nu indica nici o locatie c) este corecta si echivalenta cu p=p*x; 6.5 Secventa de program int x,*p,*q; x=11; p=q=&x; printf(%d,*p); a) afiseaza valoarea 11 b) este gresita, deoarece n expresia p=q=&x; semnul = apare de doua ori c) este gresita, deoarece descriptorul de format pentru pointeri este %p si nu %d

6.6 Secventa de program int x,y,*p; x=5; y=10; p=&x; &y=p; printf(%d,y); a) este corecta si afiseaza valoarea 5 b) este incorecta, deoarece atribuirea &y=p; este incorecta c) este corecta si afiseaza valoarea 10 6.7 Secventa de program int *x[10],y=10; x[1]=&y; printf(%d,*x[1]); a) este gresita, deoarece componenta x[1] nu poate primi drept valoare o adresa b) este corecta si afiseaza valoarea 10 c) este gresita deoarece expresia *x[1] este gresita 6.8 Secventa de program char *s=Imi place limbajul C!; int i; for(i=0;s[i];i++) printf(\n %c,s[i]); a) este gresita, deoarece s este declarat ca pointer catre char si este folosit ca un tablou b) este corecta deoarece un pointer catre char poate fi interpretat ca si un tablou de caractere c) este corecta si afiseaza mesajul Imi place limbajul C! pe verticala 6.9 O declaratie de forma: char *p; poate avea interpretarea: a) variabila p este pointer catre char b) variabila p poate contine adresa unui sir de caractere c) variabila p este numele unui vector de caractere alocate dinamic 6.10 Secventa de program int x[10],*p=x; a) este echivalenta cu secventa int x[10]; *p=x; b) este incorecta, deoarece n locatia indicata de p se memoreaza o valoare nedefinita x

c)

este corecta si este echivalenta cu int x[10],*p=&x[0];

d) este corecta si este echivalenta cu int x[10],*p=x[0]; e) este corecta si este echivalenta cu int x[10],*p=&x; 6.11 n secventa de program float x[10],y[10],*p; x=y; p=x; a) prima atribuire este corecta b) a doua atribuire e corecta c) ambele atribuiri sunt corecte 6.12 Declaratia int (*x)[10]; a) este gresita b) este echivalenta cu int *x[10]; c) este corecta si reprezinta declaratia unui pointer catre un tablou unidimensional de 10 componente int x[3]; int(*p)[3]; p=x; p++; pointerul p: a) indica componenta x[1] a vectorului x[10] b) indica locatia cu adresa x+10*sizeof(int) 6.14 Secventa de program int *p,x[]={1,2,3}; p=x; p+=2; printf(%d,*p); a) b) c) d) afiseaza valoarea 3 afiseaza valoarea 2 este gresita, deoarece n expresia p=x; se atribuie un nume de tablou unui pointer este gresita, deoarece adunarea unui ntreg la un pointer ( expresia p+=2;) este interzisa x[i] x+i &x[i] *(x+i)

6.13 n secventa de program

6.15 Componenta i a unui tablou x[10],0<=i<=9 se poate indica prin a) b) c) d)

6.16 Valoarea componentei i a unui tablou x[0],0<=i<=9 se poate obtine folosind notatia

a) b) c) d)

x[i] x+i &x[i] *(x+i) int *p,**q,x=10; p=&x; q=&p; printf(\n %d,**q);

6.17 Secventa de program

este gresita, deoarece n declaratie q e precedat de doua semne * si nu doar de unul b) este gresita, deoarece intentia de a memora adresa unui pointer (expresia q=&p;) tot ntr-un pointer este o eroare c) este corecta, deoarece operatiile cu pointerul p si pointerul dublu q sunt corecte d) este corecta si afiseaza valoarea 10 a) RASPUNSURI 6.1-b 6.6-b 6.11-b 6.16-a, d 6.2-a 6.7-b 6.12-c 6.17-c, d 6.3-c 6.8-b, c 6.13-b 6.4-b 6.9-a, b, c 6.14-a 6.5-a 6.10-c, e 6.15-b, c

TESTE DE CONTROL
7.1 Secventa de program struct { int x; float y; }z; scanf(%d,&x); este gresita, deoarece: a) la structura din secventa lipseste numele structurii b) n apelul functiei scanf(), cmpul x este tratat ca o variabila 7.2 Selectarea unui cmp din structura se face utiliznd simbolul: a) . b) ; c) ? d) : e) % 7.3 Daca p este un pointer catre structura struct numere { int x; float y; }; atunci expresia p? x este echivalenta cu:

a) b) c)

*p.x (*p).x *p.(x)

7.4 Atribuirea informatiei dintr-o variabila structura x unei alte variabile structura y de acelasi tip se poate face: a) scriind y=x; b) atribuind valorile cmpurilor structurii x cmpurilor corespunzatoare din y 7.5 Secventa de program int y; struct num { char *x; int y; }a,*p; y=10; a? y=y; p.x= Eroare; este gresita deoarece: a) y este n acelasi timp nume de variabila si de cmp b) cmpul x nu este selectat cu ? ci cu ajutorul operatorului punct c) cmpul y nu este selectat cu operatorul punct, ci cu ajutorul operatorului ? 7.6 Structura

struct persoana { int salariu; struct persoana *p; struct inform { long int telefon; char *adresa; }inf; }pers; este: a) gresita, deoarece cmpul pointer p indica catre structura din care face parte b) corecta c) gresita, deoarece structura persoana include o alta structura (structura inform) 7.7 Secventa de program struct a { int x; float z; }w; struct b { int x; float y; }z; w.x=1; z.x=2; a) este gresita, deoarece structurile a si b contin un cmp cu nume comun (cmpul x) b) este corecta, deoarece doua structuri diferite pot avea cmpuri cu nume comune 7.8 Structura struct a { int x; float y[3][2]; }a; a) este incorecta, deoarece contine un cmp care este tablou b) este corecta, iar referirea la elementul y[0][1] se poate face sub forma a.y[0][1]. 7.9 Secventa de program struct material { int cant; float pu,valoare; }maga[3]; maga[0].cant=100; este: a) incorecta deoarece componentele tabloului maga[5] sunt de tip struct b) incorecta deoarece cmpurile pu si valoare nu sunt declarate sub forma float pu; float valoare; este corecta

c)

7.10 Lungimea unei structuri se afla a) adunnd lungimile truturor cmpurilor care compun structura b) utiliznd operatorul sizeof 7.11 Structura struct a { unsigned adm:1; int nota; }elev; a) este gresita deoarece contine un cmp de biti de lungime 1 bit b) este corecta, deoarece o structura poate contine att cmpuri obisnuite ct si cmpuri de biti 7.12 Structura struct a { unsigned:4; unsigned x:2; float y:3; }; este gresita deoarece a) primul cmp de biti nu are nume b) al treilea cmp de biti nu este de tip unsigned sau signed 7.13 Membrii unei uniuni a) mpart aceeasi zona de memorie b) au rezervate zone de memorie diferite 7.14 Operatorii de selectare . si ? se pot folosi pentru selectia cmpurilor a) numai la structuri b) la structuri si la uniuni c) numai la uniuni 7.15 Secventa de program union { float a; int b; }z={10}; a) este gresita deoarece nu are nume b) este corecta iar cmpului a i se atribuie valoarea 10 c) este corecta iar cmpului b i se atribuie valoare 10 7.16 Lungimea unei uniuni se afla a) calculnd maximul dintre lungimile membrilor uniunii b) utiliznd operatorul sizeof 7.17 Secventa de program enum masini{Trabant,Dacia,Audi,Marcedes}masina_ta; masina_ta=Dacia; printf(\n Masina ta este %s,masina_ta); a) este incorecta deoarece constanta simbolica Dacia este tratata ca un sir

b) este corecta si afiseaza cuvntul Dacia c) este incorecta deoarece este ilegala atribuirea masina_ta=Dacia; 7.18 Numele simbolice dintr-o lista de enumerare a) b) c) d) se pot atribui unei variabile de tip corespunzator se pot citi de la tastatura se pot scrie nemijlocit pot figura ca o constanta case ntr-o instructiune switch enum luna_an{ianuarie,martie=3,mai}; valoarea atasata numelui simbolic mai este:

7.19 n secventa de program

a) b) c)

3 4 5

7.20 Declaratia typedef enum{false,true}boolean; a) este incorecta deoarece lipseste numele tipului enumerare b) este corecta si asociaza enumerarii un nume nou (boolean) c) este corecta si defineste n C tipul boolean pe care standardul ANSI C nu-l prevede.

RASPUNSURI 7.1-b 7.6-b 7.11-b 7.16-b 7.2-a, c 7.7-b 7.12-b 7.17-a 7.3-b 7.8-b 7.13-a 7.18-a, d 7.4-a, b 7.9-c 7.14-b 7.19-b 7.5-b, c 7.10-b 7.15-b 7.20-b

TESTE DE CONTROL
8.1 Definitia unei functii C se poate face a) numai n forma moderna b) att n forma clasica ct si n forma moderna 8.2 Definitia moderna float min(int a,int b) { instructiuni } este echivalenta n forma clasica cu secventa: a) float min(int a,b) { instructiuni } float min(a,b) int a,b; { instructiuni }

b)

c)

float min() { int a,b instructiuni }

d)

float min(a,b) { int a,b; instructiuni }

8.3 O functie declarata fara tip ntoarce a) o valoare de tip int b) o valoare de tip float c) o valoare de tip void 8.4 O functie C nu poate sa ntoarca a) o valoare de tip tablou b) o valoare de tip struct c) un pointer de tip void 8.5 Lista declaratii parametrii din prototipul unei functii a) b) c) d) poate sa lipseasca complet, dar trebuie pastrate parantezele rotunde poate sa lipseasca si acest lucru sa fie semnalizat prin cuvntul void poate sa contina doar numele tipurilor de date ale parametrilor poate sa contina doar numele parametrilor

8.6 Functia float f(int x) { if(x) return -1; else return 1; } a) este incorect scrisa deoarece are doua instructiuni return b) este corect scrisa si ntoarce la apel valorile -1 sau 1 convertite la tipul float c) este incorect scrisa deoarece nu are semnul ; dupa declaratorul float f(int x) 8.7 Pentru a lucra corect, functia f(float x) { float y; y=x; } a) nu trebuie sa contina o instructiune return, deoarece este implicit de tip void b) trebuie sa contina o instructiune return c) trebuie sa contina o instructiune de forma return expresie 8.8 Functia void f(int x,int *y) { *y=x; } este scrisa incorect deoarece corpul functiei nu contine o instructiune return de ntorcere n functia apelanta b) este scrisa corect iar ntorcerea n functia apelanta este determinata de ntlnirea acoladei } a) 8.9 Functia void f(float x) { float y=x; return y*y; } este gresit scrisa, deoarece fiind de tip void nu este necesar sa contina o instructiune return b) este corect scrisa, deoarece instructiunea return este obligatorie a) 8.10 Declaratia unei functii nainte de apelare este obligatorie daca functia este: a) de tip int b) de tip float c) de orice tip diferit de tipul int 8.11 n declaratia unei functii a) prezenta listei de declaratii parametrii este optionala

b) daca lista declaratii parametrii este vida trebuie scris obligatoriu cuvntul void ntre parantezele rotunde c) daca lista declaratii parametrii este vida este recomandabil sa se scrie cuvntul void ntre parantezele rotunde d) numele de parametri pot lipsi, dar tipurile de date la care apartin parametrii trebuie sa existe obligatoriu 8.12 O declaratie de forma tip nume_functie(); reprezinta a) forma traditionala de declaratie a unei functii b) forma moderna de declaratie a unei functii (prototipul functiei) 8.13 Secventa de program float f(int a,float b) poate fi considerata a) declaratia (prototipul) unei functiei f() b) antetul din definitia functiei f() c) att prototip ct si antet de functie 8.14 Numarul parametrilor din lista de parametri actuali ai unei functii a) trebuie sa fie obligatoriu fix b) este totdeauna variabil c) poate fi att fix ct si variabil 8.15 Prototipurile sunt importante deoarece a) permit compilatorului sa verifice corectitudinea apelului unei functii b) contribuie la cresterea lizibilitatii codului sursa 8.16 Corespondenta dintre parametri actuali si formali se poate face a) la ntmplare b) pozitional 8.17 Parametrii actuali si formali care au aceeasi pozitie n lista de parametri trebuie sa aiba obligatoriu a) acelasi tip b) tipuri compatibile 8.18 Transferul informatiei ntre functia apelanta si functia apelata se poate face a) numai prin valoare b) numai prin referinta c) si prin valoare si prin referinta 8.19 Transferul prin valoare consta n copierea valorii parametrului actual n zona de memorie a parametrului formal corespunzator, n momentul efectuarii apelului b) transmiterea adresei parametrului actual parametrului formal corespunzator care trebuie sa fie un pointer a)

8.20 Secventa de program void f(int x); void main(void) { int x=7; f(x); printf(\nx= %d,x); } void f(int x) { x=10; } a) este gresita, deoarece parametrul formal si actual au acelasi nume b) este corecta si afiseaza x=10 c) este corecta si afiseaza x=7 8.21 Secventa de program void f(int *x); void main(void) { int x=7; f(&x); printf(\n x=%d,x); } void f(int *x) { *x=10; } a) este gresita, deoarece parametrului formal i se transmite o adresa si nu o valoare b) este corecta si afiseaza x=7 c) este corecta si afiseaza x=10 8.22 Secventa de program s(int x[2][3]); void main(void) { int y[2][4]={1,2,3,4,5,6,7,8},sum; sum=s(y); printf(\n Suma=%d,sum); } s(int x[2][3]) { int i,sum=0; for(i=0;i<2;i++) for(j=0;j<3;j++) sum+=x[i][j];

return sum; } a) este corecta si afiseaza suma=24 b) este corecta si afiseaza suma=21 c) este incorecta, deoarece parametrul formal x si parametrul formal y trebuie sa aiba aceeasi dimensiune 8.23 Secventa de program s(int x[],int m); void main(void) { int y[3]={1,2,3},m=3,suma; suma=s(y,m); printf(\n Suma=%d,sum); } s(int x[],int m) { int i,sum=0; for(i=0;i<m;i++) sum+=x[i]; return sum; } a) este gresita, deoarece parametrul formal x nu are precizata dimensiunea b) este corecta, deoarece la apel se transmite numele tabloului (care este pointer catre prima componenta a tabloului) si dimensiunea sa 8.24 Secventa de program void afis(int x[][],int m,int n); void main(void) { int y[2][3]={1,2,3,4,5,6},m=1,n=3; afis(y,m,n); } void afis(int x[][],int m,int n) { int i,j; for(i=0;i<m;i++) for(j=0;j<n;j++) printf(\n x[%i][%i]=%d,i,j,x[i][j]); } este incorecta, deoarece parametrul tablou x nu poate avea prima dimensiune neprecizata b) este incorecta deoarece parametrul tablou x nu poate avea att prima ct si a doua dimensiune neprecizata c) este corecta a) 8.25 Secventa de program void f(int x);

void main(void) { int y[10]; ...... f(y[2]); } void f(int x) { int z; z=x; ...... } a) este corecta b) este gresita, deoarece parametrul actual al functiei f() este o componenta de tablou 8.26 Secventa de program void f(int y[2]); void main(void) { int x[2]={3,4}; f(x); printf(\n x[0]=%d,x[1]=%d,x[0],x[1]); } void f(int y[2]) { y[0]=y[1]; } a) afiseaza x[0]=3,x[1]=4 b) afiseaza x[0]=4,x[1]=4 c) este gresita 8.27 n limbajul C tablourile pot fi transmise unei functii a) numai prin referinta b) numai prin valoare c) si prin valoare si prin referinta 8.28 Structurile pot fi transmise unei functii a) numai prin referinta b) numai prin valoare c) si prin valoare si prin referinta 8.29 Un membru al unei structuri poate fi transmis unei functii a) b) c) d) numai prin referinta numai prin valoare si prin valoare si prin referinta numai o data cu ntreaga structura

8.30 Declaratiile float (*p)(float,int); si float *p (float,int); a) sunt echivalente b) nu sunt echivalente: prima reprezinta un pointer catre o functie de tip float, care are un parametru de tip float si unul de tip int, iar a doua reprezinta prototipul functiei p() de tip pointer catre float, care are un parametru de tip float si unul de tip int c) sunt corecte 8.31 Secventa de program void f(int x); void main(void) { int x=7; f(x); } void f(int x) { printf(%3i,x); if(x) f(x-1); } afiseaza a) 7 6 5 4 3 2 1 0 b) 0 1 2 3 4 5 6 7 8.32 Secventa de program void f(int x); void main(void) { int x=7; f(x); } void f(int x) { if(x) f(x-1); printf(%3i,x); } afiseaza

a) b)

7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7

8.33 Secventa de program

void f(int x); void main(void) { int x=7; f(x); } void f(int x) { f(x-1); printf(%3i,x); } a) afiseaza 7 6 5 4 3 2 1 0 b) afiseaza 0 1 2 3 4 5 6 7 c) apeleaza de o infinitate de ori functia f() 8.34 Secventa de program void f(void) { float x=23.5; printf(\n x=%f,x); } void main(void) { f(); printf(\n x=%f,x); } a) afiseaza x=23.500000 b) afiseaza x=23.500000 x=23.500000 c) este gresita, deoarece n main() se utilizeaza variabila x care nu este declarata aici

RASPUNSURI 8.1-b 8.6-b 8.11-a, c, d 8.16-b 8.21-c 8.26-b 8.31-a 8.2-b 8.7-c 8.12-a 8.17-b 8.22-c 8.27-a 8.32-b 8.3-a 8.8-b 8.13-b 8.18-c 8.23-b 8.28-c 8.33-c 8.4-a 8.9-a 8.14-c 8.19-a 8.24-b 8.29-c 8.34-c 8.5-a, b, c 8.10-b, c 8.15-a, b 8.20-c 8.25-a 8.30-b, c

TESTE DE CONTROL
9.1 Locul de memorare al variabilei auto este:

a) stiva b) memoria heap


9.2 Clasa de memorare auto

a) trebuie declarata explicit ntotdeauna b) este considerata implicit pentru variabilele care sunt declarate n interiorul unei functii si care nu au specificata clasa de memorare
9.3 Apelul functiei

void f(void) { float i=1; { int i=2; printf(\n %d,i); } }


a) produce eroare, deoarece variabila i este declarata att de tip float ct si de tip int b) are ca efect afisarea valorii 2 c) are ca efect afisarea valorii 1
9.4 Secventa de program

register int x=10; int *p; p=&x;


a) este corecta, deoarece atribuie unui pointer de tip int adresa unei variabile de tip int b) este incorecta, deoarece variabilelor care au clasa de memorare register nu li se poate aplica operatorul &
9.5 Care din afirmatiile urmatoare sunt adevarate?

a) initializarea unei variabile din clasa auto sau register se face de fiecare data cnd se executa blocul n care sunt declarate b) initializarea unei variabile din clasa static se face o singura data si anume la compilare c) afirmatiile a) si b) sunt adevarate
9.6 O variabila din clasa de memorare static

a) este memorata n stiva b) este memorata n locatii cu adrese fixe

c) este recunoscuta doar n interiorul functiei unde a fost declarata d) si pastreaza valoarea de la un apel la altul al functiei
9.7 O variabila este numita globala atunci cnd

a) este declarata n afara functiei main() b) este declarata n afara oricarei functii
9.8 Variabilele globale fac parte implicit din clasa

a) b) c)

register auto

extern

9.9 Care din afirmatiile urmatoare sunt adevarate?

a) o variabila globala declarata ntr-un fisier al unui program multifisier este recunoscuta automat si n functiile celorlalte fisiere b) ntr-un program multifisier, o variabila globala trebuie declarata identic n toate fisierele programului c) pentru a fi recunoscuta de toate functiile programului multifisier, o variabila globala trebuie declarata obisnuit ntr-unul din fisiere, iar n celelalte declaratii trebuie sa fie precedata de specificatorul extern d) precedata de specificatorul static, o variabila globala este recunoscuta doar de functiile fisierului n care este declarata

RASPUNSURI

9.1-a 9.6-b, c, d

9.2-b 9.7-b

9.3-b 9.8-c

9.4-b 9.9-c, d

9.5-a, b, c

TESTE DE CONTROL
10.1 Sunt initializate ntotdeauna cu valoarea 0 a) variabilele locale si globale declarate static si neinitializate explicit b) variabilele locale neinitializate explicit 10.2 n C operatiile de intrare/iesire se realizeaza cu ajutorul a) unor instructiuni speciale b) unor functii speciale din biblioteca standard 10.3 Fluxul (streamul) este un dispozitiv logic de interfata, care asigura generalitatea functiilor de intrare/iesire fata de diversitatea dispozitivelor fizice specifice acestor operatii b) se reprezinta printr-o entitate de tip FILE c) poate fi de doua tipuri: text sau binar a) 10.4 Fisierul deschis n mod w a) va fi creat daca nu exista b) va pierde informatiile daca exista deja c) va putea fi completat cu noi informatii daca exista deja 10.5 Fisierul deschis n mod a a) se creeaza daca nu exista b) permite adaugarea de date la sfrsitul sau daca exista deja c) va pierde informatiile daca exista deja 10.6 Pentru citirea unui caracter dintr-un fisier de pe disc se poate folosi functia

a) b) c)
si

fgetc() getch() getc()

10.7 Apelurile de functii fgetc(stdin); getch(); a) au acelasi efect: ambele citesc un caracter de la tastatura b) au efecte diferite 10.8 Pentru scrierea unui caracter ntr-un fisier pe disc se poate folosi functia

a) b) c)

fputc() putc() putch()

10.9 Apelurile de functii fputc(stdout,nume_caracter); si putch(nume_caracter); a) au acelasi efect: ambele afiseaza un caracter pe monitor b) au efecte diferite 10.10 Pentru citirea unui sir de caractere dintr-un fisier creat pe disc se poate folosi functia

a) b) a) b)
si

fgets() gets()

10.11 Pentru scrierea unui sir de caractere ntr-un fisier creat pe disc se poate folosi functia puts() fputs()

10.12 Apelurile de functii fgets(nume_sir,nr_caractere,stdin); gets(nume_sir); a) au acelasi efect: ambele citesc un sir de caractere de la tastatura b) au efecte diferite 10.13 Functiile fprintf() si fscanf() a) transfera blocuri de date b) transfera date formatate 10.14 Care afirmatie de mai jos este adevarata? a) apelul fscanf(stdin, ...) are acelasi efect cu apelul scanf() b) apelul fprintf(stdout, ...) are acelasi efect cu apelul printf() 10.15 Pentru transferul unui volum mare de date este recomandabila folosirea functiilor a) fread() si fwrite() b) fscanf() si fprintf() 10.16 Functia feof() a) ntoarce valoarea adevarat daca nu s-a atins sfrsitul fisierului si fals n caz contrar b) ntorce valoarea fals daca nu s-a atins sfrsitul fisierului si adevarat n caz contrar 10.17 Functia rewind() pozitioneaza indicatorul de pozitie al fisierului a) la nceputul sau b) la sfrsitul sau 10.18 Functia fseek() pozitioneaza indicatorul de pozitie al fisierului la o valoare calculata relativ fata de una din valorile de referinta: nceputul fisierului, sfrsitul fisierului, pozitia curenta n fisier b) seteaza valoarea indicatorului de pozitie la o valoare data c) memoreaza valoarea indicatorului de pozitie ntr-un obiect din memorie a) 10.19 Care afirmatii sunt adevarate? a) functia fgetpos() seteaza valoarea indicatorului de pozitie la o valoare data b) functia fsetpos() memoreaza valoarea indicatorului de pozitie ntr-un obiect din memorie c) afirmatiile 1 si 2 sunt false

d) functia ftell() ntoarce valoarea curenta a indicatorului de pozitie

RASPUNSURI

10.1-a 10.6-a, c 10.11-b 10.16-b

10.2-b 10.7-a 10.12-a 10.17-a

10.3-a, b, c 10.8-a, b 10.13-b 10.18-a

10.4-a, b 10.9-a 10.14-a, b 10.19-c, d

10.5-a, b 10.10-a 10.15-a

TESTE DE CONTROL
11.1 Directiva #define N 10 a) este gresita, deoarece nu se termina cu semnul ; b) este corecta si are ca efect atribuirea valorii 10 identificatorului N c) este corecta si are ca efect nlocuirea, oriunde apare n program, a identificatorului 11.2 Secventa de program #define A 10 #define B A+20 este gresita deoarece cea de-a doua directiva #define contine macroul A din prima directiva #define b) este corecta si are ca efect nlocuirea n textul programului a identificatorului B cu textul 10+20 a) 11.3 Secventa de program #define X 10 #define Y X+5 ...... int v; v=X+3*Y; ...... este gresita deoarece cea de-a doua directiva #define contine macroul X din prima directiva #define b) este corecta, iar variabilei v i se atribuie valoarea 45 c) este corecta, iar variabilei v i se atribuie valoarea 55 a) 11.4 Pentru a anula efectul directivei #define M 20 se foloseste

N cu 10

a) b) c)

#define #define M #undef

11.5 Directiva #define f(x) 3*x ...... int y; y=f(4-1); are ca efect a) atribuirea valorii 9 variabilei y b) atribuirea valorii 11 variabilei y c) semnalarea unei erori, deoarece f(x) este folosita ca o functie 11.6 Folosirea unei macrodefinitii n locul unei functii a) reduce timpul de executie al programului b) micsoreaza codul programului

11.7 Directiva #include permite a) includerea unui fisier C ntr-alt fisier C b) includerea unui fisier antet ntr-un fisier C c) includerea unui fisier antet ntr-un fisier antet 11.8 Directiva #include se poate folosi a) numai sub forma #include <nume_fisier> b) numai sub forma #include nume_fisier c) att sub forma 1 ct si sub forma 2 11.9 Un fisier antet a) b) c) d) e) poate contine prototipuri de functii poate contine definitii de functii poate contine definitii de constante poate contine definitii de date sau constante structurate poate contine directive #define si #include

11.10 Directiva #if a) permite executia conditionata a unei sectiuni de program b) permite compilarea conditionata a unei sectiuni de program c) permite compilarea unei sectiuni de program functie de valoarea unei expresii constante 11.11 Directiva #ifdef a) permite compilarea conditionata a unei sectiuni de program daca o expresie e adevarata b) permite compilarea unei sectiuni de program conditionata de definirea unui macro c) este echivalenta cu #if defined nume si cu #if defined(nume) unde nume reprezinta numele unui macro.

RASPUNSURI 11.1-c 11.6-a 11.11-b, c 11.2-b 11.7-b, c 11.3-b 11.8-c 11.4-c 11.9-a, c, e 11.5-b 11.10-b, c

1. Generalitati despre C
Limbajul C a fost inventat si implementat prima data n anii 70 de catre Dennis Ritchie, programator de sistem la Bell Laboratories. El si are originea n limbajul BCPL (Basic Computer Programming Language) care, prin perfectionari si dezvoltari succesive a devenit limbajul B si n final limbajul C. Raspndirea initiala a limbajului C se datoreaza folosirii sale n scrierea sistemului de operare UNIX, sistem care cunoaste astazi o ascensiune constanta printre sistemele de operare existente. Una din versiunile remarcabile ale limbajului C este cea furnizata mpreuna cu versiunea a 5 a sistemului de operare UNIX. Aceasta versiune este descrisa prima oara n cartea lui Brian Kernighan si Dennis Ritchie intitulata The C Programming Language. Cartea este cunoscuta ca un punct de referinta n evolutia limbajului, fiind asimilata cu un adevarat standard. Aparitia microcalculatoarelor a contribuit la raspndirea spectaculoasa a limbajului C n diverse variante. Desi diferenta dintre ele nu a fost niciodata semnificativa, totusi, pentru eliminarea anumitor neconcordante, n anul 1983 o comisie speciala ncepe lucrul pentru elaborarea standardului ANSI (American National Standards Institute) al limbajului C, care apare 6 ani mai trziu, n anul 1989. La ora actuala majoritatea compilatoarelor C sunt compatibile cu acest standard. n primele 12 capitole ale lucrarii se prezinta limbajul C standard. Datorita importantei domeniului s-a considerat utila si o initiere, n capitolul al 13-lea, n grafica pe calculator, folosind functiile video Borland C++.

LOCUL LIMBAJULUI C N FAMILIA LIMBAJELOR DE PROGRAMARE


Familia limbajelor de programare se poate clasifica n: limbaje de nivel cobort, limbaje de nivel nalt si limbaje de nivel mediu. Prima categorie cuprinde limbajul cod-masina si limbajul de asamblare. Ambele sunt specifice tipului de masina de calcul pe care sunt implementate. Limbajul cod masina este limbajul alcatuit din acele instructiuni elementare care sunt ntelese si executate de un anumit tip de calculator. L imbajul de asamblare foloseste n locul codurilor numerice reprezentari simbolice, numite si mnemonice, care usureaza munca de programare. Operatiile limbajului de asamblare sunt operatii de baza ale calculatorului. El nu accepta structuri de control si date structurate, dar permite adresarea simbolica a locatiilor de memorie. Din aceste motive programele n limbaj de asamblare sunt lungi si se scriu anevoios, dar sunt performante din punct de vedere al vitezei de executie si al posibilitatilor de acces la resursele hardware. A doua categorie, cea a limbajelor de nivel nalt , include nume binecunoscute: Fortran, Cobol, Basic, Pascal etc.. O parte din trasaturile lor comune se refera la posibilitatea de a folosi structuri de control, date structurate, de a elabora cu usurinta programe portabile (care se pot adapta usor la implementarea pe diverse categorii de sisteme de calcul). Limbajele din aceasta categorie pierd nsa calitatea esentiala a limbajelor de nivel cobort, aceea de a exploata eficient resursele masinii de calcul pe care sunt implementate. Categoria limbajelor de nivel mediu mbina trasaturile principale ale limbajelor de nivel nalt cu cele ale limbajelor de nivel cobort. Limbajul C este un limbaj de nivel mediu.

CTEVA TRASATURI ALE LIMBAJULUI C


Limbajul C ofera posibilitatea organizarii programelor n module si permite implementarea unor structuri de control si tipuri de date care faciliteaza programarea structurata. Ca si limbajele de nivel nalt este usor de nvatat si de folosit, iar n plus are un numar foarte mic de cuvinte cheie

(32 cuvinte dupa standardul ANSI C). Portabilitatea specifica limbajelor de nivel nalt este accentuata n C prin folosirea functiilor de biblioteca n realizarea operatiilor de intrare/iesire si de prelucrare a fisierelor. Numarul mic de cuvinte cheie si prezenta unei bogate familii de operatori permit realizarea unor programe concise, cu un cod sursa relativ mic. Compilatorul C este mai putin sever n comparatie cu majoritatea compilatoarelor limbajelor de nivel nalt. Daca la aceste trasaturi adaugam si posibilitatea de a oferi facilitati ale limbajelor de nivel cobort (lucru cu adrese de memorie, accesarea registrilor, incrementari, decrementari, apelul unor functii ale sistemului de operare) obtinem imaginea unui limbaj puternic si flexibil preferat n special de programatorii profesionisti.

STRUCTURA GENERALA A UNUI PROGRAM C


Structura generala a unui program C este urmatoarea: directive preprocesor declaratii globale tip main(lista de parametri) { declaratii locale instructiuni } tip f1(lista de parametri) { declaratii locale instructiuni } tip f2(lista de parametri) { declaratii locale instructiuni } ..... tip fn(lista de parametri) { declaratii locale instructiuni } Precizari: Faza de analiza a unei probleme evidentiaza uzual o functie principala si mai multe functii secundare ale acesteia. Rezultatul acestei faze l constituie o reprezentare modulara, care reflecta interdependenta dintre functiile problemei. n principiu, orice program C este o secventa de functii aflate la acelasi nivel. ?? Cuvintele main, f1,f2,,fn sunt nume de functii C. Orice functie dintr-un program poate apela oricare alta functie din program. Exceptie face functia main() care poate apela alte functii, dar nu poate fi apelata dintr-o functie a programului. La executia unui program C prima functie apelata este main(): executia programului ncepe si se termina cu instructiuni din main(). Ea este obligatorie si poate figura oriunde n program. De obicei este plasata la nceputul programului, pentru a-i mari lizibilitatea.
??

Directivele preprocesor sunt instructiuni destinate compilatorului, care face transformari preliminare asupra textului nainte de a ncepe compilarea. Faza preprocesarii are drept rezultat obtinerea unei unitati de compilare. Unitatea de compilare este analizata sintactic (compilata), iar rezultatul este depus n module obiect. Modulele obiect rezultate, mpreuna cu modulele obiect corespunzatoare functiilor de biblioteca folosite sunt >legate> cu ajutorul programului linkeditor (editor de legaturi) obtinndu-se programul executabil. Directivele se constituie ca un limbaj n interiorul limbajului C si ofera anumite facilitati: compilarea conditionata a unor portiuni de cod, nlocuirea n text a unui identificator la fiecare aparitie cu un set de caractere, includerea unui fisier sursa n program etc..
??

O functie C este alcatuita din antet si un bloc de declaratii si instructiuni delimitat de acoladele A si , numit si corpul functiei. Antetul contine numele functiei, tipul valorii returnate (ntreg, real etc.) si o lista de parametri formali care poate fi eventual vida. O functie este definita daca este prezentata complet, adica are forma: antet { corpul functiei } Daca se prezinta doar antetul functiei, se spune ca functia este declarata. Declaratia moderna a unei functii poarta numele de prototip. Apelul functiei se face sub forma nume_functie(lista de parametri actuali) si presupune transferul controlului executiei programului catre instructiunile din corpul functiei, executia acestora si revenirea n functia apelanta n punctul unde se executa operatia care urmeaza. Schema de apel este urmatoarea: Functie apelanta Functie apelata

Apel functie

Figura 1.1 - Schema de apel a unei functii O functie C nu poate fi declarata sau definita n interiorul altei functii. Declaratiile globale se refera la entitati (tipuri de date, variabile etc.) care sunt recunoscute de toate functiile. Declaratiile locale limiteaza valabilitatea acestor entitati doar la nivelul functiei unde se fac. Prezenta prototipului unei functii n zona declaratiilor globale face posibila recunosterea sa n toate functiile care o apeleaza si permite evitarea unor erori de apelare, nca din faza de compilare. n constructia unui program C se pot folosi doua categorii de functii: ?? functii utilizator, elaborate de programator; ?? functii standard (predefinite), care pot fi preluate din biblioteca standard a limbajului C.

Functiile standard ale limbajului se pot clasifica n: functii de intrare/iesire, functii pentru prelucrarea caracterelor, functii pentru prelucrarea sirurilor de caractere etc.. n mod corespunzator prototipurile acestor functii sunt grupate n fisiere speciale numite fisiere antet sau header (au extensia <.h>). De exemplu, functiile matematice sunt grupate n fisierul antet math.h, functiile de manipulare a sirurilor de caractere n string.h etc.. Pentru a utiliza o functie standard n program trebuie cunoscut prototipul ei. Acest lucru este posibil prin includerea fisierului antet n program utiliznd directiva #include. Un program C poate avea functiile editate ntr-un singur fisier (programe monofisier) sau n mai multe fisiere (programe multifisier). Modul de construire a programelor multifisier este abordat n Capitolul 9. Un exemplu simplu de program C este urmatorul:

Primul program C #include stdio.h void main(void) { printf(\nPrimul program C!); }

Se observa ca programul este alcatuit dintr-o singura functie, functia main() si foloseste functia standard printf() cu prototipul n stdio.h.

2. Elemente de baza ale limbajului C


Asa cum se ntmpla cu orice limbaj artificial, temelia pe care se cladesc programele C este alcatuita din alfabet si vocabular (atomi lexicali). Combinnd atomii lexicali dupa regulile specifice de sintaxa se construiesc linii valide de program si, n final, programul.

ALFABETUL LIMBAJULUI
Alfabetul limbajului este alcatuit dintr-o multime de simboluri care se pot clasifica n simboluri afisabile si neafisabile. Setul minim de simboluri afisabile (care pot fi reprezentate grafic) este alcatuit din:
??

litere mari ale alfabetului englez: ABCDEFGHIJKLMNOPQRSTUVWXYZ litere mici ale alfabetului englez: abcdefghijklmnopqrstuvwxyz

??

?? ?? ??

cifre zecimale: 0 1 2 3 4 5 6 7 8 9 liniuta de subliniere: _ semne de punctuatie si semne speciale: , . ; : ? ( ) [ ] < > ! |\/~#& ^ *-=+{}% Prezentam n Tabelul 2.1 denumirea unora din semnele enumerate. Tabelul 2.1 Cteva semne folosite n C si denumirea lor Semn | \ # & Nume bara verticala backslash diez ampersand Semn / ~ _ ^ Nume slash tilda liniuta de subliniere (underscore) sageata sus

Simbolurile neafisabile (fara echivalent grafic) sunt reprezentate prin secvente escape (de evitare) sau coduri backslash-caracter. Aceste simboluri reprezint[ coduri ASCII care nu pot fi citite de la tastatura. Folosirea lor n programe n locul echivalentelor ASCII este recomandata din ratiuni de portabilitate. Codurile backslash ale limbajului C sunt prezentate n Tabelul 2.2.

Tabelul 2.2 Codurile backslash din C =i semnifica\ia lor Coduri backslash \a \b \f \n \r \t \v \ \ \0 \\ \ddd \xdd Semnificatie Alarma backspace (recul cu o pozitie) form feed (salt pagina noua) newline (salt la rnd nou si de la capat) carriage return (retur car) horizontal tab (tab orizontal) vertical tab (tab vertical) apostrof ghilimele caracter nul backslash caracter ASCII n notatie octala caracter ASCII n notatie hexazecimala

Observatii: Tab orizontal nseamna saltul cursorului cu un numar de coloane, iar tab vertical saltul cursorului cu un numar de linii.
?? ??

Notatia octala foloseste cifre n baza 8 (adica 0,1,2,3,4,5,6,7), iar notatia hexazecimala cifre n baza 16 (adica 0,1,2,3,4,5, 6,7,8,9,A,B,C,D,E,F). Se observa ca literele A,B,C,D,E,F corespund respectiv numerelor 10,11,12,13,14,15. Secventele \ddd permit scrierea oricarui caracter din setul ASCII ca un numar octal format din trei cifre, iar secven\ele \xdd ca un numar hexazecimal format din doua cifre. De exemplu, caracterul backspace poate fi scris ca \010 sau \x08.
??

VOCABULARUL LIMBAJULUI
Vocabularul limbajului este alcatuit din atomi lexicali. Acestia reprezinta grupuri de simboluri afisabile care primesc n timpul procesului de compilare o anumita semnificatie. Prezentam mai jos urmatorii atomi lexicali: ?? identificatori (nume) ?? constante ?? operatori ?? semne de punctuatie ?? simboluri speciale IDENTIFICATORI Un identificator reprezinta o secventa de litere, cifre, liniute de subliniere, primul caracter din secventa fiind obligatoriu o litera sau liniuta de subliniere. De exemplu, Cod_mat,cod_mat,y_1,ax,_ol sunt identificatori, n timp ce x...1, a&b, 3xy nu sunt. n legatura cu identificatorii facem urmatoarele precizari: ?? n C se face deosebirea ntre literele mari si mici ale alfabetului. De exemplu, Cod_mat si cod_mat reprezinta nume diferite; ?? Desi sunt permisi, este recomandabil ca identificatorii care ncep cu liniuta de subliniere sa fie evitati. Ei pot coincide cu nume rezervate, invizibile programatorului, provocnd erori; ?? Standardul ANSI C nu limiteaza numarul de caractere (lungimea) unui identificator. Un compilator C va ignora nsa caracterele aflate pe pozitii mai mari dect un numar prestabilit.

Cuvintele cheie sunt cuvinte rezervate C care au o destinatie prestabilita (nu pot fi folosite ca nume de functie sau variabila). Standardul ANSI C are 32 de cuvinte cheie, din care 27 au fost definite de varianta originala a limbajului C (standardul Kernighan/Ritchie). Tabelul 2.3 Cuvinte cheie dupa standardul Kernighan / Ritchie auto default float long static unsigned break do for register struct while case double goto return switch char else if short typedef continue extern Int Sizeof Union

Tabelul 2.4 Cuvinte cheie adaugate de standardul ANSI C const enum signed Void volatile

Dupa cum se poate observa cuvintele cheie din C se scriu cu litere mici. Pe lnga cuvintele cheie rezervate de standardul ANSI C, diverse tipuri de compilatoare C includ si cuvinte cheie folosite n exploatarea eficienta a mediului de operare specific (facilitati privind programarea interlimbaje, accesarea ntreruperilor etc.), numite cuvinte cheie extinse. Cuvintele cheie extinse folosite cel mai des sunt: asm pascal cdecl _cs far _ds huge _es interrupt _ss Near

CONSTANTE Constantele pot fi numere, caractere, siruri de caractere; valoarea lor nu se schimba n timpul executiei unui program. n C exista patru tipuri de constante: ntreg, real, caracter, sir. O constanta ntreaga este un numar zecimal, octal sau hexazecimal care reprezinta o valoare ntreaga pozitiva. Daca se doresc si reprezentari ale unor numere ntregi negative se adauga semnul minus n fata constantei respective. Constantele ntregi zecimale sunt numere ntregi scrise n baza 10 (de exemplu: 759,+38,6496), constantele octale sunt numere n baza 8 care ncep, pentru identificare, cu cifra zero (de exemplu: 012,0765), iar constantele hexazecimale sunt numere n baza 16 care ncep pentru identificare cu caracterele 0x (de exemplu: 0xA3,0xBC1,0x7E31). Constantele reale sunt numere reale pozitive. Pentru a reprezenta valori reale negative se plaseaza semnul minus n fata constantei. Semnul minus este tratat ca operator aritmetic. Exista doua modalitati de reprezentare o constantelor reale: n format F (cu punct zecimal) si n format exponential (forma stiintifica). Reprezentarea n format F este reprezentarea uzuala pentru numere reale. De exemplu, constantele 32.753,0.591,-4296.823, .69 sunt valori reale reprezentate n format F. Se observa ca partea ntreaga a reprezentarii poate sa lipseasca atunci cnd este egala cu zero (.69 este tot una cu 0.69). O constanta reala n format exponential are forma generala: numar simbexp valexp unde: ?? numar este o constanta ntreaga sau o constanta reala n format F ?? simbexp este E sau e ?? valexp este o constanta ntreaga pozitiva precedata sau nu de semnele + sau?? grupul simbexp valexp se interpreteaza ca fiind egal cu 10valexp.

Remarca. n realitate ntre numar, simbexp, valexp nu apar spatii. De exemplu, numerele reale: 1.6*103, -2.6*10-4, 0.32*106, 423*104 se scriu, respectiv: 1.6E+3,-2.6E-4, 0.32e+6,423E+04. O constanta caracter este o litera, cifra, semn de punctuatie sau secventa escape cuprinse ntre doua apostrofuri. Exemplu de constante caracter: a,\n,\,7 reprezentnd respectiv a,newline, apostrof,7. O constanta sir este o secventa de litere, cifre si simboluri incluse ntre ghilimele. Exemple de constante sir: Acesta e un sir, Str. Cameliei, nr.3 etc.. Constantele sir se \n memoreaza n octeti consecutivi (un octet pentru fiecare caracter). Sfrsitul sirului este marcat de un octet nul (care contine \0) ce se adauga automat. Din acest motiv un sir cu n caractere ocupa n+1 octeti consecutivi.

OPERATORI
Operatorii reprezinta combinatii de semne speciale care arata modalitatea de prelucrare sau atribuire a valorilor. Limbajul C poseda o bogata familie de operatori, fapt ce permite elaborarea unor programe compacte. n Tabelul 2.5 prezentam lista operatorilor C si semnificatia lor. Tabelul 2.5 Lista operatorilor C si semnificatia lor Operator [] () . ? ++ -sizeof ~ ! & * / + << >> == != < > <= >= ^ ? && || ?: = op= % , Semnificatie paranteze drepte (stnga si dreapta) paranteze rotunde (stnga si dreapta) membru structura referinta indirecta la membru de structura incrementare (prefix sau postfix) Decrementare (prefix sau postfix) dimensiunea unei variabile sau unui tip n octeti NOT pe bit NOT logic adresa, SI pe bit indirectare, nmultire mpartire Adunare scadere, negatie aritmetica deplasare logica pe bit la stnga deplasare logica pe bit la dreapta Egalitate Neegalitate mai mic mai mare mai mic sau egal cu mai mare sau egal cu SAU exclusiv pe bit SAU pe bit SI logic SAU logic operator conditional atribuire simpla atribuire compusa modul virgula (operator de secventiere)

Observatie. Caracterul op de la atribuirea compusa poate fi unul din semnele * / % + - >> << ?& ^ rezultnd corespunzator operatorii compusi *= /= %= += -= >>= <<= ?= &= ^=. ntre semnele care alcatuiesc un operator compus nu trebuie sa existe spatiu. De exemplu, operatorul >= nu poate fi utilizat sub forma > = . De la regula de mai sus face exceptie operatorul conditional (?:) care are alta interpretare. SEMNE DE PUNCTUATIE Semnele de punctuatie folosite n C sunt: ... # : {} Utilizarea lor va fi exemplificata pe parcursul lucrarii. SIMBOLURI SPECIALE Orice simbol care nu apartine alfabetului C este considerat atom lexical. Astfel de semne sunt: @ =i $. Ele pot fi folosite n constructia constantelor caracter si a sirurilor. Ca exemplu, consideram constanta sir Am folosit semnul @ in FoxPro. Atomii lexicali sunt separati n cadrul programului prin simboluri ale alfabetului cu rol de separator. Separatorii sunt: spatiul, tabul (orizontal si vertical), sfrsit de linie, sfrsit de pagina, comentariu. Prin comentariu se ntelege orice succesiune de simboluri cuprinse ntre /* si */. Comentariile nu sunt luate n considerare de compilator, ele servesc la documentarea programului usurnd ntelegerea si depanarea lui. Un comentariu poate fi plasat oriunde n program si se poate ntinde pe unul sau mai multe rnduri. Exemplu: /* Acest program calculeaza produsul a doua matrici. Matricile de intrare A(4x3) si B(3x2) se citesc de la tastatura, iar matricea produs rezultata este C(4x2). */ Comentariile nu pot fi imbricate (incluse unul n altul). De exemplu, urmatoarea constructie este eronata: /* Un comentariu care include /* un comentariu inclus */ */ Modul n care sunt aranjati atomii lexicali ntr-un program este impus de specificul problemei si de regulile de sintaxa ale limbajului.

3. Expresii n C. Functii de intrare/iesire uzuale pentru consola


Expresiile sunt combinatii valide sintactic de date si operatori. Aici, prin date, ntelegem deopotriva constante si variabile. Spre deosebire de constante care sunt valori fixe, variabilele semnifica valori care se pot modifica prin program. n C, ca si n alte limbaje, datele sunt clasificate n tipuri de date. Exista tipuri de date fundamentale (numite si predefinite, simple sau de baza) si tipuri de date derivate. Tipurile derivate (tablouri, pointeri, structuri, uniuni, enumerari si orice tip definit de programator) se bazeaza pe tipurile fundamentale.

TIPURILE FUNDAMENTALE DE DATE IN C Tipurile de date fundamentale din C se mpart n cinci categorii: char, int, float, double si void. Primele patru tipuri se mai numesc si tipuri aritmetice si se refera respectiv la valori caracter, ntregi, reale n simpla precizie si reale n dubla precizie. Tipul de date void indica absenta oricarei valori si este utilizat, de exemplu, la descrierea functiilor care nu returneaza nici o valoare. Dimensiunea zonei de memorie alocate si domeniul de valori asociate tipurilor aritmetice pot sa difere functie de varianta de implementare a limbajului si de tipul de procesor folosit. Standardul ANSI C nu precizeaza dect domeniul minimal de valori al fiecarui tip de date, nu si dimensiunea sa. n majoritatea implementarilor nsa, tipul char ocupa un octet, int ocupa doi octeti, iar float patru octeti. Domeniul de valori poate fi modificat utiliznd modificatorii de tip. Acestia sunt: signed, unsigned, short si long. Modificatorii signed, unsigned, short si long se pot aplica tipului int, signed si unsigned, tipului char, iar long, tipului double. Efectul aplicarii modificatorilor signed sau unsigned asupra tipurilor de date ntregi consta n interpretarea diferita, din punct de vedere al semnului, a informatiei memorate. Sa consideram o configuratie binara de lungime N, bitii fiind numerotati ca mai jos: N-1 N-2 ^ 1 0

Figura 3.1 Configuratie binara de N biti Daca aceasta zona o destinam memorarii doar a ntregilor pozitivi, printr-un calcul simplu se poate vedea ca plaja de reprezentare este [0, 2N-1]. Daca zona este destinata memorarii att a ntregilor cu semn ct si fara semn, bitul N-1 va fi folosit pentru reprezentarea semnului (0 pentru numere pozitive, 1 pentru numere negative), iar plaja de reprezentare va fi [-2N-1, 2N-1-1]. Avnd n vedere aceste consideratii , de exemplu, o variabila de tip signed int va avea un domeniu de valori cuprins ntre -32768 =i 32767, iar una de tip unsigned int va lua valori ntre 0 =i 65535. Observatii: Pentru tipul ntreg de date (char, int, short, long) reprezentarea implicita este signed. Specificarea unui modificator fara tip nseamna considerarea implicita a tipului int. n C nu exista tipul de date boolean. Din acest motiv functioneaza urmatoarea conventie: orice expresie diferita de zero are valoarea adevarat, iar daca e egala cu zero, valoarea fals.
?? ?? ??

VARIABILE SI TIPURI DE DATE Asocierea dintre numele unei variabile si un anumit tip de date se face folosind declaratiile. Forma generala a declaratiei unei variabile este: tip lista_de_variabile; unde: tip poate fi orice tip de date recunoscut n C, iar lista_de_variabile contine unul sau mai multi identificatori despartiti prin virgula. n exemplele de mai jos vom folosi doar tipurile fundamentale. Exemple de declaratii de variabile: float x,y; int a,b1; short a_x,b_y; double z; Orice variabila folosita n program trebuie mai nti declarata. Daca pe linia de declarare variabila este initializata se spune ca are loc o definire a variabilei. Exemple de declaratii si definitii de variabile: float x=38.981,I; int ab=-453; char ch=A,z; Orice variabila definita (adica declarata si initializata) pastreaza n continuare atributul de baza al variabilei, adica poate fi modificata. Daca se doreste <nghetarea> asocierii dintre o variabila si o anumita valoare se utilizeaza modificatorul de acces (sau calificatorul) const. Practic, efectul unei declaratii de genul const tip nume_variabila; este crearea constantei simbolice nume care poate fi utilizata, dar nu poate fi modificata prin program. Daca tip lipseste se considera implicit ca tipul este int. n exemplul de mai jos se definesc doua constante, constanta pi si constanta de tip int, ore_zi. Exemplu: const double pi=3.1415926536; const ore_zi=24;

CONSTANTE SI TIPURI DE DATE n Capitolul 2 (vezi paragraful Vocabularul limbajului), am prezentat o clasificare a constantelor n: ntregi, reale, caracter, sir. Se pune problema, carui tip de date i apartine o constanta numerica? Cnd constanta este caracter, raspunsul este simplu: tipului char. De asemenea, constanta n virgula mobila (n notatie uzuala cu punct sau n notatie stiintifica) va apartine tipului double. Pentru celelalte constante numerice compilatorul va considera implicit ncadrarea ]n cel mai mic tip de date compatibil. De exemplu, 23 este de tip int, 65000 de tip unsigned, 2000002 de tip long int. ncadrarea ntr-un tip de date se poate face si explicit adaugnd constantei unul din sufixurile L sau U, daca e ntreaga sau F sau L, daca e reala. Constanta ntreaga cu sufixul L este de tip long, iar cu sufixul U, de tip unsigned. Constanta reala cu sufixul F are tipul float, iar daca e urmata de sufixul L are tipul long double.

11

FUNCTII UZUALE DE INTRARE/IESIRE PENTRU CONSOLA. DESCRIPTORI DE FORMAT Prezentam n continuare functiile folosite frecvent pentru transferul de date de la tastatura n memoria calculatorului (functii de intrare) si din memoria calculatorului pe ecran (functii de iesire); cu aceasta ocazie introducem si descriptorii de format cei mai folositi. Deosebim trei categorii de functii de intrare/iesire pentru consola:
?? ??

functii generale de intrare/iesire (scanf() =i printf()); functii speciale de intrare/iesire: ?? functii pentru citirea si scrierea caracterelor; ?? functii pentru citirea si scrierea sirurilor de caractere.

FUNCTIILE PRINTF() SI SCANF() Aceste functii reprezinta echivalentele pentru consola a functiilor de intrare/iesire pentru fisiere, fprintf() si fscanf(), functii care vor fi prezentate n detaliu n Capitolul 10. Forma generala a functiei de afisare printf() este: int printf(sir_format,lista_de_argumente); unde:
?? ??

sir_format poate contine: mesaje pentru utilizator, secvente escape si descriptori de format pentru valorile care se afiseaza; lista_de_argumente reprezinta variabile sau expresii al caror continut se va afisa.

Functia ntoarce numarul de caractere scrise efectiv sau o valoare negativa n caz de insucces. Precizari: Descriptorii de format servesc la efectuarea conversiilor dintre reprezentarea externa si interna (la citire) si ntre reprezentarea interna si externa (la scriere); formatul extern presupune succesiuni de caractere, iar cel intern succesiuni de cifre binare; ?? Att la scriere ct si la citire, descriptorii de format sunt pusi n corespondenta de la stnga spre dreapta cu elementele listei de argumente. Argumentul trebuie sa fie compatibil cu tipul anuntat n descriptorul de format corespunzator; ?? Daca numarul de argumente este mai mic dect numarul descriptorilor de format, datele de iesire sunt nedefinite; daca numarul argumentelor este mai mare dect numarul descriptorilor de format, argumentele n plus sunt ignorate. n Tabelul 3.1 prezentam lista celor mai utilizati descriptori folositi de functia printf() si semnificatia lor.
??

Tabelul 3.1 Descriptori de format Descriptori %u %d sau %i %c %f %e sau % E %x sau %X %o %s %g sau %G

Utilizare numere ntregi zecimale fara semn numere ntregi zecimale cu semn caracter numere reale n notatie uzuala numere reale n notatie stiintifica (e sau E) hexazecimal fara semn (litere mici sau majuscule) octal fara semn sir de caractere se alege reprezentarea cu numarul cel mai mic de caractere dintre cea n notatie uzuala si cea n notatie stiintifica (de tip e sau E) valoare pointer

%p

12

Descriptorul %x are ca efect afisarea cifrelor hexazecimale A,B,C,D,E,F cu litera mica; daca se foloseste %X se afiseaza cu litere mari. Daca se foloseste %e litera e din notatia stiintifica apare ca e, iar daca se foloseste %E, apare ca majuscula (litera E). Valorile de tip long int se afiseaza utiliznd %ld,%li,%lu,%lo sau %lx. Valorile de tip short int se afiseaza utiliznd %hd,%hi,%hu,%ho sau %hx. Pentru a afisa valori double se va alege una din variantele: %lf,%le,%lE,%lg,%lG, iar pentru valori long double una din variantele: %Lf,%Le,%LE,%Lg,%LG. Tabelul 3.1 prezinta descriptori de format fara caracteristici de lungime, precizie si aliniere. Folositi astfel, ei aliniaza implicit valorile la stnga si folosesc spatiu de afisare necesar reprezentarii acestor valori dupa cum urmeaza: %f afiseaza implicit partea ntreaga, punctul si 6 cifre la partea subunitara; %e sau %E afiseaza implicit o cifra la partea ntreaga, 6 cifre la partea subunitara, caracterul e sau E si exponentul precedat de + sau -; %g sau %G alege reprezentarea cu cel mai mic numar de caractere dintre cea uzuala si cea stiintifica. %d,%i,%c,%o,%x,%X,%s,%p folosesc un numar de coloane egal cu numarul de caractere ce trebuie afisate. Exemplificam folosirea descriptorilor prezentati si a secventelor de evitare cu ajutorul urmatoarelor programe:

Afisarea valorilor si sumei a doua numere ntregi, sub forma: x=valoare y=valoare suma=valoare # include "stdio.h" void main(void) { int x=10, y=-43; printf ("\n\tx=%d\t\y=%d\n\t }

suma=%i", x,y, x+y);

Afisarea unei constante ntregi si a valorilor sale n octal si hexazecimal pe cte un rnd. #include "stdio.h" void main(void) { const x=4529; printf("\n numarul este=%d\n",x); printf("\n valoarea in octal este=%o",x); printf("\n valoarea in hexazecimal este=%x",x); }

13

Afisarea unui caracter si a codului sau ASCII; afisarea se va termina cu un semnal sonor. #include "stdio.h" void main(void) { char a='Q'; printf("\n caracterul %c are codul ASCII=%i\a",a,a); }

Afisarea unor valori folosind diversi descriptori de format; comentariile arata efectul executiei functiei printf(). #include "stdio.h" void main(void) { char ch; short k; int i; long int j; float x; clrscr(); ch='A'; printf("\n Caracterul %c are codul ASCII = %i",ch,ch); /* Caracterul A are codul ASCII = 65 */ k=250; printf("\n k=%hu",k);

/* k=250 */

i=4567; printf("\n i=%i",i); /* i=4567 */ printf("\n i=%u",i); /* i=4567 */ printf("\n -i=%i",-i); /* -i=-4567 */ printf("\n i=%i",i); /* i=4567 */ printf(" are valoarea hexazecimala %x",i); /* are valoarea hexazecimala 11d7 */ printf(" sau echivalent, %X",i); /* sau echivalent, 11D7 */ printf("\n i=%i",i); /* i=4567 */ printf(" are valoarea octala %o",i); /* are valoarea octala 10727 */ j=123456; printf("\n j=%li",j); /* j=123456 */

14

x=76.5432; printf("\n printf("\n printf("\n printf("\n printf("\n

x=%f",x); x=%e",x); x=%E",x); x=%g",x); x=%G",x);

/* /* /* /* /*

x=76.543198 x=7.65320e+01 x=7.65320E+01 x=76.543200 x=76.543200

*/ */ */ */ */

x=-0.123456789; printf("\n x=%f",x); printf("\n x=%e",x); printf("\n x=%E",x); printf("\n x=%g",x); printf("\n x=%G",x);

/* /* /* /* /*

x=-0.123457 x=-1.234568e-01 x=-1.234568E-01 x=-0.123457 x=-0.123457

*/ */ */ */ */

printf("\n %s","testare"); }

Dimensiunea cmpului de afisare, precizia si modul de aliniere pot fi stabilite prin simboluri plasate ntre semnul % si specificatorul descriptorului de format (i,d,u,f etc.). Astfel, un ntreg pozitiv aflat ntre % si specificatorul descriptorului de format indica dimensiunea minima a cmpului de afisare. Daca sirul sau numarul care se afiseaza are mai multe caractere dect dimensiunea minima precizata, atunci afisarea va fi integrala, n caz contrar se completeaza cu spatii pna la realizarea dimensiunii minime. Daca dorim ca aceasta completare sa fie facuta cu cifra 0 n loc de spatii, atunci, nainte de ntregul care specifica dimensiunea de afisare, se pune cifra 0. De exemplu, descriptorul %7f semnifica afisarea unui numar real pe minim 7 coloane si, completarea eventualelor coloane libere cu spatii, iar %07f impune acelasi numar minim de coloane pentru afisare, nsa completarea coloanelor libere se va face cu cifra 0. Utilitatea precizarii dimensiunii minime de afisare apare mai ales la afisarea tablourilor n care alinierea se face pe coloane. Programul urmator ilustreaza efectul precizarii dimensiunii cmpului de afisare:

15

Afisarea unor valori folosind descriptori de format cu precizarea dimensiunii cmpului de afisare #include "stdio.h" void main(void) { int i; float x; i=4567; printf("\n printf("\n printf("\n printf("\n

i=%4i",i); i=%6i",i); i=%3i",i); i=%06i",i);

/* /* /* /*

i=4567 i= 4567 i=4567 i=004567

*/ */ */ */

x=76.123001; printf("\n x=%10f",x); printf("\n x=%010f",x);

/* x= 76.123001 /* x=076.123001

*/ */ */ */

printf("\n %3s","testare"); /* testare printf("\n %10s","testare"); /* testare }

Precizia de afisare se specifica printr-un punct urmat de un ntreg pozitiv. Specificatorul de precizie astfel obtinut se plaseaza imediat dupa dimensiunea cmpului de afisare (cnd este precizata). Interpretarea lui depinde de tipul de date avut n vedere. De exemplu, descriptorul %7.3f indica afisarea unui numar real pe minim 7 coloane si cu 3 cifre zecimale dupa virgula. Lucrurile se petrec asemanator daca n loc de %f se foloseste %e sau %E. Daca se foloseste unul din descriptorii %g sau %G specificatorul de precizie arata minimul de cifre semnificative. Aplicat unui ntreg, specificatorul de precizie arata numarul minim de cifre cu care va apare afisat ntregul respectiv (daca ntregul nu are suficiente cifre atunci se completeaza la nceput cu numarul necesar de cifre 0). Un descriptor de format prevazut cu specificator de dimensiune si specificator de precizie poate fi aplicat unui sir cu lungimea cuprinsa ntre valoarea specificatorului de dimensiune si cea a specificatorului de precizie. Daca se depaseste valoarea specificatorului de precizie, sirul se trunchiaza. Se poate impune alinierea la stnga a datelor plasnd semnul (minus) imediat dupa semnul % n cadrul descriptorului.

16

Programul urmator ilustreaza toate cazurile prezentate:

Exemplu 3.6
Afisarea unor valori folosind diverse facilitati ale descriptorilor prezentati #include "stdio.h" void main(void) { int i; double x; i=4567; printf("\n i=%3.7i",i); printf("\n i=%7.3i",i); printf("\n i=%-7.3i",i);

/* i=0004567 /* i= 4567 /* i=4567

*/ */ */

x=76.123401; printf("\n x=%10.3f",x); printf("\n x=%-10.3f",x); printf("\n x=%3.7f",x); printf("\n x=%10.2e",x); printf("\n x=%-10.1E",x); printf("\n x=%10.3g",x); printf("\n x=%-10.4G",x); printf("\n printf("\n printf("\n printf("\n }
Functia de citire scanf() are forma generala:

/* /* /* /* /* /* /*

x= 76.123 x=76.123 x=76.1234010 x= 7.61e+01 x=7.6E+01 x= 76.1 x=76.12

*/ */ */ */ */ */ */ */ */ */ */

%.4s","testare"); /* test %10.4s","testare"); /* test %-10.4s","testare");/* test %-1.10s","testare");/* testare

int scanf(sir_format,lista_de_argumente); unde: ?? sir_format poate contine descriptori de format, caractere de spatiere albe, alte caractere; ?? lista_de_argumente este de forma: &var1,&var2,...,&varn. Prin &v se ntelege adresa variabilei v. Functia ntoarce numarul de argumente carora li s-a atribuit o valoare sau constanta EOF (egala de obicei cu 1) n caz de insucces. Precizari: n marea lor majoritate descriptorii de format folositi la functia scanf() sunt identici cu cei de la functia printf(); practic, din tabelul prezentat anterior obtinem o lista valida pentru scanf(), ndepartnd %E,%X,%G. Folositi cu scanf(), descriptorii %f,%e,%g sunt echivalenti. ?? Lista de argumente este citita de la stnga la dreapta si asociata n aceasta ordine cu lista de descriptori. Fiecare descriptor arata functiei scanf() tipul valorii care se va citi: ntreg, real, sir, pointer etc.. Sa observam ca aceste valori sunt transferate variabilelor v1,v2,...vn prin intermediul adreselor &v1,&v2,...&vn.
??

17

Este de mentionat faptul ca n cazul citirii unui sir, deoarece nsusi numele sirului reprezinta o adresa, operatorul de luare a adresei & nu va mai preceda obligatoriu numele sirului. Un exemplu de program care citeste siruri de caractere este prezentat n Capitolul 5 (vezi paragraful Functii pentru prelucrarea sirurilor de caractere). ?? Ca si n cazul functiei printf() descriptorii de format pot avea si un modificator de lungime maxima a sirului de caractere care va fi citit. De exemplu, apelul:
??

scanf(%15s,sir); are drept consecinta citirea a maximum 15 caractere din sirul de intrare si atribuirea sirului format variabilei sir. Daca sirul de intrare are mai mult de 15 caractere, caracterele n plus se ignora; la un nou apel al functiei scanf() explorarea sirului de intrare ncepe cu aceste caractere anterior ignorate. ?? Caracterele albe de spatiere n sirurile de intrare pot fi blankurile (spatiile albe), taburile (spatii tab), sau caracterul linie noua (tasta enter). Aceste caractere albe de spatiere sunt ignorate dacp n sirul format avem corespunzator ntre descriptori cel putin un spatiu. De asemenea, orice alt caracter poate fi folosit ca separator n fluxul de intrare, cu conditia ca el sa fie plasat corespunzator si ntre descriptorii de format din sir_format. Daca aceasta conditie nu e ndeplinita, la prima neconcordanta (de la stnga la dreapta) ntre separatorii din fluxul de intrare si cei din sir_format executia functiei scanf() se ncheie. De exemplu, apelul functiei scanf(): scanf(%d,%f,%s,&x,&y,sir); realizeaza o atribuire corectaa datelor de intrare daca ele sunt despartite prin virgula. Pentru a atribui variabilei x valoarea 32, lui y valoarea 10.75 =i variabilei sir valoarea anI, n fluxul de intrare trebuie sa avem 32,10.75,anI.

Observatii:
??

X =i x sunt variabile diferite;

Citirea numerelor reale x =i X de la tastatura, calculul produsului x*X si afisarea lui n format exponential. #include "stdio.h" void main(void) { float x,X; printf("\n Tastati doua numere separate prin spatiu "); scanf("%f %f",&x,&X); X=X*x; printf("\n Produsul X*x este = %e", X); } X=X*x; este o expresie de atribuire care se poate scrie mai scurt sub forma X*=x; cu ajutorul operatorului compus *=. Functiile scanf() si printf() sunt functii de intrare/iesire standard cu destinatie generala. Din considerente de eficienta (cod mai mic, viteza de executie sporita, comoditate n programare etc.) limbajul C pune la dispozitia utilizatorului si functii cu destinatie speciala.
??

18

FUNCTII SPECIALE PENTRU CITIREA/SCRIEREA CARACTERELOR LA NIV ELUL CONSOLEI Standardul ANSI C prevede doua functii simetrice pentru transferul caracterelor la nivelul consolei: functiile getchar() si putchar(). Ele si au prototipurile n stdio.h. Functia pentru citire getchar() are forma generala int getchar(void); si ntoarce urmatorul caracter care va fi citit. Daca s-a atins sfrsitul sirului sau se produce o eroare se ntoarce EOF. Desi nu apartin standardului ANSI C, totusi, functiile getch(), getche(), putch() sunt incluse frecvent n biblioteca standard a compilatoarelor compatibile DOS. Prototipurile acestor functii sunt n fisierul header conio.h. Functiile getch() si getche() sunt echivalente functional cu functia getchar(). Forma generala este: int getch(void); int getche(void); Spre deosebire de functia getchar() unde caracterul tastat este citit numai daca se apasa n continuare tasta Enter, functiile getch() si getche() preiau caracterul imediat dupa ce a fost tastat (fara a mai apasa Enter). De asemenea, functia getch() preia caracterul de la tastatura fara a-l afisa pe ecran, n timp ce getche() afiseaza pe ecran caracterul citit (citire cu ecou). Functia pentru afisare putchar() are forma generala int putchar(int ch); unde ch este caracterul care se afiseaza. Functia ntoarce n caz de succes caracterul scris, iar n caz contrar EOF. Functia putch() este echivalenta functional cu putchar() si are forma generala int putch(int ch); unde ch este caracterul care se afiseaza. Ea este de asemenea o functie nestandard (nu e definita de standardul ANSI C) frecvent utilizata.

Citirea si afisarea unui caracter folosind functiile speciale getche() si putch() #include "stdio.h" #include "conio.h" void main(void) { char x; printf("\n Tastati o litera! "); x=getche(); printf("\n Multumesc! Ati tastat litera "); putch(x); getch(); } FUNCTII SPECIALE PENTRU CITIREA/SCRIEREA SIRURILOR DE CARACTERE LA NIVELUL CONSOLEI Functii speciale pentru citirea/scrierea sirurilor de caractere la nivelul consolei sunt gets() si puts(). Ambele functii si au prototipurile n fisierul header stdio.h. Aceste prototipuri sunt

19

prezentate n Capitolul 12 al lucrarii. Deoarece n constructia acestor prototipuri intervine notiunea de pointer, prezentata n detaliu n Capitolul 6, ne limitam aici la a spune ca prin apelul gets(sir_destinatie); se citeste un sir de la tastatura n sir_destinatie, iar apelul puts(sir); are ca efect afisarea sirului sir pe ecran. Dam ca exemplu secventa de program: . . . . . . . gets(x); . . . . . . . printf(\n Sirul citit este =); puts(x); . . . . . . . Un program complet care utilizeaza functiile gets() si puts() este prezentat n Capitolul 5 (vezi paragraful Functii pentru prelucrarea sirurilor de caractere).

OPERATORI. CLASIFICARE Operatorii sunt elemente de baza ale limbajului care arata ce operatii trebuie executate asupra unor operanzi. n C, operanzi pot fi constantele, numele de variabile, numele de functii, expresiile. Bogata familie de operatori confera limbajului C o trasatura aparte. Clasificarea operatorilor C se poate face dupa mai multe criterii: ?? dupa numarul de operanzi prelucrati (unari, binari, ternari); ?? dupa prioritatea avuta n evaluarea expresiilor (clase de precedenta); ?? dupa tipul operanzilor (aritmetici, relationali, logici si la nivel de bit ). OPERATORI UNARI, BINARI, TERNARI Tinnd cont de numarul de operanzi prelucrati, n C exista operatori unari, binari si ternari. Clasele rezultate nu sunt disjuncte, n sensul ca, de exemplu, un operator unar poate fi si binar. Astfel, n expresia -3 operatorul - (minus) este unar, iar n expresia a-3, este binar. Singurul operator ternar este operatorul conditional ?:. Operanzii sai sunt plasati dupa schema operand1 ? operand2 : operand 3. CLASE DE PRECEDENTA Prioritatile operatorilor impun ordinea de evaluare a expresiilor. Ca si n calculele algebrice obisnuite ordinea de evaluare poate fi modificata cu ajutorul parantezelor rotunde. Operatorii care au prioritati egale, apartin aceleiati clase de precedenta. Lista operatorilor grupati dupa clase de precedenta este data n Tabelul 3.2. Tabelul 3.2 Clase de precedenta Clasa 1 (paranteze, op. de selectie) 2 (op.unari) 3 (op. multiplicativi) 4 (op. aditivi) 5 (op. shift) 6 (op. relationali) Operatori () [] -> . ++ -- ! ~ - + & * % / * + << >> < <= > >=

sizeof

cast

20

7 (op. relationali) 8 (SI pe bit) 9 (SAU exclusiv bit cu bit) 10 (SAU bit cu bit) 11 (SI logic) 12 (SAU logic) 13 (operator conditional) 14 (atribuire) 15 (secventiere)

== != & ^ | && || ?: = += -= *= etc. ,

Functioneaza, de asemenea, reguli de asociere de la stnga la dreapta sau de la dreapta la stnga. Singurii operatori care se asociaza de la dreapta la stnga sunt operatorii unari si operatorul ?:, restul se asociaza de la stnga la dreapta. Operatorul de atribuire = ocupa un loc aparte n familia operatorilor. Cu ajutorul lui putem sa atribuim unei variabile o anumita valoare. Forma sa generala este: v=e; unde v este un nume de variabila, iar e este o expresie. n C, membrul stng si membrul drept al unei atribuiri se mai numesc valoare stng[ (lvalue), respectiv valoare dreapta (rvalue). Spre deosebire de alte limbaje (Fortran, Pascal etc.) n C, operatorul de atribuire poate apare si n interiorul unei expresii, fapt ce permite o compactare a codului sursa. De exemplu, doua atribuiri succesive de genul: A=pi*r*r; V=A*h; pot fi scrise compact sub forma: V=(A=pi*r*r)*h; Practic, ce am scris mai sus este o instructiune expresie. Rezultatul evaluarii expresiei (A=pi*r*r)este pi*r*r; dupa cum se vede, acest rezultat se poate folosi mai departe n calcule. Atribuirea valorii pi*r*r variabilei A apare ca un efect secundar al instructiunii expresie A=pi*r*r;. Compilatorul C permite ca n expresii de genul v=e; v =i e sa aiba tipuri diferite. n aceasta situatie au loc conversii de tip. Regula de conversie este urmatoarea: valoarea membrului drept (valoarea lui e) se converteste la tipul membrului stng (tipul lui v). Deoarece sizeof (int) <= sizeof (float) <= sizeof (double), se spune ca int este mai >slab> dect float, care este la rndul sau mai >slab> dect double. Daca membrul stng este de un tip mai >slab> dect tipul membrului drept pot avea loc pierderi de informatie (prin trunchiere) sau depasirea posibilitatilor de reprezentare. De exemplu, n secventa: int x,y; float a,b; ......... x=a; b=y; variabila x va primi partea fara fractie a valorii a sau un rezultat imprevizibil daca se depasesc posibilitatile de reprezentare, iar valoarea ntreaga y va fi convertita la o valoare reprezentata n virgula mobila. Consideratiile de mai sus referitore la conversia tipurilor sunt valabile si n situatia n care cel putin unul dintre cei doi operanzi ai operatorului de atribuire sunt variante ale tipului int (char, signed, unsigned, short, long) sau ale tipului double (long double).

21

n C este posibila atribuirea multipla, ca n exemplul de mai jos: x=y=z=s=0; Efectul este atribuirea valorii 0 variabilelor x,y,z,s. De asemenea, n anumite situatii se pot folosi operatorii de atribuire compusa. Practic, orice atribuire de genul: variabila=variabila operator expresie; unde operator poate fi ales din lista de operatori *, /, %, +, -, >>, <<, ? &, ^ se , poate scrie simplificat sub forma: variabila operator=expresie; De exemplu, expresia x=x+2; are acelasi efect cu x+=2; Daca v e un operand oarecare cazurile uzuale de atribuire v=v+1 =i v=v-1 se pot scrie simplificat sub forma v++ si respectiv v--. Operatorul ++ se numeste operator de incrementare, iar -- operator de decrementare. Functie de pozitia lor fata de operand ei pot fi operatori prefix sau postfix. De exemplu: x=x+1 se poate scrie x++ sau ++x, x=x-1 se poate scrie x-- sau --x. Daca operandul nu apare n cadrul unei expresii, dupa cum se vede din exemplul de mai sus, nu are importanta daca operatorii sunt prefix sau postfix. Cnd operandul apare n cadrul unei expresii, daca este precedat de operatorul ++ sau -- se executa nti incrementarea sau decrementarea operandului si apoi este folosit n expresie; daca este ns urmat de operatorul ++ sau -- incrementarea sau decrementarea se va face dupa folosirea sa n expresie. De exemplu, expresia y=++x; e echivalenta cu secventa x=x+1; y=x; iar expresia y=x++; e echivalenta cu secventa y=x; x=x+1; Sa observam ca cele doua secvente vor produce aceeasi valoare pentru x =i valori diferite pentru y. Folosirea operatorilor de incrementare si decrementare este recomandabila nu doar din ratiuni de simplificare a scrierii programelor ci si datorita faptului ca majoritatea compilatoarelor C genereaza coduri obiect foarte rapide n astfel de cazuri. Operatorul de secventiere (virgula) permite construirea unei expresii ca o lista de alte expresii. Expresiile din lista se evalueaza de la stnga la dreapta. Valoarea si tipul ntregii expresii este dat de ultima expresie din lista. De exemplu, n urma executiei secventei de program: . . . . . int x=7,y=3,z,w; . . . . . z=(w=x<y,x+y); z va primi valoarea 10. Explicatia este urmatoarea: se evalueaza mai nti expresia w=x<y, apoi expresia x+y. Rezultatul evaluarii expresiei x+y este 10, iar ntreaga expresie (w=x<y,x+y) va primi aceasta valoare care se va atribui lui z. Practic, operatorul virgula ofera o modalitate eleganta de a scrie mai multe expresii n secventa, sub forma compacta a unei singure expresii. n exemplul de mai sus, expresia z=(w=x<y,x+y); nlocuieste secventa w=x<y; z=x+y;

22

Operatorii ?,[,],*,&,sizeof,,-> vor fi prezentati mai pe larg n capitolele unde prezenta lor este necesara: operatorul ?: la instructiuni conditionale, parantezele patrate [ =i ] la tipul de date tablou, *,& =i sizeof la pointeri, =i -> la tipurile de date struct si union. OPERATORI ARITMETICI Limbajul C are urmatorii operatori aritmetici: -,+,*,/,%,--,++. Semnificatia lor rezulta din Tabelul 2.5 si din prezentarea facuta anterior operatorilor ++ si --. n plus vom face urmatoarele observatii:
?? aplicat unor operanzi ntregi operatorul / va produce doar partea ntreaga a mpartirii; de exemplu, secventa:

. . . int x,y; x=7; y=x/2; . . . va produce valoarea 3 pentru y. ?? operatorul % aplicat unor operanzi ntregi furnizeaza restul mpartirii acelor ntregi; de exemplu, secventa: . . . int x,y x=7; y=x%2; . . . produce pentru y valoarea y=1, adica restul mpartirii lui 7 la 2.

OPERATORI RELATIONALI SI LOGICI Operatorii relationali din limbajul C sunt: <, >, <=, >=, ==, !=. Semnificatia lor rezulta din Tabelul 2.5. n urma evaluarii unei expresii n care intervin operatori relationali rezulta valoarea 0 pentru fals si 1 pentru adevarat. Programul de mai jos afiseaza valorile anuntate n comentariile alaturate: Operatorii logici din C sunt: ! && || NU logic SI login SAU logic

23

Afisarea valorilor unor expresii n care intervin operatori relationali


#include "stdio.h" #include "conio.h" void main(void) { float x=0, printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n getch(); }

y=2.3; x<y are x<=y are x>y are x>=y are x==y are x!=y are

valoarea valoarea valoarea valoarea valoarea valoarea

%d",x<y) ; %d",x<=y); %d",x>y) ; %d",x>=y); %d",x==y); %d",x!=y);

/*1*/ /*1*/ /*0*/ /*0*/ /*0*/ /*1*/

Modul de actiune al acestor operatori este prezentat n Tabelul 3.3. Tabelul 3.3 Tabla valorilor de adevar pentru operatori logici A 0 0 1 1 Observatii: Din Tabelul 3.2 rezulta ca operatorii logici && si || au o prioritate mai mica dect operatorii relationali. Din acest motiv: expresia a<=b||c>d e echivalenta cu (a<=b)||(c>d) expresia a>b&&c<=d e echivalenta cu (a>b)&&(c<=d) Concluzia este ca n astfel de situatii prezenta parantezelor este optionala. Exista nsa cazuri n care parantezele sunt absolut necesare; de exemplu, daca dorim sa negam expresia a>3 vom scrie !(a>3) si nu !a>3. Expresia !(a>3) va returna corect valoarea 0 sau 1 functie de marimea lui a, n timp ce expresia !a>3 are totdeauna valoarea 0 (datorita prioritatii mai mari a operatorului ! fata de > se evalueaza mai nti !a care poate fi 0 sau 1; oricare ar fi valoarea lui a, rezulta n final valoarea 0). Daca ntr-o expresie formata din operanzi legati prin operatorul || , iar valoarea primului operand este 1, valoarea expresiei este 1 (vezi Tabelul 3.2 ) si ceilalti operanzi nu se mai evalueaza. Daca ntr-o expresie formata din operanzi legati prin operatorul &&, primul operand ia valoarea 0, valoarea expresiei este 0 iar ceilalti operanzi nu se mai evalueaza. B 0 1 0 1 a && b 0 0 0 1 a || b 0 1 1 1 !a 1 1 0 0

24

Deoarece prezenta parantezelor nu reduce viteza de executie a expresiilor, ele pot fi folosite, alaturi de spatii, la cresterea gradului de lizibilitate a unui program. Iata doua expresii echivalente: a=c<d||d>=c&&a<3; a = (c<d) || (d>=c) && (a<3); Este evident ca cea de a doua expresie este mai usor de citit. OPERATORI LA NIVEL DE BIT Operatorii la nivel de bit din C permit programatorului sa manevreze bitii apartinnd unor valori de tip char sau int (ei nu se aplica tipurilor de date float, double sau long double). Aceasta este una din acele facilitati care apropie limbajul C de nivelul limbajelor de asamblare. Operatorii la nivel de bit sunt: & SI pe bit | SAU pe bit ^ SAU exclusiv pe bit ~ NU pe bit (complement fata de 1) >> deplasare dreapta (shift dreapta) << deplasare stnga (shift stnga) Modul n Tabelul 3.4. de actiune al primilor 4 operatori pe bit e rezumat

Tabelul 3.4 Tabla valorilor de adevar pentru operatorii pe bit &,|,^ =i ~ A 0 0 1 1 b 0 1 0 1 a & b 0 0 0 1 a | b 0 1 1 1 a ^ b 0 1 1 0 ~a 1 1 0 0

Se observa ca a^b ia valoarea 1 numai daca bitii a si b sunt diferiti. Asemanarea dintre Tabelul 3.3 si Tabelul 3.4 nu trebuie sa ne induca n eroare. Operatorii logici trateaza valorile 0 si 1 ale operanzilor ca pe valori logice, iar operatorii pe bit trateaza operanzii ca succesiuni de biti. Sa consideram doua valori x=5 =i y=36 si reprezentarile lor n baza 2, adica x=0000 0101 si y=0010 0100. Avem: 0000 0101 0010 0100 x&y= 0000 0100 0000 0101 0010 0100 x^y= 0010 0001 Observatii: Operatorul & reprezinta o modalitate de a elimina un bit (de a-l face egal cu 0) sau de a retine bitii care ne intereseaza (operatie de mascare). ?? n operatiile de transmitere a datelor, bitul superior (cel mai din stnga) al unui octet este considerat de cele mai multe ori bit de paritate. Printr-o operatie de setare la 1 a acestui bit sau de anulare a sa, se poate obtine un numar par sau impar de biti n octetul respectiv functie de conventia de paritate acceptata (paritatea poate fi para sau impara). Utilizarea paritatii, permite
??

0000 0101 0010 0100 x|y= 0010 0101 0000 0101 ~x= 1111 1010

25

verificarea corectitudinii octetului transmis. Aratam mai jos cum se poate modifica bitul de paritate: Exemplu: 1. Anularea bitului de paritate al unui octet. 1000 0101 & 0111 1111 0000 0101 2. Setarea la valoarea 1 a bitului de paritate a unui octet. 0000 0101 | 1000 0000 1000 0101 Observam ca anularea bitului de paritate s-a facut prin nmultire cu numarul 127 (0111 1111 n baza 2), iar setarea la valoarea 1 a aceluiasi bit prin adunare cu numarul 128 (1000 0000 n baza 2). ?? Datorita posibilitatii de a modifica valorile bitilor, operatorii pe bit se folosesc mai ales n proiectarea programelor de interfata cu dispozitive periferice. ?? O alta aplicatie interesanta se refera la codificarea si decodificarea unui fisier. O modalitate simpla este folosirea operatorului ~, pornind de la observatia ca ~(~x)=x, pentru x ntreg arbitrar. Deci, daca exista un program de codificare, la prima sa rulare toti bitii nuli devin 1, iar toti bitii d 1 devin nuli ( codificarea). La o noua rulare este evident ca se obtine fisierul initial e (decodificarea). ?? O posibilitate mai puternica de codificare si decodificare este operatorul ^. n acest caz pentru aceste operatii se foloseste o cheie. Relund exemplul cu valorile x=0000 0101 =i y=0010 0100 0000 0101 0010 0100 x^y= 0010 0001 sa presupunem ca y este cheia. Daca ntre codificarea obtinuta (x^y) si cheia y efectuam din nou operatia ^ se obtine rezultatul initial x (decodificarea). 0010 0001 0010 0100 (x^y)^y= 0000 0101 Forma generala a operatorilor de deplasare este: variabila << numar_intreg, pentru deplasare la stnga si variabila >> numar intreg, pentru deplasare la dreapta. Aici, numar_intreg se refera la numarul de pozitii cu care vor fi deplasati spre stnga, respectiv spre dreapta bitii variabilelor. Deplasarea bitilor spre un capat sau altul poate produce pierderea unui numar de biti de la respectiva extremitate. Cu ce se completeaza bitii ramasi liberi? Daca deplasarea este spre stnga, bitii liberi din dreapta se completeaza cu 0. Exemplu. Sa consideram declaratia unsigned char x=7; Numarul 7 este reprezentat n baza 2 ca 0000 0111. Atunci, x<<1 va produce 0000 1110. Daca vom continua cu x<<5 se va obtine 1100 0000 ceea ce arata pierderea unui bit.

26

Daca deplasarea este spre dreapta, bitii liberi din stnga se completeaza automat cu 0 numai daca numarul este fara semn. Daca numarul este negativ, din neccesitatea de a conserva semnul (reprezentat n bitul cel mai semnificativ cu 1), bitii liberi din stnga se completeaza cu 1. Exemplu. Sa consideram x=-9. Reprezentarea sa n binar se face folosind codul complementar fata de 2. Acesta se obtine adunnd la complementul fata de 1 al numarului valoarea 1. Deci, valoarea -9 se va obtine ca ~9+1. Suita de operatii n binar este urmatoarea: 9 ~9 ~9+1 -9 se reprezinta prin este este se reprezinta prin 0000 1111 1111 1111 1001 0110 0111 0111

n consecinta, tinnd cont de precizarea facuta mai sus x>>2 va produce 1111 1101

Observatii. Deplasarea spre stnga cu n pozitii echivaleaza cu o nmultire a variabilei cu 2n, iar deplasarea spre dreapta cu n pozitii echivaleaza cu mpartire a variabilei cu 2n. Aceste deplasari (operatii shift) sunt mai rapide dect operatiile corespunzatoare de nmultire sau mpartire cu 2n. Exemplu: Daca x=7 atunci: x<<1 va produce 0000 1110 adica, 7x21=14 Daca x=-8, atunci x>>2 va produce 1111 1110 adica, -2=-8/22.

CONVERSII DE TIP IMPLICITE Limbajul C ofera posibilitatea de a construi expresii cu date de tipuri diferite. Din acest motiv exista un set de reguli de conversie a operanzilor la tipul operandului cel mai >tare>. Aceste reguli sunt cunoscute sub numele de avansare de tip (type promotion). Iata setul de reguli:
?? ??

Variabile de tip char si short se convertesc la tipul int. Daca un operand este long double Atunci al doilea este convertit la long double; Altfel daca un operand este double Atunci al doilea e convertit la double; Altfel daca un operand este float Atunci al doilea e convertit la float; Altfel daca un operand este unsigned long Atunci al doilea e convertit la unsigned long; Altfel daca un operand este long Atunci al doilea e convertit la long; Altfel daca un operand este unsigned Atunci al doilea este convertit la unsigned.

27

CONVERSII DE TIP EXPLICITE (OPERATORUL CAST) Operatorul de conversie explicita (cast) actioneaza temporar, fortnd schimbarea tipului expresiei la care se refera. Forma generala a operatorului este: (tip)expresie; unde tip este tipul la care dorim sa se faca conversia expresiei. De exemplu, rezultatul evaluarii expresiei (float)i/2; unde i a fost definit prin int i=3; este 1.5. Daca n aceleasi conditii se evalua expresia i/2; rezultatul ar fi fost trunchiat la 1 (se mparteau doi ntregi).

28

4. Instructiuni de control ale programului


n acest capitol se prezinta instructiunile de control ale unui program C: instructiunea expresie, instructiunile decizie (sau de selectie), instructiunile iterative (repetitive sau de ciclare) si instructiunile se salt. n situatia n care sintaxa limbajului impune utilizarea unei singure instructiuni, dar logica programului cere folosirea unei secvente de instructiuni, secventa de instructiuni se organizeaza ca o instructiune bloc (sau instructiune compusa). O instructiune bloc ncepe cu { si se termina cu }.

INSTRUCTIUNI EXPRESIE
Instructiunea expresie are forma generala: expresie; unde expresie are efect lateral (contine o atribuire sau reprezinta un apel de functie). Secventa de program urmatoare contine exemple de instructiuni expresie: . . . . . . . x=(a+b)*c; x+=2; p++; getch();

INSTRUCTIUNI DE DECIZIE
INSTRUCTIUNEA IF Forma generala a instructiunii if este: if(expresie) instructiune_1; else instructiune_2; Efectul instructiunii este urmatorul: daca expresie este adevarata (diferita de zero) se executa instructiune_1 n caz contrar (expresie este egala cu zero) se executa instructiune_2. Figura 4.1 ilustreaza modul de executie al instructiunii if.

Fals

Expresie

Adev[rat

Instruc\iune_2

Instruc\iune_1

Figura 4.1. Modul de executie al instructiunii if

Programul calculeaza si afiseaza radicalul dintr-un numar. #include "stdio.h" #include "conio.h" #include "math.h" void main(void) { float x,y; printf("\n Introduceti x="); scanf("%f",&x); if (x<=0) printf("\n Calcul imposibil"); else { y=sqrt(x); printf("\n y=%f",y); } getch(); }

Observatii: Sintaxa generala a instructiunii if cere pe ambele alternative cte o instructiune. n situatia n care pe o alternativa sunt necesare mai multe instructiuni acestea vor fi grupatecu ajutorul acoladelor ntr-o instructiune bloc. Astfel, n exemplul de mai sus, secventa

30

{ y=sqrt(x); printf("\n y=%f",y); } este o instructiune bloc. Daca alternativa else a instructiunii if este vida atunci se poate scrie: if(expresie) instructiune_1 else; sau si mai simplu if(expresie) instructiune; forma cunoscuta sub numele de if cu ramura vida. Instructiunea instructiune se executa numai daca expresia este adevarata, adica se executa conditionat. Modul de executie al instructiunii if cu ramura vida este ilustrat n Figura 4.2.

Fals

Expresie

Adev[rat

Instruc\iune

Figura 4.2. Modul de executie al instructiunii if cu ramura vida

31

Programul calculeaza maximul dintre doua numere. #include "stdio.h" #include "conio.h" void main(void) { float x,y,max; printf("\n x="); scanf("%f",&x); printf("\n y="); scanf("%f",&y);; max=x; if (max<y) max=y; printf("\n Maximul dintre x=%.2f si y=%.2f este=%.2f",x,y,max); getch(); }

Daca n structura unei instructiuni if intervin alte instructiuni if avem de a face cu o structura de instructiuni if incluse sau imbricate. Forma generala a unei astfel de structuri este urmatoarea:
??

if(expr1) instr1; else if(expr2) instr2; else . . . . . . if(exprn) instrn; else instrn+1 Efectul executiei unei instructiuni if imbricate este urmatorul: daca nu exista i, i=1,...,n astfel nct expresia expri sa fie adevarata, se executa instrn+1, n caz contrar se executa prima instructiune (considernd evaluarea de sus n jos), instri pentru care expri este adevarata, iar restul instructiunilor se ignora. Pentru n >mare>, folosind o aliniere stricta, se ajunge la o structura cu adncime mare (mult deplasata spre dreapta). Din acest motiv se foloseste de obicei forma:

32

if(expr1) instructiune1; else if(expr2) instructiune2; else if(expr3) instructiune3; . . . . . . else instructiunen+1; numita si scara if-else-if.

Programul citeste coordonatele unui numar si stabileste n ce cadran se afla acesta. #include "stdio.h" #include "conio.h" void main(void) { float x,y; printf("\n abscisa x="); scanf("%f",&x); printf("\n ordonata y="); scanf("%f",&y); if (x>=0 && y>=0) printf("\n Numarul apartine else if (x<0 && y>=0) printf("\n Numarul apartine else if(x<0 && y<0) printf("\n Numarul apartine else printf("\n Numarul apartine getch(); }

cadranului I"); cadranului II"); cadranului III"); cadranului IV");

Tinnd cont ca n limbajul C orice expresie diferita de zero este adevarata, o secventa de genul
??

if(expr!=0) instructiune; este echivalenta cu if(expr) instructiune; forma care va fi ntotdeauna preferata. ?? Cuvntul else se asociaza ntotdeauna cu cel mai apropiat if incomplet care nu este deja asociat cu un else si care este n acelasi bloc cu el.

33

De exemplu, n secventa if(x) if(y) printf(\n x si y nenuli); else printf(\n x nul); alinierea si mesajele sugereza asocierea lui else cu primul if. n realitate, else se asociaza cu cel de-al doilea if, fiind adecvata alinierea if(x) if(y) printf(\n x si y nenuli); else printf(\n x nul); Daca totusi vrem sa punem n practica prima intentie putem folosi una din f rmele o echivalente: a) if(x) { if(y) printf(\n x si y nenuli); } else printf(\n x nul);

b) if(x) if(y) printf(\n x si y nenuli); else; else printf(\n x nul); c) if(x&&y) printf(\n x si y nenuli); else if(!x) printf(\n x nul);

??

n ideea creerii unor programe >dense> se pot scrie constructii de genul if( (c=getch() ) == a) putch(a); else printf(\n caracter diferit de a);

unde expresia conditionala contine o atribuire. ?? Constructiile dense trebuie facute nsa cu grija, deoarece este posibil sa avem surprize neplacute. Secventa de mai jos x=y=7; a=5;b=6; if((x=a)? ? (y=b)) printf(\nx=%i si y=%i,x,y); va produce x=5, y=7 si nu x=5, y=6 cum ne-am fi asteptat. Pentru ca expresia (x=a) ? ? (y=b)

34

sa fi adevarata este suficient ca numai una din expresiile (x=a) sau (y=b) sa fie adevarata. Cu alte cuvinte, se executa atribuirea x=5, expresia (x=5) ia valoarea adevarat, valoarea expresiei (y=b) nu mai are importanta si deci atribuirea y=6 nu mai are loc. OPERATORUL CONDITIONAL ? : Operatorul conditional ?: este un operator ternar, iar forma sa generala este: expr1?expr2:expr3; Efectul executiei unei astfel de secvente este echivalent cu efectul executiei secventei: if expr1 expr2; else expr3;

n plus, expresia expr1 ? expr2 : expr3 va lua valoarea expr2 sau expr3 dupa cum expr1 este adevarata sau nu.

INSTRUCTIUNEA SWITCH

Programul afiseaza maximul dintre doua numere a si b citite de la tastatura. #include "stdio.h" #include "conio.h" void main(void) { int a,b; printf("\n a="); scanf("%i",&a); printf("\n b="); scanf("%i",&b); printf("\n Maximul dintre a=%i si b=%i este %i",a,b,a<b?b:a); getch(); }

Instructiunea switch permite selectia unei alternative din mai multe posibile ntr-o forma comoda si eleganta. Forma generala a instructiunii este: switch(expresie) { case c1:secventa de instructiuni1;break; case c2:secventa de instructiuni2;break; . . . . . . . . case cn:secventa de instructiunin;break;

35

default:secventa de instructiunin+1;break; } unde:


?? ?? ?? ?? ??

expresie este expresia selectoare care trebuie sa fie de tip ntreg; c1,c2,...,cn sunt constante de tip ntreg distincte ntre ele; default este o eticheta optionala; secventa instructiunii, pentru orice i=1,...,n+1 se poate constitui sau nu ntr-un bloc de instructiuni, poate sa fie vida sau nu; break este o instructiune care permite saltul la prima instructiune aflata dupa structura switch. Prezenta sa este optionala.

Efectul instructiunii este urmatorul: Daca exista un i astfel nct expresia selectoare este egala cu ci se executa secventa instructiunik,k>=i pna la primul break ntlnit sau pna la sfrsitul instructiunii switch. ?? Daca pentru orice i=1,...,n constantele ci sunt diferite de expresia selectoare se executa instructiunen+1, daca exista optiunea default sau se iese direct din switch, daca aceasta lipseste.
??

Programul citeste una din literele a,A,m,M,p,P de la tastatura si afiseaza o lista de nume care ncep cu una din aceste litere, fara sa tina cont daca litera este mare sau mica. Daca se tasteaza alt caracter se afiseaza un mesaj de eroare. #include "stdio.h" #include "conio.h" void main(void) { printf("\n Tastati una din literele:a,A,m,M,p,P "); switch(getch()) { case 'a': case 'A':printf("\n Aurel,Ana,Andrei"); break; case 'm': case 'M':printf("\n Maria,Mihai,Marin"); break; case 'p': case 'P':printf("\n Paula,Petre,Pavel"); break; default :printf("\n Ati tastat gresit !"); } getch(); }

Observatie. n programul de mai sus, indiferent daca s-a tastat a sau A se afiseaza aceeasi lista de nume: Aurel, Ana, Andrei. Explicatia este urmatoarea. Daca se tasteaza a se >intra> n switch, prin case 'a'. Secventa de prelucrari corespunzatoare fiind vida si nentlnindu-se nici o instructiune break se trece si se executa secventa de prelucrari corespunzatoare constantei case 'A' (adica afisarea listei). Deoarece secventa se ncheie cu break se >iese> din switch. Analog se ntmpla si cu grupurile de litere m,M si p,P.

36

INSTRUCTIUNI ITERATIVE
Instructiunile iterative (repetitive sau de ciclare) permit ca una sau mai multe instructiuni sa fie repetate. Numarul de iteratii depinde de ndeplinirea unei conditii. Daca testul asupra conditiei se face naintea instructiunilor care se repeta, se spune ca iteratia e cu test initial; n caz contrar iteratia e cu test final. n limbajul C exista doua instructiuni cu test initial, while si for si o instructiune cu test final, do - while. INSTRUCTIUNEA WHILE Forma generala a instructiunii while este: while(conditie) instructiune; unde: conditie poate fi orice expresie; instructiune poate fi o instructiune simpla, vida sau o instructiune compusa (numita si corpul ciclului). Efectul instructiunii este urmatorul: se executa instructiune ct timp conditie e adevarata (diferita de zero). Atunci cnd conditie devine falsa (egala cu zero), executia programului continua cu instructiunea imediat urmatoare. Organigrama de mai jos ilustreaza sugestiv modul de lucru al instructiunii while.
?? ??

Condi\ie

Fals

Adev[rat Instruc\iune

Figura 4.3. Modul de lucru al instructiunii while

Observatii: Daca din start conditia este falsa instructiune nu se executa niciodata. Iesirea din ciclu se poate face normal (ca efect al prelucrarilor din instructiune, dupa un numar de pasi, conditie devine falsa), anormal (printr-o instructiune de salt care transfera executia programului din interiorul ciclului n afara lui) sau >niciodata> (conditie ramne mereu adevarata - se obtine asa zisa bucla eterna). Prezentam mai jos un exemplu de program unde apare foarte naturala prezenta testului initial n bucla si deci folosirea instructiunii while.
?? ??

37

Calculul lungimii unui sir de caractere citit de la tastatura; sfrsitul sirului este marcat de tasta Enter (caracterul \r). #include "stdio.h" #include "conio.h" void main(void) { int i=0; printf("\n Tastati un sir:\n"); while (getche()!='\r') i++; printf("\n Lungimea sirului =%d",i); getch(); }

INSTRUCTIUNEA FOR n C, instructiunea for prezenta si n alte limbaje, are implementarea cea mai flexibila. Ea depaseste cadrul traditional n care este plasata de obicei: instructiune cu contor (variabila de control) recomandata spre a fi folosita ori de cte ori se cunoaste numarul de iteratii. Forma generala a instructiunii for este: for(initializare;conditie;actualizare) instructiune; Semnificatia traditionala a celor trei componente este urmatoarea: initializare este de regula o instructiune de atribuire folosita pentru initializarea contorului ciclului; ?? conditie este o expresie care determina sfrsitul ciclului; ?? actualizare se refera la felul n care se modifica variabila contor. Instructiunea for este echivalenta cu secventa de instructiuni
??

initializare while(conditie) { instructiune actualizare }

De aici rezulta si efectul executiei sale: se executa blocul instructiuneactualizare ct timp conditia este ndeplinita.

38

Programul realizeaza suma a n numere reale. #include "stdio.h" #include "conio.h" void main(void) { float s=0,x; int i,n; printf("\n n="); scanf("%i",&n); for (i=0;i<n;i++) { printf("\n x[%i]=",i); scanf("%f",&x); s+=x; } printf("\n Suma este =%f",s); getch(); }

Observatii: Componenta initializare a instructiunii for poate contine atribuiri care nu se refera neaparat la variabila contor. n exemplul de mai sus initializarea variabilei s se poate face n componenta initializare a ciclului for cu ajutorul operatorului virgula, astfel:
??

for(s=0,i=0;i<n;++i) { . . . . . . } . . . . . .
??

Componenta actualizare a instructiunii for poate sa contina mai multe variabile contor.

Variabila contor nu trebuie sa fie prezenta obligatoriu n componenta conditie a unei instructiuni for.
??

39

Afisarea termenilor sirului lui Fibonacci mai mari ca 1 si mai mici ca un numar dat m. +irul are forma 1, 1, 2, 3, 5, 8, , adica primii doi termeni sunt egali cu 1, iar orice alt termen se obtine ca suma a celor doi termeni care-l preced. #include "stdio.h" #include "conio.h" void main(void) { int a,b,c,m,i; printf("\n Limita de afisare ="); scanf("%i",&m); printf("\n Termenii sirului Fibonacci < %i\n",m); printf("\n 1 1 "); for (i=3,a=b=1,c=2;c<m;i++) { printf("%i ",c); a=b;b=c; c=a+b; } getch(); }

Exemple: Daca n programul precedent renuntam la ideea de a afisa numarul de ordine al termenilor putem folosi secventa.
??

for(a=b=1,c=2;c<m;) { printf(\n Termenul =%i,c) a=b;b=c; c=a+b; } Se observa absenta componentei actualizare. Secventa de program int i=50000; for(;i;i--); are ca efect introducerea unei temporizari egala cu timpul necesar decrementarii contorului i de la voloare 50000 la 0. Aici se observa absenta componentei initializare si totodata ciclarea unei instructiunii vide. ?? Oricare din componentele initializare,conditie, actualizare ale instructiunii for poate sa lipseasca, nsa delimitatorii ; trebuie sa fie prezenti. Lipsa componentei conditie este interpretata ca fiind echivalenta cu prezenta unei expresii conditionale adevarate. Din acest motiv, secventa

??

40

for(;;); creeaza un ciclu infinit sau o bucla eterna. INSTRUCTIUNEA DO-WHILE Este recomandabil sa se foloseasca instructiunea do-while cnd instructiunea care reprezinta corpul ciclului trebuie executata cel putin o data. Forma generala a acestei instructiuni este: do { instructiuni } while(conditie); Cnd corpul ciclului este format dintr-o singura instructiune acoladele pot sa lipseasca. Totusi, este recomandabil sa se pastreze chiar si n aceasta situatie pentru a distinge mai usor o instructiune while care ncepe, de o instructiune do-while care se termina. Efectul instructiunii este urmatorul: se executa secventa de instructiuni ct timp expresia conditionala conditie este adevarata. Organigrama de mai jos ilustreaza cu claritate modul de executie al ciclului do-while.

Instruc\iune

Adev[rat

Fals Condi\ie

Figura 4.4. Modul de executie al instructiunii do-while Observatii: Datorita asezarii testului de conditie dupa corpul ciclului, este evident ca grupul de instructiuni se executa cel putin o data. ?? Daca n corpul ciclului nu se afla instructiuni care sa conduca la o conditie falsa dupa un numar finit de ciclari (iesire normala din ciclu) sau instructiuni de salt din interiorul ciclului (iesire anormala) se obtine un ciclu infinit (bucla eterna).
??

41

Programul citeste doua numere a si b de la tastatura si afiseaza suma lor. Procesul continua ct timp la ntrebarea Continuati? se apasa una din tastele care contin literele d sau D. #include "stdio.h" #include "conio.h" void main(void { float a,b; char c; do { printf("\n a="); scanf("%f",&a); printf("\n b="); scanf("%f",&b); printf("\n Suma a+b=%.2f",a+b); printf("\n Continuati? (d/n) "); c=getch(); } while (c == 'D' || c == 'd');

Programul implementeaza un meniu simplu. Pentru doua numere citite de la tastatura se efectueaza una din urmatorele operatii: suma, diferenta produsul sau mpartirea (atunci cnd este posibil) sau se iese din meniu. #include "stdio.h" #include "conio.h" void main(void) { float a,b; char ch; printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n printf("\n do

a=");scanf("%f",&a); b=");scanf("%f",&b); + Adunare"); - Scadere"); * Inmultire"); / Impartire"); r Renunta!"); Introduceti optiunea dvs:");

42

{ ch=getchar(); switch(ch) { case '+' :printf("\n a+b=%.2f",a+b); break; case '-' :printf("\n a-b=%.2f",a-b); break; case '*' :printf("\n a*b=%.2f",a*b); break; case '/' :if (b) printf("\n a/b=%.2f",a/b); else printf("\n Impartire imposibila!"); break; case 'r' : case 'R' :exit(0); } } while (ch != '+' && ch != '-' && ch != '*' && ch != '/'); getch(); }

Observatie. Functia exit() provoaca ntreruperea programului si revenirea n sistemul de operare. Functia are forma generala: void exit(int cod_retur); De obicei, cod_retur are valoarea 0 la iesire normala din program si o valoare diferita de 0 n caz contrar.

Programul calculeaza si afiseaza radicalul dintr-un numar. Noutatea fata de Exemplul 4.1 este aparitia unei bucle iterative de validare: iesirea din bucla urmata de calculul radicalului se face doar atunci cnd numarul citit de la tastatura este pozitiv. #include "stdio.h" #include "conio.h" #include "math.h" void main(void) { float x; do

43

{ printf("\n x="); scanf("\n %f",&x); } while (x<0); printf("\n Radical din x=%.2f este y=%.4f",x,sqrt(x)); getch(); }

CICLURI IMBRICATE (INCLUSE) Exista posibilitatea ca o instructiune de ciclare sa includa o alta instructiune de ciclare; n acest caz se spune ca ciclurile sunt imbricate sau incluse. Rezultatul executiei programului de mai sus este:

Programul ilustreaza folosirea ciclurilor imbricate: #include "stdio.h" #include "conio.h" void main(void) { int i,j; for (i=1;i<=5;i++) { for (j=1;j<=i;j++) printf("%2d",j); printf("\n"); }; getch(); }

1 1 1 1 1

2 2 3 2 3 4 2 3 4 5

Pentru o valoare a variabilei contor i, variabila contor j a ciclului interior variaza de i ori; se spune ca indicele interior variaza mai repede ca indicele exterior. Evident, o structura de cicluri incluse poate sa contina oricare din instructiunile de ciclare permise n C. Iata un exemplu de acest gen:

44

Programul rezolva urmatoarea problema: ct timp se citeste de la tastatura o valoare x diferita de zero se afiseaza sirul de numere x,x+1,x+2,x+3,x+4. Acest proces continua sau se ntrerupe la cerere. #include "stdio.h" #include "conio.h" void main(void) { int x,i; char ch; do { printf("\n x="); scanf("%i",&x); while (x) { for (i=0;i<5;i++) printf("%5d",x+i); printf("\n x="); scanf("%i",&x); } printf("\n Continuati ? (d/n)"); ch=getch(); } while (ch == 'd' || ch == 'D'); }

INSTRUCTIUNI DE SALT
n limbajul C exista patru instructiuni care executa un salt neconditionat n program: break, continue, goto, return. Deoarece instructiunea return se refera la revenirea din functia apelata la punctul unde s-a facut apelul, ea va fi tratata mai trziu n capitolul dedicat functiilor n C. INSTRUCTIUNEA BREAK Dupa cum s-a vazut la prezentarea instructiunii switch, instructiunea break poate ncheia executia unei prelucrari case si forta trecerea executiei la prima instructiune aflata dupa switch. O alta situatie unde instructiunea break si dovedeste utilitatea este atunci cnd se doreste ncheierea fortata a unui ciclu. n acest caz ntlnirea instructiunii break n corpul ciclului determina ntreruperea ciclarii si trecerea controlului la prima instructiune care urmeaza dupa ciclu.

45

Programul >extrage> si afiseaza cel mult 20 de numere aleatoare folosind functia rand(). Daca s-a >extras> numarul 10 programul nu mai efectueaza restul >extragerilor>. #include "stdio.h" #include "conio.h" #include "stdlib.h" void main(void) { int i,x; for (i=0;i<20;i++) { x=rand()%20+1; printf("\n %i. S-a extras numarul %i",i+1,x); if (x==10) break; } getch(); }

Precizari. O instructiune break are efect numai asupra ciclului cel mai interior n care se afla. Aceeasi precizare e valabila si la instructiuni switch imbricate sau incluse n cicluri: instructiunea break va afecta numai prima instructiune switch in care se afla, nu si eventualele instructiuni switch sau de ciclare n care ar putea fi inclusa prima.

Programul tipareste de 10 ori cte 7 numere extrase aleator. #include "stdio.h" #include "conio.h" #include "stdlib.h" void main(void) { int i,j; for (i=0;i<10;i++) { printf("\n"); for (j=0;j<10;j++) { printf("%5d",rand()%10+1); if (j>=7) break; } } getch(); }

46

INSTRUCTIUNEA CONTINUE Instructiunea continue se foloseste numai n corpul unui ciclu; ea forteaza executia urmatoarei iteratii a ciclului, omitndu-se instructiunile prezente ntre cele doua iteratii (locul unde se afla plasata n corpul ciclului si sfrsitul acestuia). Efectul instructiunii este urmatorul: ?? Cnd continue este ntlnita ntr-un ciclu for se continua cu executia componentei de actualizare si apoi a testului conditional. ?? Cnd este ntlnita ntr-un while sau do-while, se trece la executia expresiei conditionale.

Programul citeste de la tastatura 10 numere ntregi nsumndu-le doar pe cele strict pozitive. #include "stdio.h" #include "conio.h" void main(void) { int i,x,s; for (i=s=0;i<10;i++) { printf("\n x="); scanf("%i",&x); if (x<=0) continue; s+=x; } printf("\n Suma este=%d",s); getch(); }

INSTRUCTIUNEA GOTO Forma generala a instructiunii goto este: goto eticheta; . . . eticheta:instructiune; unde eticheta este numele de identificare al instructiunii instructiune. Efectul executiei instructiunii goto este saltul neconditionat la instructiunea identificata prin eticheta. Saltul poate fi facut nainte sau dupa instructiunea goto. Folosirea frecventa a acestei instructiuni n programe afecteaza claritatea acestora, ngreunnd ntelegerea lor. Ea poate

47

fi nsa evitata n orice situatie utiliznd celelalte structuri de control din C. Dam mai jos un exemplu tipic de folosire a instructiunii goto: iesirea fortata dintr-un ciclu sau dintr-o instructiune switch.

Programul reprezinta o alta implementare a Exemplului 4.10. #include "stdio.h" #include "conio.h" void main(void) { float a,b; char ch; for(;;) { clrscr(); printf("\n a="); scanf("%f",&a); printf("\n b="); scanf("%f",&b); printf("\n + Adunare"); printf("\n - Scadere"); printf("\n * Inmultire"); printf("\n / Impartire"); printf("\n r Renunta"); printf("\n Introduceti optiunea dvs:"); ch=getche(); switch (ch) { case '+' :printf("\n a+b=%.2f",a+b); break; case '-' :printf("\n a-b=%.2f",a-b); break; case '*' :printf("\n a*b=%.2f",a*b); break; case '/' :if (b) printf("\n a/b=%.2f", a/b); else printf("\n Impartire imposibila"); break; case 'r' : case 'R' :goto STOP; } getch(); } STOP:printf("\n Am renuntat!"); getch(); }

48

INSTRUCTIUNEA RETURN Instructiunea return realizeaza ntoarcerea din functia apelata n functia care a facut apelul (functia apelanta). O discutie detaliata despre instructiunea return se va face n Capitolul 8, capitol dedicat functiilor C.

49

5. Tablouri, siruri de caractere


Notatia indiciala a variabilelor nu este noua; ea a fost si este folosita n special de matematicieni ori de cte ori este nevoie ca o prelucrare sa fie exprimata ct mai sintetic. Este evident ca prelucrarea S=a+b+c+d+e+f arata mult mai putin elegant dect

S?

x i n ultimul caz cele sase variabile

i ?1

a,b,c,d,e,f au fost redenumite x1,x2,...,x6; ele pot fi privite astfel drept primele 6 componente ale vectorului x. Tablourile reprezinta tocmai implementarea notiunilor de vector, matrice sau masiv multidimensional ntr-un limbaj de nivel nalt. Ele reprezinta colectii omogene de date (adica de acelasi tip), stocate n locatii de memorie nvecinate. Accesul la oricare din elementele tabloului se face cu ajutorul indicilor.

TABLOURI UNIDIMENSIONALE
Declaratia unui tablou unidimensional se face dupa forma generala: tip nume_tablou[dim]; unde: tip reprezinta tipul de baza al elementelor indexate; dim reprezinta numarul de elemente ale tabloului; nume_tablou este un identificator reprezentnd numele tabloului. Dimensiunea zonei continue alocate se calculeaza prin relatia
?? ?? ??

dimens_alloc=dim*sizeof(tip). De exemplu, prin declaratia int a[100]; se vor aloca vectorului a, 100*2=200 de octeti ntr-o zona continua de memorie. Observatii: ?? alocarea se face n timpul compilarii; ?? o data facuta alocarea, n scopul maririi vitezei de executie nu se mai face nici o verificare a dimensiunii; gestionarea corecta a zonei de memorie alocate cade, deci, exclusiv n sarcina programatorului. Referirea la un element al tabloului unidimensional se face preciznd numele tabloului urmat de o expresie ntreaga ntre paranteze drepte, adica nume_tablou[expr] Expresia expr poate lua valori ntregi cuprinse n intervalul[0,dim-1]. Astfel, n urma declaratiei float y[10]; vectorului y va avea 10 componente reale =i anume: y[0] componenta_1 y[1] componenta_2 . . . . . . . . y[9] componenta_10 Intentia de a referi ultimul element al unui tablou sub forma nume_tablou[dim] este o greseala cu att mai grava, cu ct, tinnd cont de observatiile anterioare, ea nu este depistata de compilator. De obicei expresiile ntregi folosite pentru a accesa componentele unui tablou sunt constante sau variabile simple (indici).

Comutarea componentelor unui vector si afisarea lor n coloana #include "stdio.h" #include "conio.h" void main(void) { int x[50],i,n,c; /* citirea elementelor vectorului */ printf("\n lungimea sirului n<50 este:"); scanf("%d",&n); for (i=0;i<n;i++) { printf("\n x[%i]=",i); scanf("%i",&x[i]); } /* comutarea elementelor */ for (i=0;i<n/2;i++) { c=x[i]; x[i]=x[n-1-i]; x[n-1-i]=c; } /* afisarea elementelor vectorului comutat */ for (i=0;i<n;i++) printf("\n componenta x[%i] este %i",i,x[i]); getch(); }

TABLOURI MULTIDIMENSIONALE
Daca elementele unui tablou unidimensional sunt la rndul lor tablouri unidimensionale obtinem un tablou bidimensional sau o matrice. De exemplu, declaratia: int x[4][3]; se poate interpreta astfel: x este un tablou cu 4 elemente x[i], i=0,1,2,3. Fiecare element x[i] cu i=0,1,2,3 este un tablou cu 3 elemente ntregi x[i][j] cu j=0,1,2. Generaliznd, un tablou multidimensional poate fi considerat ca un tablou unidimensional care are ca elemente un tablou cu restul de dimensiuni; declaratia unui tablou cu dimensiunile dim1,dim2,...,dimn are forma generala: tip nume_tablou[dim1][dim2]...[dimn]; iar referirea la un element al tabloului se face prin: nume_tablou[indice1][indice2]...[indicen], unde indicei ia valori ntregi n intervalul [0,dimi-1], pentru i=1,2,...,n.

51

Observatii:
?? Referirea clasic la elementele unui tablou prin separarea indicilor prin virgula este incorecta n C si are semnificatia rezultata din folosirea operatorului de secventiere. De exemplu, considernd declaratia de mai sus, referirea x[i,j] este echivalenta cu x[j]. ?? Dimensiunea zonei continue de memorie alocate este dat[ (n octeti) de valoarea dim1*dim2*dim3*dimn*sizeof(tip).

Calculul si afisarea sumei a doua matrici a si b de dimensiune 4x3 citite de la tastatura.

#include "stdio.h" #include "conio.h" void main(void) { int a[10][10],b[10][10],c[10][10]; short i,j,m,n; /* citirea dimensiunilor mtricilor a,b si c */ do { printf("\n m="); scanf("%i",&m); printf("\n n="); scanf("%i",&n); } while (m<1 || m>10 || n<1 || n>10); /* citirea elementelor matricilor a si b */ for (i=0;i<m;i++) for (j=0;j<n;j++) { printf("\n a[%i][%i]=",i,j); scanf("%i",&a[i][j]); printf("\n b[%i][%i]=",i,j); scanf("%i",&b[i][j]); } /* calculul matricii suma c=a+b */ for (i=0;i<m;i++) for (j=0;j<n;j++) c[i][j]=a[i][j]+b[i][j]; /* afisarea matricii suma c */ printf("\n matricea suma este:\n"); for (i=0;i<m;i++) { for (j=0;j<n;j++) printf("%4i",c[i][j]); printf("\n"); } getch(); }

52

INITIALIZAREA TABLOURILOR
Exemplele anterioare ne-au aratat cum se poate initializa un tablou prin valori date de la tastatura. Exista nsa posibilitatea initializarii unui tablou printr-o definitie de forma: declaratie tablou={lista valori_initiale}; unde valorile_initiale sunt expresii constante compatibile cu tipul de baza al tabloului. Exemple: int vector[6]={-7,-2,95,21,5,-23}; float a[3][2]={1,2,3,4,5,6}; int b[3][2][2]={1,2,3,4,5,6,7,8,9,10,11,12}; n cel de-al doilea caz a fost initializata o matrice. Cum se vor completa elementele matricii? Raspunsul este simplu: pe linii, adica se completeaza linia 0, apoi linia 1 si n sfrsit linia 2. Matricea a va arata astfel: 1 3 5 2 4 6

Pentru a sugera modul de initializare al tablourilor multidimensionale se pot folosi acolade despartitoare n interiorul listei de valori. Astfel, matricea a se poate scrie mai sugestiv int b[3][2]= { {1, 2}, {3, 4}, {5, 6}, }; n general, initializarea elementelor unui tablou multidimensional se face dupa regula <ultimul indice variaza cel mai rapid>, care este o generalizare a regulii de memorare <pe linii>. Astfel, cele douasprezece componente ale tabloului b[3][2][2] vor fi initializate astfel: b[0][0][0]=1 b[0][0][1]=2 b[0][1][0]=3 b[0][1][1]=4 b[1][0][0]=5 b[1][0][1]=6 b[1][1][0]=7 b[1][1][1]=8 b[2][0][0]=9 b[2][0][1]=10 b[2][1][0]=11 b[2][1][1]=12 Aceste reguli decurg din modul de liniarizare a unui tablou in C: pozitiei i1*i2*...*in din tablou i corespunde n forma sa liniarizata (forma sub care i se va aloca memoria) pozitia k data de formula: k=i1*dim2*...*dimn+i2*dim3*dimn+...+in-1*dimn+in. De exemplu, n cazul unei matrici a[m][n] liniarizarea se face dupa formula k=i*n+j. Sa notam n cazul general cu dim produsul dim1*dim2*...*dimn si cu nval_in numarul constantelor din lista de initializare. ?? Daca dim=nval_in initializarea tabloului decurge normal dupa regula de mai sus. ?? Daca dim<nval_in se va produce o eroare, iar daca dim>nval_in restul de elemente sunt initializate cu zero.

53

Se observa ca formula de alocare nu depinde de prima dimensiune a tabloului, fapt ce permite urmatoarele initializari echivalente cu cele prezentate la nceputul sectiunii: int vector[]={-7,-2,95,21,5,-23}; float a[][2]={1,2,3,4,5,6}; int b[][2][2]={1,2,3,4,5,6,7,8,9,10,11,12};

TABLOURI SI SIRURI DE CARACTERE


n C nu exista un tip special pentru definirea unui sir de caractere. sirurile de caractere se construiesc cu ajutorul tablourilor unidimensionale. Declaratia unui sir cu numele, nume_sir de lungime maxim dim-1 caractere se face sub forma: char nume_sir[dim]; Sfrsitul unui sir este marcat de caracterul \0; din acest motiv, n declaratia sirului, dimensiunea tabloului trebuie sa fie cel putin cu o unitate mai mare dect numarul de caractere al sirului pe care vrem sa-l memoram. De exemplu: char sr[10]; contine declaratia sirului sr. Initializarea se poate face printr-o definitie a sirului sub forma: char sr[10]=aAbBcCdD; Daca numarul elementelor din sir este mai mare dect dimensiunea sirului, caracterele n plus sunt ignorate, n caz contrar restul elementelor este initializat cu zero (caracterul nul). Daca nu se precizeaza dimensiunea, se poate face urmatoarea initializare: char sr[]=aAbBcCdD; care este echivalenta cu initializarea tabloului sr cu un sir de caractere, adica char sr[]={a,A,b,B,c,C,d,D,\0}; Se observa prezenta explicita a terminatorului de sir \0. Ultimele doua variante rezerva sirului un numar de locatii egal cu numarul elementelor din sir plus o unitate (corespunzatoare terminatorului \0) si este mai comoda, deoarece nu precizeaza o limita maxima pentru numarul de caractere, permitnd programatorului sa evite numararea elementelor din sir. Legatura dintre siruri si tablouri unidimensionale permite referirea indexata la caracterele sirului. Folosind acest lucru, n exemplul de mai jos se selecteaza si afiseaza literele mici din sirul sir.

Selectarea si afisarea literelor mici dintr-un sir. #include "stdio.h" #include "conio.h" void main(void) { int i; char sir[]="aAbBcCdD"; printf("\n Sirul este: %s",sir);

54

printf("\n Literele mici din sir sunt: "); for (i=0;sir[i];i+=2) printf("%c",sir[i]); getch(); }

FUNCTII PENTRU PRELUCRAREA SIRURILOR DE CARACTERE


FUNCTII CU PROTOTIPUL N STDIO.H Pentru operatiile de intrare/iesire cu siruri se pot folosi functiile scanf() respectiv printf() cu descriptorul de format %s.

Citirea a doua siruri sub forma nume prenume si afisarea lor sub forma prenume nume. #include "stdio.h" #include "conio.h" void main(void) { char nume[25],prenume[25]; printf("\n Numele="); scanf("%s",nume); printf("\n Prenumele="); scanf("%s",prenume); printf("\n %s %s",prenume,nume); getch(); }

Pentru citirea unui sir de la tastatura se poate folosi functia gets() cu forma generala: gets(sir_destinatie); iar pentru afisarea unui sir pe ecran functia puts() cu forma generala: puts(sir). Observatie. Functia gets() transfera n sir_destinatie toate caracterele pna la apasarea tastei Enter. FUNCTII CU PROTOTIPUL N STRING.H Fisierul string.h contine prototipurile functiilor specializate pentru manipularea sirurilor de caractere. Iata cteva dintre acestea: ?? Functia strcpy() cu forma general: strcpy(sir_destinatie, sir_sursa); copiaza sir_sursa n sir_destinatie;

55

??

Functia strcmp() cu forma generala: int strcmp(sir1, sir2);

compara cele doua siruri caracter cu caracter si ntoarce o valoare: <0 =0 >0 dac[ dac[ dac[ sir1<sir2; sir1=sir2; sir1>sir2.

Comparatia sirurilor se face lexicografic. ?? Functia strlen() cu forma generala: unsigned int strlen(sir); ntoarce numarul de elemente dintr-un sir;
??

Functia strcat() cu forma generala: char *strcat(sir1, sir2);

adauga sir2 la sfrsitul sirului sir1 (concatenare).

Se citeste de la tastatura un numar neprecizat de siruri de caractere. Citirea se termina cnd se ntlneste sirul stop. Programul stabileste sirul minim (n sens lexicografic) si l afiseaza mpreuna cu lungimea sa. #include "stdio.h" #include "conio.h" #include "string.h" void main(void) { char min[20],x[20]; printf("\n Introduceti siruri de caractere ! "); printf("\n La sfarsit tastati STOP \n"); strcpy(min,gets(x)); while (strcmp(x,"stop")) { strcmp(min,x)<0 ? min : strcpy(min,x); gets(x); } printf("\n Sirul minim este "); puts(min); printf("\n si are lungimea %i",strlen(min)); getch(); }

56

6. Pointeri
n modul obisnuit de alocare (alocarea statica), asocierea nume de variabila, zona de memorie este constanta pe toata durata executiei programului. Alocarea se face n faza de compilare, iar dealocarea pe parcursul executiei programului este imposibila. Pointerii sunt variabile n care se pot memora adrese de memorie. Ei ofera posibilitatea de a aloca dinamic memoria, adica pe parcursul executiei unui program se pot aloca sau dealoca zone de memorie asociate lor. Forma generala de declarare a unui pointer este: tip *var_pointer; unde tip reprezinta tipul de date asociat pointerului var_pointer. Mai corect, tip reprezinta tipul de date care pot fi indicate (de la verbul to point din limba engleza) de catre var_pointer. Exemple de declaratii de pointeri: int *x,*y; void *z; float *p[10]; Declaratiile de mai sus se citesc astfel: x, y sunt pointeri catre int, z e pointer catre orice sau nimic, iar p este un tablou de pointeri catre float, adica p[0],p[1],...,p[9] sunt pointeri catre float. Orice variabila pointer trebuie initializata cu o valoare valida (0 sau o adresa; obiectele au adrese diferite de zero, iar pentru 0 se foloseste constanta NULL din fisierul header stdio.h).

OPERATORII & SI *
La o variabila pointer putem fi interesati fie de adresa pe care o memoreaza, fie de informatia memorata la aceasta adresa (informatia utila), fie de amndoua. O modalitate de a obtine adresele unor variabile spre a fi memorate ntr-un pointer este oferita de operatorul & numit operator de referentiere sau adresare. Operatorul * numit operator de dereferentiere sau indirectare permite accesul (indirect) la informatia memorata n zonele de memorie referite de pointeri. Daca p este un pointer si contine valoarea unei adrese de memorie, *p va furniza continutul locatiei a carei adresa e memorata n p. Atribuirea din secventa de program int x,*p; p=&x; este corecta si are ntelesul ca adresa variabilei x este memorata n p. Sa consideram secventa: int x,*p; x=10; p=&x; Deoarece n pointerul p se va memora adresa lui x, variabila *p va avea continutul variabilei x, adica *p=10. Din modul de definire, se observa ca operatorii & si * sunt complementari, adica x=*(&x). Sa observam, de asemenea, ca o secventa de genul: int *p; *p=31; este gresita deoarece simpla declaratie a lui p ca pointer nu presupune alocare de memorie.

Corect ar fi, de exemplu, o secventa de forma int x,*p; p=&x; *p=31; care implica si initializarea variabilei x cu valoarea 31. Procedeul de a ne referi la valoarea unei variabile in mod indirect, folosind un pointer este numit indirectare, de unde si numele operatorului * . n atribuirea *p=31, *p tine locul variabilei x, adica are valoare stnga (left value). n general *p poate apare oriunde poate apare variabila x. Este necesar ca tipul pointerului sa fie acelasi cu cel al variabilei la care se refera pentru a asigura ntotdeauna o folosire corecta a indirectarilor. Astfel, secventa int *p; float x,y; x=62549.21; p=&x; y=*p; este gresita deoarece se ncearca folosirea pointerului catre ntregi p spre a transfera n y valoarea reala continuta n x. n aceasta situatie doar doi octeti din x vor fi transferati n y. Sunt posibile si abateri de la regula de mai sus (folosind pointeri catre void sau conversii explicite cast) si n aceste cazuri este evident ca programatorul trebuie sa-si asume responsabilitatea efectuarii corecte a operatiilor. Desi, de obicei, nu suntem interesati n cunoasterea valorii concrete a unui pointer, totusi, se poate obtine aceasta valoare ( n hexazecimal) cu ajutorul descriptorului %p, ca n exemplul urmator:

Afisarea valorii unei variabile pointer #include "stdio.h" #include "conio.h" void main(void) { int x,*x_ptr; x=10; x_ptr=&x; printf("\n Adresa lui x printf("\n Valoarea lui printf("\n Valoarea lui printf("\n Valoarea lui getch(); }

este=%p",&x); x este=%i",x); x_ptr=%p",x_ptr); x=%i",*x_ptr);

Este admisa folosirea pointerului catre tipul void cu ntelesul de pointer care nu are asociat un tip de date precis (pointer catre orice). Declaratiile void * se folosesc n situatiile n care nu trebuie precizat n mod expres tipul pointerilor tocmai pentru a permite scrierea unor functii ct mai generale din punct de vedere al tipurilor pe care le manevreaza. Valoarea NULL (declarata n fisierele antet stdio.h, stdlib.h etc.) are ntelesul de pointer zero si poate fi atribuita oricarui pointer cu semnificatia ca nu indica nimic (nu contine nici o adresa).

58

Asa cum s-a vazut, un pointer poate fi initializat cu adresa unei variabile statice. Exista nsa si posibilitatea de a atribui pointerului considerat adresa unui bloc de memorie din zona dinamica, numita heap. Acest lucru se poate face utiliznd una din functiile speciale, malloc(), calloc(), realloc(), prezentate n Capitolul 12, Functii pentru gestiunea memoriei heap. Funtiile malloc() si calloc() au prototipul n fisierul stdlib.h, iar realloc() are prototipul n fisierul malloc.h. Zonele alocate ramn ocupate pna la dealocarea explicita cu ajutorul functiei free()(prototipul functiei se afla n stdlib.h) prezentate n acelasi paragraf cu functiile de alocare. Drept exemplu, consideram secventa de program: int *p; . . . . . . . /* se aloca o zona de memorie de 30*sizeof(int) octeti la o adresa memorata in p*/ p=malloc(30*sizeof (int)); *p=70; . . . . . . . /* se dealoca(elibereaza) zona de memorie alocata*/ free(p); Observatii:
??

Functia malloc() are forma generala void *malloc(unsigned int, nr_octeti);

Ea ntoarce un pointer de tip void si din acest motiv poate fi atribuit oricarui tip de pointer (vezi secventa program de mai sus). ?? Daca spatiul de alocare este suficient, pointerul ntors de functia malloc() contine adresa primului octet al zonei de memorie alocate. n caz contrar (spatiu insuficient), functia ntoarce valoarea NULL. De aceea, un mod riguros de alocare se poate face sub forma: if(!(p=malloc(50*sizeof(int)))) { printf(\n Spatiu insuficient); exit(1); } Functia free() se aplica unui pointer care indica o zona de memorie alocata anterior cu functia malloc().
??

POINTERI SI TABLOURI UNIDIMENSIONALE


n C, numele unui tablou este echivalent cu un pointer constant catre primul element al tabloului, adica sunt echivalente referirile: t &t &t[0] unde t e pointer catre tipul de baza al tabloului si reprezinta adresa la care e memorat tabloul. n consecinta, e corecta declaratia: int t[10],*p=t;

59

dar nu este corecta o secventa de genul int t1[5],t2[5]; t1=t2; deoarece ea constituie o ncercare de a modifica pointerul constant t1.

POINTERI CATRE TIPUL CHAR SI SIRURI DE CARACTERE


Se pot memora constante sir la o adresa indicata de un pointer catre char. De exemplu, o atribuire de genul char *spoint=Bine v-am gasit!; este valida, deoarece, ntlnind-o, compilatorul va memora sirul ntr-un tabel de siruri genernd pointerul spoint catre adresa sirului. n tabel se vor memora toate constantele sir din program. Daca dorim sa afisam pe ecran mesajul continut n sir este suficient sa scriem printf(spoint) sau printf(%s,spoint). O formula de afisare ca aceasta este utila mai ales daca mesajul este lung si trebuie afisat frecvent. ntr-o declaratie de forma char *sir; sir poate avea mai multe interpretari: pointer catre char, sir de caractere sau vector de caractere alocat dinamic. Daca sirul e declarat ca un tablou de caractere spatiul necesar e alocat automat, iar daca declaratia este ca un pointer la char programatorul trebuie sa aloce n mod explicit spatiu sau sa atribuie adresa unui sir existent (cazul de mai sus).

POINTERI CATRE TABLOURI UNIDIMENSIONALE


Declararea unui pointer catre un tablou unidimensional de dimensiune dim se face sub forma: tip (*var_pointer)[dim]; Sa consideram urmatoarea secventa de program: int x[10],(*t)[10]=&x; t++; Pointerul va contine adresa unui tablou cu 10 ntregi (de fapt adresa primului element al tabloului), iar t++ va contine adresa t=&x+sizeof(int)*10. Observatii: Parantezele rotunde din declaratie sunt esentiale pentru individualizarea pointerilor catre tablouri. Daca ar fi omise, declaratia int *t[10]; ar fi de asemenea corecta, dar ar avea o alta semnificatie si anume: t este un tablou de pointeri catre ntregi. Pentru a ntelege semnificatia pointerilor catre tablouri, sa consideram secventa de program:

60

int x[10],*p=x; p++; Pointerul p va contine, de asemenea, adresa primului element al tabloului x, dar p++ va contine adresa p=&x+sizeof(int) (adica adresa variabilei x[1]). Se observa ca diferenta dintre cele doua cazuri apare la aritmetica pointerilor: ?? cnd se lucreaza cu pointeri catre tablouri de dimensiune dim componente de tip tip, aritmetica folosita are unitatea egala cu dim*sizeof(tip). ?? cnd se lucreaza cu pointeri catre tip aritmetica folosita are unitatea egala cu sizeof(tip). Pointerii catre tablouri unidimensionale sunt extremi de utili mai ales n ntelegerea si folosirea tablourilor cu mai multe dimensiuni n C.

OPERATII ARITMETICE CU POINTERI


Au sens operatiile de adunare (scadere) ale unui pointer cu un intreg. Semnificatia expresiilor p+n,p-n, unde p este pointer de un anumit tip, iar n este un numar ntreg pozitiv este urmatoarea: p+n va contine adresa p+n*sizeof(tip), iar p -n adresa p-n*sizeof(tip), unde tip este tipul pointerului. n secventa urmatoare se considera ca tipul int se memoreaza pe 2 octeti, iar tipul float pe 4 octeti. int *p1,*p2; float *p; . . . . . . p2=p1+3;/* p2 contine adresa p1+3*2 */ . . . . . . p-=5;/* p contine adresa p-5*4 */ De obicei, programatorul este mai putin interesat de adresa concreta a zonei de memorie unde se afla variabilele. De aceea, operatiile asupra pointerilor au semnificatie practica daca deplasarile se fac n interiorul unui tablou. Daca x[n] este un tablou cu n componente, atunci pentru orice i=0,1,,n-1 x+i e echivalent cu &x[i], iar

*(x+i) e echivalent cu x[i]

Comutarea elementelor egal departate de capetele unui vector si afisarea vectorului obtinut. #include "stdio.h" #include "conio.h" void main(void) { int x[7]={1,2,3,4,5,6,7},i,*p,y; /* afisarea vectorului initial */ printf("\n Vectorul initial este:"); for (i=0;i<7;i++) printf("\nx[%i]=%i",i,x[i]);

61

/* comutarea elementelor vectorului x */ for (i=0;i<3;i++) { y=*(x+i); *(x+i)=*(x+6-i); *(x+6-i)=y; } /* afisarea vectorului comutat */ printf("\n Vectorul comutat este:"); for (i=0,p=x;i<7;++i) { printf("\nx[%i]=%i",i,*p); p++; } getch();

Programul reprezinta o alta implementare a Exemplului 6.2. #include "stdio.h" #include "conio.h" void main(void) { int x[7]={1,2,3,4,5,6,7},i,*p,*q,y; /* afisarea vectorului initial */ printf("\n Vectorul initial este:"); for (i=0;i<7;i++) printf("\nx[%i]=%i",i,x[i]); /* comutarea elementelor vectorului x */ p=x; q=&x[6]; do { y=*p; *p=*q; *q=y; p++; q--; } while (p<q); /* afisarea vectorului comutat */ printf("\n Vectorul comutat este:"); for (i=0,p=x;i<7;++i) { printf("\nx[%i]=%i",i,*p); p++; } getch();

62

Observatie. Secventa de instructiuni

printf("\nx[%i]=%i",i,*p); p++;
din programul de mai sus putea fi scrisa echivalent:

printf("\nx[%i]=%i",i,p[i]);
ceea ce arata ca pointerul p poate fi folosit cu indice, ntocmai ca un tablou. Scaderea a doi pointeri p1,p2 are semnificatie n cazul n care p1,p2 au acelasi tip de baza si se refera la elementele aceluiasi tablou. Daca t este numele tabloului si p1=&t[i1]; p2=&t[i2]; atunci p1-p2 semnifica adresa i1-i2 din tabloul t. Sunt posibile combinatii de pointeri cu ajutorul operatorilor &,*,++ si --, nsa ele trebuie facute cu multa grija.Urmatorul program ilustreaza cteva din efectele acestor combinatii (rezultatele afisarii sunt date n comentarii).

Afisarea valorilor unui pointer p combinat n expresii cu operatorii * si ++. #include "stdio.h" void main(void) { int x[]={5,4,3,2,1},*p; p=x; printf("\n Valoarea=%i",*p++); printf("\n Valoarea=%i",*p); p=x; printf("\n Valoarea=%i",(*p)++); printf("\n Valoarea=%i",*p); p=x; printf("\n Valoarea=%i",*++p); p=x; printf("\n Valoarea=%i",++*p); getch();

/* val=5 */ /* val=4 */ /* val=5 */ /* val=6 */ /* val=4 */ /* val=7 */

POINTERI DUBLI SI TABLOURI BIDIMENSIONALE


Un pointer catre un pointer la un anumit tip se numeste pointer dublu catre acel tip. Declaratia unui pointer dublu se face sub forma tip **var_pointer; Semnificatia acestei declaratii este urmatoarea: var_pointer poate memora adresa unui pointer catre tip.

63

n Figura 6.1 se prezinta schema acestui tip de adresare. Pointer Adresa Pointer Adresa Valoare

Figura 6.1 Adresare indirecta (pointer dublu) Exemplu: int **p,*q,x; x=10; q=&x; p=&q; Se observa ca pointerului q i se atribuie adresa variabilei x n timp ce pointerului p i se atribuie adresa pointerului q. Daca la aceasta secventa adaugam **p=7; efectul este ca valoarea lui x va deveni 7. Deci **p permite accesul indirect (indirectare dubla) la continutul variabilei x. Limbajul permite folosirea pointerilor tripli si n general a pointerilor multipli, dar apar dificultati la manevrarea corecta a unor astfel de pointeri. Pointerii dubli si tablourile de pointeri permit extinderea corecta a aritmeticii pointerilor prezentati si la cazul tablourilor multidimensionale. Se porneste de la observatia ca un tablou multidimensional este un tablou cu o singura dimensiune care are drept elemente un tablou cu restul de dimensiuni. Pentru usurinta expunerii sa consideram cazul unui tablou bidimensional (matrice), ca n exemplul de mai jos. Fie declaratia: int x[3][5]; Conform observatiei facute, tabloul x[3][5] poate fi privit ca un tablou cu trei variabile pointer x[0],x[1],x[2] care arata respectiv catre tablourile unidimensionale: x[0][0] x[0][1] ... x[0][4], x[1][0] x[1][1] ... x[1][4], si x[2][0] x[2][1] ... x[2][4]. Dar x[0],x[1],x[2], se pot scrie echivalent *x,*(x+1) si *(x+2), respectiv. Deci numele tabloului x poate fi vazut ca un pointer dublu catre tablouri de cte cinci locatii de tip int. Din acest motiv, avem echivalentele: x[i][j]<=>(*(x+i))[j]<=>*(*(x+i)+j), unde expresia x+i nseamna x+i* sizeof(int)*5 (conform proprietatii pointerilor catre tablouri unidimensionale). Alte echivalente evidente sunt x[i][j]<=>*(x[i]+j)<=>*(&x[0][0]+5*i+j) Observatie: Ultima expresie din echivalentele de mai sus contine functia de alocare a tabloului bidimensional x[3][5].

64

7. Structuri, Uniuni, Enumerari, Declaratii TYPEDEF


Structurile si uniunile reprezinta pentru programator doua posibilitati importante de a modela colectii de date eterogene din punct de vedere al tipului de date. Enumerarile permit crearea unor tipuri de date noi cu ajutorul unor liste de constante ntregi cu nume. Cu ajutorul declaratiilor typedef se pot modifica numele unor tipuri de date existente. n acest capitol se va arata cum se definesc tipurile de date struct, union si enum, cum se folosesc concret n aplicatii si cum se poate redenumi un tip de date deja definit.

STRUCTURI
Structurile reprezinta colectii de date neomogene grupate sub acelasi nume, ntr-o zona de memorie compacta. Declaratia unei structuri are forma generala: struct nume_structura { tip nume_camp1 ; tip nume_camp2 ; tip nume_camp3 ; ............. tip nume_campn ; }lista_variabile_structura; unde: nume_camp1,nume_camp2,,nume_campn sunt nume de cmpuri, membre ale structurii; ?? tip reprezinta tipul cmpului si poate fi orice tip simplu sau derivat admis n C; ?? nume_structura reprezinta numele structurii; ?? lista_variabile_structura este o lista de variabile al carei tip este struct nume_structura. Nume_structura si lista variabile_structura sunt optionale dar nu pot lipsi simultan: declaratia unei structuri anonime, care n plus are si lista_variabile_structura vida este absolut inutila.
??

Iata un exemplu de structura care reflecta un rnd din cartea de telefoane.

struct rand_tel { char nume[15]; char prenume[20]; char adresa[50]; unsigned long int telefon; }pers1,pers2; Aici, pers1 si pers2 sunt nume de variabile care vor avea tipul struct rand_tel. Aceasta declaratie este echivalenta cu: struct rand_tel { char nume[15]; char prenume[20]; char adresa[50]; unsigned long int telefon; }; struct rand_tel pers1,pers2; Selectarea unui cmp din structura se face utiliznd operatorul . sub forma: nume_variabila_structura.nume_camp De exemplu, accesul la informatia continuta n cmpul adresa al variabilei pers1 se face utiliznd constructia pers1.adresa Asupra unui cmp selectat se pot aplica toate operatiile care se pot aplica unei variabile de acelasi tip cu cmpul. Are sens, de exemplu, o secventa ca aceasta . . . . . . scanf(%d,&pers1.telefon); printf(\n Numarul de telefon este %d,pers1.telefon); . . . . . . Daca o variabila este un pointer catre o structura, accesul la un cmp al structurii se face utiliznd operatorul -> (alcatuit din semnul - si semnul >) sub forma: nume_pointer_structura->nume_camp De exemplu, daca avem declarat pointerul pers3 astfel, struct rand_tel *pers3; referirea la cmpul nume al acestei variabile se face sub forma pers3->nume Remarcam faptul ca operatorul -> este o sinteza a operatorilor * (indirectare) (selectie). Asadar, expresia pers3->nume este echivalenta cu expresia (*pers3).nume Marimea unei structuri se calculeaza utiliznd operatorul sizeof. De exemplu, marimea structurii rand_tel este data de sizeof(struct rand_tel). Este recomandata folosirea acestui operator si nu calculul manual al marimii structurii (nsumnd octetii ocupati de fiecare cmp) att din motive de portabilitate ct si din ratiuni de corectitudine: n unele situatii, compilatorul, prin alinierile pe care le face, obtine un necesar de memorie mai mare dect dimensiunea obtinuta manual. O variabila de tip structura poate fi initializata pe linia de declarare n maniera obisnuita: Declaratie structura={lista_valori_initiale}; si .

De exemplu, prin declaratia Struct rand_tel pers1={Dan,Ion,Ploiesti,str.Tei,165239}; cmpurile nume,prenume,adresa,telefon ale variabilei pers1, vor primi n ordine, valorile enumerate n lista. Copierea informatiei bit cu bit dintr-o structura n alta structura de acelasi tip este posibila printr-o singura instructiune de atribuire. Astfel, atribuirea pers1=pers2; este echivalenta cu strcpy(pers1.nume,pers2.nume); strcpy(pers1.prenume,pers2.prenume); strcpy(pers1.adresa,pers2.adresa); pers1.telefon=pers2.telefon; O structura poate fi inclusa n alta structura obtinndu-se structuri imbricate. De exemplu, se poate imagina structura: struct info_pers { struct rand_tel info; int varsta; float salariu; }angajat; Referirea la un cmp al structurii incluse se face urmnd modelul constructiei angajat.info.nume care indica numele unui angajat. n interiorul unei structuri numele cmpurilor trebuie sa fie diferite, nsa doua structuri diferite pot avea nume de cmpuri comune. De asemenea, att numele structurii ct si numele unui cmp al structurii pot coincide cu numele unei variabile. De exemplu, declaratiile: struct pers { int i; int j; }p; int pers; float i; . . . . . sunt perfect valide. Cmpurile unei structuri pot fi pointeri catre structura din care fac parte. Acest lucru permite construirea unor structuri recursive (autoreferite) foarte utile n implementarea dinamica a cozilor, stivelor, listelor, arborilor, grafurilor. Iata un exemplu de program care construieste o lista si apoi afiseaza elementele sale. Lista va fi construita element cu element de la sfrsit spre nceput.

Construirea unei liste si afisarea elementelor sale. #include "stdio.h" #include "conio.h" #include "alloc.h"/*

programul creaza si afiseaza o lista simplu inlantuita dintr-un sir de date cu marcatorul de sfirsit 0 */ void main(void) { int x; struct nod { int data; struct nod *leg; }*p,*q; q=NULL; printf("\nIntroduceti elementele listei\n\a"); printf("\nx="); scanf("%d",&x); while (x) { p=(struct nod *)malloc(sizeof(struct nod)); p->data=x; p->leg=q; q=p; printf("\nx="); scanf("%i",&x); } /* afiseaza elementele listei */ printf("\n Elementele listei sint \n\a"); p=q; while (p) { printf("%i ",(*p).data); p=p->leg; } getch(); }

Un tablou poate fi cmp al unei structuri asa cum se vede n exemplul urmator: struct anonima { float x[5][2]; int y; }a; Referirea la un element al tabloului se face dupa modelul a.x[i][j],0<=i<=4,0<=j<=1. De asemenea, componentele unui masiv de date pot fi de tip structura. n exemplul care urmeaza se prezinta un program care memoreaza o lista cu informatii despre notele obtinute de o grupa de studenti ntr-un vector de maxim 30 componente.

Un rnd din lista se descrie cu ajutorul structurii: struct catalog { char npr[30]; short nota; }stud[30]; Dupa memorare, lista este aranjata n ordinea alfabetica a numelor folosind un algoritm de sortare cunoscut (sortarea prin selectie).

Ordonarea unei liste folosind algoritmul de sortare prin selectie. #include "stdio.h" #include "conio.h" #include "string.h" void main(void) { short i,j,l,n; struct catalog { char npr[30]; short nota; }stud[30],studint; /* introducerea datelor */ printf("\n n="); scanf("%i",&n); for (i=0;i<n;i++) { printf("\n Nume="); scanf("%s",&stud[i].npr); printf("\n Nota="); scanf("%i",&stud[i].nota); } /* ordonarea alfabetica a catalogului */ for (i=0;i<n-1;i++) { l=i; for (j=i+1;j<n;j++) strcmp(stud[j].npr,stud[l].npr)<0 ? l=j : l; studint=stud[i]; stud[i]=stud[l]; stud[l]=studint; } /* afisarea catalogului ordonat */ printf("\n Catalogul ordonat este:\n"); for (i=0;i<n;i++) printf("\n %s %i",stud[i].npr,stud[i].nota);

getch(); }

CMPURI DE BITI n C exista posibilitatea sa accesam un bit din memorie prin intermediul cmpurilor de biti. Forma generala a declaratiei unui cmp de biti este: struct nume_structura { tip nume_camp1:lungime; tip nume_camp2:lungime; . . tip nume_campn:lungime; }lista_variabile; unde lungime reprezinta numarul de biti ai cmpului, iar tip poate fi unsigned sau signed. Dupa cum se observa, un cmp de biti este de fapt membrul unei structuri caruia i se precizeaza lungimea (numarul de biti). Din acest motiv, accesul la informatia continuta ntr-un cmp se face folosind operatorii consacrati . sau -> . Totusi, nu toate facilitatile oferite de structuri sunt valabile si n cazul special al cmpurilor de biti. De exemplu, nu se poate utiliza operatorul & pentru a lua adresa unui cmp. De asemenea, numele unui cmp nu poate fi numele unui tablou, iar ordinea cmpurilor este dependenta de implementare. Cmpurile de b si gasesc utilitatea n special n programarea de sistem (gestionarea iti registrilor hardware, constructia interfetelor cu dispozitivele de intrare/iesire etc.) sau pentru a face economie de memorie. De exemplu, sa consideram un dispozitiv cu 6 componente. Daca fiecarei componente i atasam un bit de stare (1 functioneaza, 0 nu functioneaza) starea ntregului dispozitiv poate fi descrisa prin structura: struct stare_disp { unsigned comp1:1; unsigned comp2:1; unsigned comp3:1; unsigned comp4:1; unsigned comp5:1; unsigned comp6:1; }stare; Starea componentei 4, de pilda, va fi furnizata de stare.comp4. Avantajul folosirii unei astfel de structuri este evident: n loc sa se foloseasca 6 octeti pentru codificare, se foloseste doar un octet. Daca anumiti membri ai unui cmp de biti nu ne intereseaza, pot fi sariti fara a le da nume. De exemplu, daca ne intereseaza numai bitul 4, se poate folosi urmatoarea forma simplificata: struct stare_disp { unsigned:3; unsigned comp4:1; }stare;

Sa observam ca bitii comp5 si comp6 au fost omisi, lucru acceptat de compilator. O structura poate contine att cmpuri obisnuite, ct si cmpuri de biti ca n exemplul de mai jos: struct stare_student { char nume[40]; unsigned nota:4; unsigned adm:1; /*admis sau respins*/ unsigned restante:3; /*numar de restante*/ }student;

UNIUNI
O uniune este o structura de date care permite folosirea n comun a aceleiasi zone de memorie de doua sau mai multe variabile diferite, la momente de timp diferite. Forma generala de declarare a unei uniuni este: union nume_uniune { tip nume_camp1; tip nume_camp2; tip nume_camp3; . . . . . . . tip nume_campn; }lista variabile_uniune; Dupa cum se poate constata forma generala de declarare a unei uniuni este asemanatoare cu cea a unei structuri. Precizarile facute la uniuni referitoare la relatiile dintre numele uniunii, numele cmpurilor, numele variabilelor_uniune si numele oricarei variabile ramn valabile si aici. Pentru selectarea cmpurilor putem folosi, de asemenea, operatorii . si ->. Operatorul sizeof aplicat tipului de date union, adica sizeof(union nume_uniune) va furniza lungimea uniunii. Aceasta este mai mare sau egala cu lungimea celui mai mare cmp al uniunii. Deosebirea fundamentala dintre o uniune si o structura consta nsa n felul n care cmpurile folosesc memoria. La o structura, zonele de memorie rezervate cmpurilor sunt diferite pentru cmpuri diferite. La o uniune, toate cmpurile din uniune mpart aceeasi zona de memorie. Din acest motiv, datele memorate ntr-o uniune pot fi referite diferit, functie de tipul membrului pe care-l avem n vedere. Astfel, folosind secventa . . . . . . union una { int i; float f; }v; datele din variabila v vor fi privite ca ntregi, daca selectam v.i sau reale, daca selectam v.f. Cmpurile i si f se refera la aceeasi adresa: n v.i se memoreaza sizeof(int) octeti care ncep la aceasta adresa, iar n v.f, sizeof(float) octeti care ncep la aceeasi adresa.

Folosirea memoriei se face conform urmatoarei schite:

v. i

v. f

Figura 7.1. Modul de folosire a memoriei de catre cmpurile uniunii una Este posibila initializarea unei uniuni cu o constanta care trebuie sa aiba tipul primului cmp declarat. De exemplu, secventa de program union { int i; float f; }x={324}; va avea ca efect initializarea primului cmp (x.i=324). Folosind uniunile si cmpurile de biti se poate accesa cu usurinta informatia la nivel de bit a unei variabile.

Afisarea primului si ultimului bit din codul binar al unui caracter introdus de la tastatura. #include "stdio.h" #include "conio.h" void main(void) { struct biti { unsigned prim :1; unsigned :6; unsigned ultim:1; }; union char_bit { char x; struct biti y; }tasta; printf("\n Tastati un caracter: "); tasta.x=getche(); if (tasta.y.prim) printf("\n Primul bit are valoarea 1"); else printf("\n Primul bit are valoarea 0"); if (tasta.y.ultim)

printf("\n Ultimul bit are valoarea 1"); else printf("\n Ultimul bit are valoarea 0"); getch(); }

Pentru interpretarea corecta a informatiei dintr-o uniune, de obicei, se pastreaza ntr-o variabila ajutatoare informatii privind continutul curent al uniunii. Pentru ilustrare am ales urmatoarul exemplu.

Se doreste crearea unei liste cu rezultatele sesiunii de examene pentru o grupa cu un numar de 30 de studenti. Pentru verificarea corectitudinii informatiilor memorate, programul trebuie sa contina si afisarea listei. Se presupune ca un student, la sfrsitul sesiunii, poate fi ntr-una din situatiile urmatoare: ?? a luat toate examenele si i s-a putut ncheia o medie pe sesiune; ?? a luat toate examenele (sa presupunem ca acestea sunt n numar de cinci), dar nu este ncheiata media; ?? a picat cel putin un examen. Solutia problemei este urmatoarea: lista va fi creata n tabloul stud de 30 componente. Fiecare componenta va fi o structura (numita catalog) care va contine: ?? cmpul npr pentru memorarea numelui si prenumelui studentului (40 de caractere); ?? cmpul tip (un caracter) unde aflam informatii despre continutul curent al uniunii care urmeaza; ?? uniunea sit cu urmatoarele cmpuri: ?? media studentului, cnd cmpul tip are valoarea m; ?? vectorul note[5], cu cele 5 note de la examene, cnd cmpul tip are valoarea e; ?? un pointer catre prima componenta a unei liste liniare simplu nlantuita, care contine numele disciplinelor restante, cnd cmpul tip are valoarea r. #include #include #include #include "stdio.h" "conio.h" "string.h" "alloc.h"

void main(void) { char disci[10],ch; int i,k,n; typedef struct lista { char disc[10]; int noter; struct lista *leg; }list; list *res; struct catalog

{ char npr[30]; char tip; union { int nota; int note[5]; struct lista *resta; }var; }stud[30]; /* introducerea datelor */ printf("\nN="); scanf("%i",&n); for (i=0;i<n;++i) { printf("\nNume Prenume="); scanf("%s",&stud[i].npr); do { printf("\nTip informatie (m,e,r)="); ch=getche(); } while (ch != 'm' && ch != 'e' && ch != 'r'); stud[i].tip=ch; switch (stud[i].tip) { case 'm':printf("\nMedia = "); scanf("%i",&stud[i].var.nota); break; case 'e':printf("\nIntroduceti notele \n"); for (k=0;k<5; k++) { printf("\nNota[%i]=",k); scanf("%d",&stud[i].var.note[k]); } break; case 'r':res=NULL; printf("\nDiscipline restante si note;"); printf(" pentru terminare tastati stop "); printf("\n Disciplina ="); scanf("%s",disci); while (strcmp(disci,"stop")) { stud[i].var.resta=(list*)malloc(sizeof(list)); strcpy(stud[i].var.resta->disc,disci); printf("\n Nota restanta="); scanf("%d",&stud[i].var.resta->noter); stud[i].var.resta->leg=res; res=stud[i].var.resta; printf("\n Disciplina ="); scanf("%s",disci); } break; } /* end switch*/ /* end for*/

/* afisarea informatiei */ for (i=0;i<n;i++) { printf("\nNume Prenume=%s",stud[i].npr); switch(stud[i].tip) { case 'm':printf("\n Media la examene=%i",stud[i].var.nota); break; case 'e':printf("\n Notele la examen sunt: \n"); for (k=0;k<5;k++) printf("\n Nota[%i]=%i",k,stud[i].var.note[k]); break; case 'r':printf("\n Disciplinele restante sunt: \n"); res=stud[i].var.resta; while(res) { printf("\n Disciplina=%s",res->disc); printf("\n Nota restanta = %d", res->noter); res=res->leg; } break; } /* end switch */ } /* end for */ getch(); }

ENUMERARI
O enumerare reprezinta o lista de constante ntregi cu nume. Forma generala a unei enumerari este: enum nume_enumerare{lista_enum}lista_de_variabile; unde nume_enumerare si lista-enum sunt optionale. Lista_enum defineste toate valorile pe care le pot lua variabilele din lista_de_variabile. n exemplul urmator enum culoare{rosu,alb,verde,albastru}culoarea_mea; variabila culoarea_mea poate lua oricare din valorile rosu,alb,verde,albastru. Este deci valida atribuirea: culoarea_mea=albastru; O data enumerarea definita, putem sa ne referim la ea sub forma

enum nume_enumerare lista_de_variabile; De exemplu, constructia enum culoare culoarea_ta; defineste o noua variabila de tip culoare. Simbolurile din lista_enumerare reprezinta valori ntregi, conform regulei: valoarea primului simbol din enumerare este 0, valoarea celui de-al doilea simbol este 1 etc. n exemplul prezentat, rosu are valoarea 0, alb valoarea 1, verde valoarea 2, iar albastru valoarea 3. Aceste valori pot fi nsa impuse, ca n exemplul urmator: enum zile_sapt{luni=1,miercuri=3,joi,vineri, duminica=7}zi; Simbolul care nu este initializat va primi o valoare cu o unitate mai mare dect valoarea simbolului precedent. Astfel, joi are valoarea 4, iar vineri valoarea 5. Simbolurile din lista de enumerare nu pot fi citite de la tastatura sau afisate pe ecran n mod direct. Este deci incorecta o secventa de forma: zi=vineri; printf(\n ziua este %s,zi); unde se trateaza un simbol din lista, ca un sir de caractere. n ciuda acestei restrictii enumerarile se folosesc n programe pentru plusul de claritate pe care-l aduc. Este mai sugestiv, de exemplu, sa folosim simbolurile luni,miercuri,joi,vineri, duminica dect, respectiv, codurile 1,3,4,5,7.

Afisarea unui simbol din lista de enumerare cu ajutorul sirurilor si a unei structuri switch. #include "stdio.h" #include "conio.h" void main(void) { enum culoare{rosu,alb,verde,albastru} culoarea_mea; culoarea_mea=alb; switch (culoarea_mea) { case rosu:printf("\n rosu"); break; case alb:printf("\n alb"); break; case verde:printf("\n verde"); break; case albastru:printf("\n albastru"); break; } getch(); }

DECLARATII TYPEDEF
Declaratia typedef permite asocierea unui nou nume unui tip de date deja definit. Forma generala pentru typedef este: typedef tip nume_nou; unde tip reprezinta un tip de date valid (predefinit sau definit de utilizator), iar nume_nou este noul nume asociat. Accentuam faptul ca typedef nu creeaza tipuri de date noi, ci doar redenumeste tipuri existente. Exemplu: typedef short int scurt_in; typedef struct { float re; float in; }complex; typedef enum{false,true}boolean; n aceste conditii au sens declaratiile, definitiile si atribuirile de variabile: . . . . . . scurt_in i; complex z; boolean b=false; i=321; z.re=0.73; z.in=-72.9; Facem precizarea ca numele initial al tipului poate fi folosit n continuare alaturi de tipurile nou definite. De exemplu, declaratiile: short int j; scurt_in i; sunt compatibile. De asemenea, se pot folosi mai multe declaratii typedef pentru a crea diferite nume noi pentru acelasi tip, ca n exemplul urmator: typedef int integer; typedef integer intreg; typedef intreg intr; Utilitatea declaratiei typedef consta, n primul rnd, n aportul de claritate pe care-l poate aduce unui program. Folosirea ei este recomandata mai ales pentru tipuri care se reprezinta mai complicat (de exemplu tipurile struct, union, enum). Exemplele date sunt edificatoare n acest sens. n al doilea r nd, typedef este recomandata pentru a mbunatatii portabilitatea unui program. Daca anumite tipuri de date sunt dependente de calculatorul pe care se ruleaza programul, este bine ca acestea sa primeasca un nume nou cu ajutorul typedef. La o compilare pe alt tip de calculator se vor modifica eventual numai aceste declaratii typedef. De exemplu, considernd ca pe un calculator unde ntregii se reprezinta pe 16 biti, am declarat typedef int intreg; trecerea pe un calculator unde ntregii se reprezinta pe 32 biti se va face modificnd doar declaratia ntregilor astfel:

typedef short int intreg; Deoarece short int pe ultimul calculator se reprezinta pe 16 biti, variabilele declarate de tip intreg vor avea aceeasi lungime pe ambele tipuri de calculatoare.

8. Functii
Notiunea de functie ocupa n limbajul C un loc central. Dupa cum stim, un program C poate fi privit ca un ansamblu de functii situate la acelasi nivel. Acest lucru nseamna ca, spre deosebire de Pascal, nu se poate defini o functie n interiorul alteia. Consecinta majora consta n posibilitatea de comunicare ntre oricare doua functii ale programului. Functia main() este obligatorie n orice program: executia unui program ncepe cu o instructiune din main() si sfrseste cu o instructiune din main(). Pentru a usura ntelegerea elementelor de limbaj prezentate pna acum, toate exemplele au avut n vedere programe alcatuite dintr-o singura functie, functia main(). n aceasta sectiune vom prezenta elementele care ne vor permite sa construim un program din doua sau mai multe functii. O functie poate fi definita sau declarata. Definitia unei functii cuprinde antetul functiei (declaratorul) si corpul functiei. Declararea unei functii se refera doar la elemente din antetul ei.

DEFINITIA FUNCTIILOR n limbajul C exista doua forme de definire a unei functii: forma moderna si forma clasica. Desi standardul ANSI C recomanda folosirea formei moderne, totusi, din motive de portabilitate, standardul accepta si forma clasica de definire a functiilor. Acest lucru permite rularea unor programe vechi pe compilatore noi sau chiar a unor programe noi pe compilatoare mai vechi. n versiunea moderna definitia unei functii are forma generala: tip nume_functie(lista_declaratii_parametri) { corpul functiei } Specificatorul tip reprezinta tipul rezultatului ntors de functie si poate preciza orice tip de date acceptat n C mai putin tipul tablou. Din acest punct de vedere functiile se pot clasifica n: ?? functii care ntorc un rezultat; ?? functii care nu ntorc un rezultat. Functiile care ntorc un rezultat se pot clasifica n: ?? functii care ntorc valori ntregi; ?? functii care nu ntorc valori ntregi. Functiile care specifica n mod explicit ca tipul lor este int ntorc, evident, valori ntregi. De asemenea, valori ntregi ntorc si functiile declarate char sau cele pentru care specificatorul tip lipseste (conventia implicita). Clasificarea facuta are urmatoarea motivatie: asa cum se va vedea n sectiunea urmatoare, pentru buna functionare a programului, functiile care nu ntorc valori ntregi si cele care nu ntorc un rezultat trebuie definite sau declarate nainte de a fi apelate. Pentru o functie care nu ntoarce nici un rezultat tipul specificat obligatoriu trebuie sa fie void. Daca tip lipseste, asa cum am precizat, tipul considerat implicit este int. De altfel, tipul void este introdus de standardul C tocmai pentru a nlatura confuzia dintre functiile care ntorc implicit valori ntregi si functiile care nu ntorc un rezultat. Precizarea tipului void previne folosirea incorecta, n expresii, a functiilor care nu ntorc rezultate. Functia main() returneaza catre sistemul de operare care o apeleaza, o valoare ntreaga (de obicei 0 daca programul nu a avut erori si o valoare diferita de 0, n caz contrar). Daca totusi, n implementarea folosita, main() nu returneaza valori, atunci tipul ei se va declara void. Lista_declaratii_parametri cuprinde enumerarea parametrilor formali (sau fictivi) ai functiei, fiecare parametru fiind precedat de tipul sau. Cele doua paranteze rotunde care ncadreaza lista_declaratii_parametri trebuie sa existe obligatoriu chiar si n situatia n

care lista este vida. Mai mult, recomandarea este ca precizarea unei liste vide de parametri sa se faca prin scrierea cuvntului void ntre cele doua paranteze rotunde. Corpul functiei, mpreuna cu acoladele formeaza o instructiune compusa, ce poate contine instructiuni si eventuale declaratii de tipuri de date si variabile utilizate de aceste instructiuni. Pentru revenirea n functia apelanta se foloseste n mod normal instructiunea de salt return. Forma generala a acestei instructiuni este: return expresie; unde expresie poate sa lipseasca. Este evident ca o functie de tip void nu trebuie sa contina o instructiune return care specifica o expresie. Daca totusi se ncearca returnarea valorii date de o expresie, valoarea respectiva ramne nedefinita. n concluzie, o functie de tip void trebuie sa contina cel mult instructiunea return fara precizarea expresiei. Daca nu exista nici o instructiune return n corpul functiei, revenirea n functia apelanta se face automat dupa executarea ultimei sale instructiuni. Utilizarea instructiunii return, cu precizarea expresiei, este obligatorie n functiile care ntorc o valoare (nu sunt de tip void), iar valoarea expresiei este tocmai valoarea ntoarsa. Aceasta valoare trebuie sa fie compatibila cu rezultatul ntors de functie, iar daca este cazul, ntocmai ca la operatia de atribuire, se fac conversiile necesare. Daca la astfel de functii instructiunea return lipseste, valoarea ntoarsa de functie e nedefinita. Daca instructiunea return exista, nsa fara precizarea expresiei, se presupune ntoarcerea unei valori inutile. n corpul unei functii pot fi mai multe instructiuni return, adica se poate reveni n programul apelant din orice punct al functiei. Valoarea ntoarsa de functie poate sa fie folosita sau nu n prelucrari, nefolosirea ei nefiind o eroare. Exemple de definire a unor functii n varianta moderna: float maxim(float x,int y) { if(x<y) return y; else return x; } void mesaj1(int x) { if(x) printf(\n Ati tastat bine!); else printf(\n Ati tastat gresit!); } void mesaj2(void){ printf(\n Intrerupeti executia programului!); } Forma generala a unei definitii clasice pentru o functie este: tip nume_functie(par1,par2,...,parn) tip par1; . . . . . tip parn; { instructiuni }

Se observa ca, n forma clasica, declaratia parametrilor nu se mai face n antetul functiei. Prima functie din exemplul precedent poate fi rescrisa n forma clasica astfel: float maxim(x,y) float x; int y; { if(x<y) return y; else return x; } Spre deosebire de cazul definitiei moderne, unde fiecare parametru din lista este precedat de tipul sau indiferent daca exista parametri de acelasi tip, definitia clasica permite declararea tipului comun al parametrilor o singura data. De exemplu, secventa de program: minim(int x,int y,int z) { instructiuni } se scrie echivalent n forma clasica minim(x,y,z) int x,y,z; { instructiuni }

DECLARAREA FUNCTIILOR, PROTOTIPURI Orice functie care returneaza valori de un tip diferit de tipul int trebuie declarata sau definita nainte de a fi apelata. Declaratiile trebuie facute la nceputul programului (nainte de definirea oricarei functii) pentru a informa compilatorul asupra tipului rezultat si a putea genera un cod corect. Deoarece verificarile de tip se fac n faza de compilare (si nu mai trziu la linkeditare sau executie) absenta declaratiei functiei, nainte de a fi apelata, conduce fie la eroare de compilare - daca functia de unde se face apelul si functia apelanta sunt n acelasi fisier program, fie la rezultate imprevizibile - daca cele doua functii n cauza sunt n fisiere diferite. Exista doua modalitati de a declara o functie: forma traditionala si forma moderna (prototip de functie). Forma traditionala este specifica fazei de nceput a evolutiei limbajului C. Structura unei astfel de declaratii este urmatoarea: tip nume_functie();

Programul calculeaza aria unui cerc.

#include "stdio.h" #include "conio.h" /* declaratia functiei aria */ double aria(); void main(void) { float r; printf("\n Raza cercului="); scanf("%f",&r); printf("\n Aria cercului este %lf",aria(r)); getch(); } /* definitia functiei aria */ double aria(float r) { const double pi=3.14159265; return pi*r*r; }
Dupa cum se poate constata, n forma traditionala a declaratiei unei functii lipseste orice referire la parametrii ei. Forma moderna de declarare a unei functii, numita prototipul functiei, constituie o extensie a declaratiei clasice. Prototipul unei functii are forma: tip nume_functie (lista_declaratii_parametri); Lista_declaratii_parametri poate sa fie vida, caz n care ntre parametri se va scrie cuvntul void, sau poate sa contina numai lista tipurilor parametrilor. Utilitatea ei consta n faptul ca permite compilatorului sa verifice corectitudinea apelului unei functii: se fac verificari privind identitatea dintre numarul parametrilor prototipului si al parametrilor actuali, iar daca este necesar, parametrii actuali se convertesc la tipul parametrilor corespunzatori din prototip, nainte de a fi plasati n stiva. Exemple de prototipuri de functii: float f(int x, float y); double comutare(float, float); void afisare(int y); int *g(float *p); char *strcat(char *sir1, char *sir2); Ultimul exemplu pune n evidenta contributia prototipurilor la cresterea lizibilitatii codului: functia strcat() concateneaza sirurile sir1 si sir2, rezultatul ntors fiind tot un sir. Sa observam, de asemenea, ca al doilea exemplu de prototip are lista_declaratii_parametri formata doar din tipurile parametrilor. Utilitatea folosirii si a numelor de parametri consta n referirea lor n eventualele mesaje de avertisment, ceea ce nlesneste depanarea programului.

Utilizarea prototipurilor de functii

#include "stdio.h" #include "conio.h" #include "math.h" /* prototipul functiei */ float f(float); void main(void) { float y,x; printf("\n x="); scanf("%f",&x); y=f(x)+2.5; printf("\n y=%f",y); getch(); } /*definitia functiei f */ float f(float x) { if (x>0) return exp(x)+1.45; else return sqrt(-x)+2.3; }
Limbajul C permite existenta functiilor cu un numar variabil de parametri. Exemplul tipic pentru astfel de functii l reprezinta functiile scanf() si printf(). Prototipul unei functii cu un numar variabil de parametri are forma: tip nume_functie(lista_declaratii_parametri...); unde: lista_declaratii_parametri contine cel putin o declaratie de parametru. Evident si declaratorul din definitia functiei trebuie sa fie de aceeasi forma. Declaratia float f(int x,float y...); reprezinta prototipul care are cel putin doi parametri, parametrul x si parametrul y. Desi prototipurile nu sunt (nca) obligatorii n C (dar obligatorii n C++) avantajele utilizarii lor n programe le recomanda din plin.

APELUL FUNCTIILOR Apelul unei functii se face sub forma: nume_functie(lista_parametri) unde:
?? ??

nume_functie este numele functiei care se apeleaza; lista_parametri reprezinta lista de valori actuale sau efective care vor fi transmise functiei. Din acest motiv, parametrii din lista-parametri se mai numesc actuali sau efectivi.

La apelul functiei, ntre parametrii actuali si parametrii formali se face o corespondenta pozitionala. Parametrii actuali si formali situati pe pozitii identice trebuie sa aiba tipuri identice sau compatibile. Daca functia apelata nu are prototip, din ratiuni care tin de definirea initiala a limbajului C, se fac implicit urmatoarele conversii de tip: char si short la int, float la double, tablou la pointer. Daca functia are prototip, tipurile precizate n prototip ramn neschimbate. n acest caz, compilatorul este de cele mai multe ori n masura sa sesizeze prompt orice ncercare de conversie nepermisa ntre tipul parametrilor actuali si tipul parametrilor formali corespunzatori. De asemenea, compilatorul va sesiza lipsa de identitate dintre numarul parametrilor actuali si formali. Conversiile permise aici sunt cele considerate la operatiile de atribuire. Transferul informatiei ntre functia apelanta si functia apelata se poate face prin valoare sau prin referinta. Transferul prin valoare consta n copierea valorii parametrului actual n zona de memorie a parametrului formal corespunzator n momentul efectuarii apelului. Practic, la apelul functiei, valoarea parametrului actual va fi depusa n stiva (stack) odata cu variabilele locale ale functiei si cu adresele de revenire n functia apelanta. Daca functia apelata are prototip, valoarea parametrului actual se converteste la tipul parametrului formal corespunzator indicat n prototip (exact ca n cazul unei atribuiri). n cazul unei incompatibilitati ntre tipuri se semnaleaza eroare. Daca functia nu are prototip (lucru nerecomandat), la apelare, tipurile char si short sunt convertite la int, float la double, iar tablou la pointer. Sa remarcam faptul ca, n cazul transferului prin valoare, valorile parametrilor actuali nu sunt afectate de prelucrarile din corpul functiilor apelate: aceste prelucrari v izeaza copii ale parametrilor actuali si nu parametrii nsisi. Pentru ca functia apelata sa poata modifica valoarea unei variabile indicata ca parametru actual trebuie sa i se cunoasca adresa sa. Solutia este ca parametrul formal corespunzator sa fie declarat pointer si sa i se transmita la apel adresa variabilei si nu valoarea. Acest mod de transfer se cheama transfer prin referinta. n scopul ilustrarii celor doua modalitati de transfer, prin valoare si prin referinta, prezentam mai jos un exemplu clasic: comutarea a doua elemente utiliznd doua functii, functia comut1() si functia comut2().

Comutarea a doua valori

#include "stdio.h" #include "conio.h" void comut1(int x,int y); void comut2(int *x,int *y); void main(void) { int a,b;

a=3; b=5; comut1(a,b); printf("\nRezultate comut1 a=%d,b=%d",a,b); comut2(&a,&b); printf("\nRezultate comut2 a=%d,b=%d",a,b); getch(); } void comut1(int x,int y) { int z=x; x=y; y=z; } void comut2(int *x,int *y) { int z=*x; *x=*y; *y=z; }
Rezultatul executiei programului este: apel comut1() apel comut2() a=3 a=5 b=5 b=3

Pentru o mai buna ntelegere a modului de lucru a celor doua functii, prezentam urmatoarea schita care reflecta situatia zonelor de memorie dupa apelul functiilor comut1() si comut2().

Transfer prin valoare


main()

Transfer prin referin\[

comut1()

main()

comut2()

a=3 b=5

x=5 y=3

a b

? 5 ? 3

? x ? y

Sa observam ca, datorita modului de transfer prin valoare, prelucrarile din functia comut1() nu afecteaza variabilele a si b care ramn neschimbate (necomutate). Comutarea este realizata de functia comut2(). Aici, prelucrarile din functie se refera direct la valorile continute n a si b.

TRANSFERUL TABLOURILOR CATRE FUNCTII O exceptie de la regula transmiterii prin valoare o reprezinta cazul cnd parametrul actual este un tablou. Deoarece copierea valorilor componentelor tabloului ntr-un parametru formal ar fi constituit, n general, un consum mare de timp s-a adoptat solutia transmiterii catre functie doar a adresei de nceput a tabloului. n consecinta, parametrul transmis poate fi numele tabloului care este, asa cum am vazut n Capitolul 6 un pointer constant catre primul element din tablou. , Parametrul formal corespunzator poate fi declarat ntr-una din modalitatile urmatoare: ?? Este declarat ca tablou cu dimensiunea egala cu dimensiunea tabloului transmis; ?? Este declarat ca tablou fara a da dimensiunea primei componente; ?? Este declarat ca pointer. Prezentam mai jos exemple care sa ilustreze cele trei cazuri. Exemple:

Calcularea sumei elementelor unei matrici a(4x3). Varianta 1 parametrul formal este o matrice de dimensiune 4x3

#include "stdio.h" #include "conio.h" suma(int x[4][3]); void main(void) { int a[4][3],i,j,s; /* citirea elementelor matricii a */ for (i=0;i<4;i++) for (j=0;j<3;j++)

{ printf("\n a[%i][%i]=",i,j); scanf("%d",&a[i][j]); } /* calculul sumei elementelor matricii a folosind functia suma() */ s=suma(a); /* afisarea sumei */ printf("\n Suma=%d",s); getch(); } suma(int x[4][3]) {

int i,j,s=0; for (i=0;i<4;i++) for (j=0;j<3;j++) s+=x[i][j]; return s; }

Comentariu. Chiar daca x este un parametru formal de tip matrice, compilatorul C l converteste automat la un pointer catre ntregi. Aceasta varianta este de o rigiditate maxima n ceea ce priveste dimensiunile matricii prelucrate.

Calcularea sumei elementelor unei matrici a(4x3). Varianta 2 parametrul formal este o matrice cu prima dimensiune neprecizata si a doua egala cu 3

#include "stdio.h" #include "conio.h" suma(int x[][3],int m,int n); void main(void) { int a[4][3],i,j,m,n,s; /* citirea valorilor m si n */ do { printf("\n m="); scanf("%i",&m); printf("\n n="); scanf("%i",&n); } while (m<1 || m>4 || n<1 || n>3); for (i=0;i<m;i++) for (j=0;j<n;j++) { printf("\n a[%i][%i]=",i,j); scanf("%d",&a[i][j]); } /* calculul sumei elementelor matricii a folosind functia suma() */

s=suma(a,m,n ); /* afisarea sumei */ printf("\n suma=%d",s); getch(); } suma(int x[][3],int m,int n) { int i,j,s=0; for (i=0;i<m;i++) for (j=0;j<n;j++) s+=x[i][j]; return s; }
Comentariu. Se observa ca functia suma() poate prelucra matrici pentru care prima dimensiune m este oarecare, iar a doua dimensiune n satisface conditia 0<=n<=2. Deoarece pentru a adresa elementul x[i][j] compilatorul foloseste relatia &x[i][j]=&x[0][0]+(i*3+j)*sizeof(int) este clar ca, la compilare, cea de-a doua dimensiune a matricii trebuie sa fie cunoscuta (n cazul nostru e egala cu 3). n consecinta, un declarator al functiei de forma suma(int x[][],int m,int n) este gresit.

Calcularea sumei elementelor unei matrici a(4x3). Varianta 3 parametrul formal este un pointer dublu.

#include "stdio.h" #include "conio.h" suma(int **y,int m,int n); void main(void) { int a[4][3],i,j,m,n,s; /* citirea valorilor m si n */ do { printf("\n m="); scanf("%i",&m); printf("\n n="); scanf("%i",&n); } while (m<1 || m>4 || n<1 || n>3); /*citirea elementelor matricii*/ for(i=0;i<m;i++) for(j=0;j<n;j++) { printf("\n a[%i][%i]=",i,j); scanf("%d",&a[i][j]); } /* calculul sumei elementelor matricii a folosind functia suma() */

s=suma(a,m,n); /* afisarea sumei */ printf("\n Suma=%d",s); getch(); } suma(int **y,int m,int n) { int i,j,s=0; for (i=0;i<m;i++) for(j=0;j<n;j++) s += ((int*)y)[n*i+j]; return s; }

Comentariu. Aceasta varianta cu pointeri permite folosirea unei functii care poate nsuma elementele unei matrici de dimensiuni oarecare. Functia primeste adresa de nceput a matricii prin intermediul pointerului dublu y. Prin conversia explicita (int*)y, pointerul y este vazut ca un pointer catre int, fapt ce permite referirea elementelor tabloului ca n cazul unui tablou unidimensional. n consecinta, referirea la a[i][j] se poate face, simulnd functia de alocare, sub forma ((int*)y)[n*i+j] Introducnd variabila auxiliara z, functia suma() se poate scrie mai clar sub forma: suma(int **y,int m,int n) { int i,j,s=0; int *z=(int*)y; for (i=0;i<m;i++) for(j=0;j<n;j++) s+=z[n*i+j]; return s; } Evident, instructiunea s+=z[n*i+j]; se putea scrie si sub forma s+=*(z+n*i+j); n locul pointerului dublu y se poate folosi un pointer catre tablou. n acest caz prototipul functiei se va scrie sub forma suma(int(*y)[],int m,int n); iar functia va fi suma(int(*y)[],int m,int n); { int i,j,s=0; int *z=(int*)y; for (i=0;i<m;i++) for(j=0;j<n;j++) s+=z[n*i+j]; return s; } Observatii. Un tablou transmis ca parametru catre o functie poate fi modificat prin instructiunile functiei. Acest lucru este posibil deoarece, la apelul functiei, nu se executa o copie a tabloului, ci se transmite doar adresa primei sale locatii. n exemplul de mai jos functia sortv() ordoneaza crescator vectorul a[n] 1<=n<=20, folosind algoritmul de sortare prin selectie. Ordonarea crescatoare a unui vector utiliznd algoritmul de sortare prin selectie.

#include "stdio.h" #include "conio.h"

void sortv(int x[],int n); void citv(int x[],int n); void afisv(int c[],int n); void main(void) { int x[20],n; do { printf("\n n="); scanf("%d",&n); } while (n<1 || n>20); citv(x,n); sortv(x,n); afisv(x,n); getch(); } void sortv(int x[],int n) { int i,k,min,l; for (i=0;i<n-1;i++) { min=x[i]; l=i; for (k=i+1;k<n;k++) if (x[k]<min) { l=k; min=x[k]; } x[l]=x[i]; x[i]=min; } } void citv(int x[],int n) { int i; for (i=0;i<n;i++) { printf("x[%i]=",i); scanf("%d",&x[i]); } } void afisv(int x[],int n) { int i; for (i=0;i<n;i++) printf("\n x[%i]=%d",i,x[i]); }

Trebuie retinut faptul ca doar transmiterea tabloului n ntregime este o exceptie de la regula de transmitere a parametrilor prin valoare. Daca se transmite doar o componenta, aceasta este tratata n mod obisnuit, ca o variabila simpla. Functia par_imp(), din exemplul de mai jos, decide daca o componenta oarecare i a tabloului de numere ntregi a[7] este para sau impara.

Testarea paritatii unei componente oarecare a tabloului a

#include "stdio.h" #include "conio.h" void par_imp(int x,int i); void main (void) { int a[7]={31,30,15,42,26,17,19},i; do { printf("\n Tastati 0,1,2,3,4,5 sau 6 \n"); printf("\n i="); scanf("%d",&i); } while (i<0 || i>6); par_imp(a[i],i); getch(); } void par_imp(int x,int i) { if (x%2) printf("\n Componenta %i este else printf("\n Componenta %i este }

impara",i); para",i);

TRANSFERUL STRUCTURILOR CATRE FUNCTII Spre deosebire de tablouri, structurile se pot transmite n ntregime unei functii daca sunt declarate drept parametrii valoare. Daca structurile au dimensiuni mari poate apare inconvenientul suprancarcarii stivei si al cresterii timpului de rulare. n aceasta situatie se poate folosi metoda transmiterii prin referinta, prin care se furnizeaza functiei doar adresa structurii si nu structura nsasi. Dupa cum se stie, se pot face, prin intermediul functiei, modificari asupra membrilor structurii.

Se initializeaza o variabila structura n functia main() si se afiseaza cu ajutorul functiei de afisare afis(). Transmiterea se face prin valoare.

#include "stdio.h" #include "conio.h" typedef struct { char *nume; int nota; }CATALOG; void afis(CATALOG y);

void main(void) { CATALOG x={"Ion",10}; afis(x); getch(); } void afis(CATALOG y) { printf("\n %s are nota %i",y.nume,y.nota); }

Observatie. Definirea tipului structura CATALOG se face n afara oricarei functii (are un caracter global) tocmai pentru a putea fi vazuta din oricare din cele doua functii main() sau afis(). Despre caracterul global sau local al declaratiilor de variabile se vor prezenta detalii n Capitolul 9.

Se initializeaza o variabila structura n main() si se face modificarea unui cmp de-al ei n functia modi(). Transmiterea se face prin referinta.

#include "stdio.h" #include "conio.h" typedef struct { char *nume; int nota; }CATALOG;

void modi(CATALOG *y); void afis(CATALOG y); void main(void) { CATALOG x={"Vasile",8}; printf("\n Mesaj initial: "); afis(x); modi(&x); printf("\n Mesaj modificat: "); afis(x); getch(); } void modi(CATALOG *y) { char *nume_nou; printf("\n Modificati numele?(D/N)"); if (toupper(getche())=='D') { printf("\n Nume Nou="); scanf("%s",nume_nou); strcpy(y->nume,nume_nou); } printf("\n Modificati nota?(D/N)"); if (toupper(getche())=='D') { printf("\n Nota Noua="); scanf("%i",&y->nota); } } void afis(CATALOG y) { printf("%s are nota=%i",y.nume,y.nota); }
Observatii:
??

Functia toupper() are forma generala int toupper(int c);

si are ca efect convertirea literei c n litera mare, daca e cazul. ?? Functia care converteste o litera mare n litera mica este tolower() si are forma generala: int tolower(int c);
??

n situatia n care argumentul nu este litera, cele doua functii lasa caracterul c neschimbat.

Oricare din membrii unei structuri poate fi transmis unei functii, fie prin valoare, fie prin referinta.

Functiei afis_val() i se furnizeaza cmpul nota din structura CATALOG, prin valoare, iar functiei afis_ref(), prin referinta.

#include "stdio.h" #include "conio.h" typedef struct { char *nume; int nota; }CATALOG; void afis_val(int y); void afis_ref(int *y); void main(void) { CATALOG x={"Gheorghe",7}; afis_val(x.nota); afis_ref(&x.nota); getch(); } void afis_val(int y) { printf("\n Mesaj afis_val: Nota este=%i",y); } void afis_ref(int *y) { printf("\n Mesaj afis_ref: Nota este=%i",*y); }

ARGUMENTELE FUNCTIEI

main()

De obicei, functia main() este folosita fara argumente n programe. n general, acest lucru se specifica folosind cuvntul cheie void. Totusi, functia main() poate primi informatii de pe linia de comanda, unde se apeleaza programul prin intermediul a doua argumente standard, denumite traditional argc si argv. Argumentul argc reprezinta numarul de argumente din linia de comanda si are valoarea cel putin 1, deoarece numele programului reprezinta primul argument.

Argumentul argv reprezinta un pointer catre un tablou de siruri de caractere. Asa cum am precizat, argv[0] reprezinta numele programului, argv[1] al doilea argument de pe linia de comanda s.a.m.d.. Presupunnd ca programul de mai jos primeste n urma compilarii si linkeditarii numele

argmain, atunci apelul argmain Nae va conduce, dupa executie, la rezultatul: Bine te-am gasit Nae!

Programul ilustreaza folosirea functiei main() cu argumente

#include stdio.h #include conio.h void main(int argc, char *argv[]) { printf(\n Bine te-am gasit %s!,argv[1]); }

POINTERI LA FUNCTII Dupa compilarea si linkeditarea unui program, fiecarei functii componente i se asociaza o adresa de memorie, care este de fapt adresa de nceput a functiei. Un pointer catre functie este un pointer care poate contine aceasta adresa. Sa presupunem ca o functie f are prototipul tip f(lista_parametri_formali); Forma generala a declaratiei unui pointer pf la functia f este: tip (*pf)(lista_parametri_formali); Exemplu. Un pointer p la functia void afisare(float,int); trebuie declarat void (*p)(float,int); Se observa ca functia si pointerul asociat functiei trebuie sa aiba tipul si lista de parametrii formali, identice. De asemenea, pointerul precedat de * trebuie sa fie inclus ntre paranteze rotunde datorita regulilor de precedenta ale limbajului. Daca n exemplul de mai sus, n loc de void (*p)(float,int); am fi scris void *p(float,int); din cauza prioritatii mari a parantezelor rotunde, declaratia ar fi avut semnificatia: prototip pentru functia p, care are un singur parametru de tip float si ntoarce un rezultat de tip pointer catre void.

Deoarece n C, numele functiei este echivalent cu adresa ei de nceput (asemanator cu obtinerea adresei unui tablou), un pointer catre functie poate primi adresa de nceput a functiei printr-o atribuire de forma: pf=f; Referindu-ne la exemplul considerat, avem: p=afisare; Dupa o atribuire de forma pf=f; apelul functiei prin intermediul pointerului pf se poate face (*pf)(lista_parametri_actuali); daca functia f este de tip void, sau nume_variabila=(*pf)(lista_parametri_actuali); daca functia f ntoarce un rezultat. Exemplu. Secventa de program de mai jos, . . . . . . p=afisare; (*p)(3.24,5); . . . . . . este perfect valida si are ca efect apelarea indirecta a functiei afisare. Prezentam n continuare doua din posibilitatile de aplicare a pointerilor la functii. O posibilitate este oferita de tablourile de pointeri catre functii. Programul de mai jos selecteaza aleator una din functiile adunare(),scadere(),inmultire() si impartire() prin intermediul unui tablou de pointeri catre functii. Selectia se face folosind functia rand() care ntoarce un numar arbitrar n domeniul 0...RAND_MAX, unde RAND_MAX are cel putin valoarea 32767. Prototipul functiei se afla n fisierul antet stdlib.h.

Programul ilustreaza folosirea pointerilor catre functii

#include "stdio.h" #include "conio.h" #include "stdlib.h" int adunare(int x,int y); int scadere(int x, int y); int inmultire(int x, int y); int impartire(int x, int y); void main(void) { int i,a=8,b=2; int (*t[])(int,int)={adunare,scadere,inmultire, impartire}; char *nume_t[]={"adunare","scadere","inmultire", "impartire"};

for (i=0;i<4;i++) { printf("\n S-a selectat functia %s",nume_t[i]); printf("\n Rezultatul este %d",(*t[i])(a,b)); } getch(); } int adunare(int x,int y) { return x+y; } int scadere(int x,int y) { return x-y; } int inmultire(int x,int y) { return x*y; } int impartire(int x,int y) { return x/y; }
Cea de-a doua posibilitate se refera la transmiterea drept parametrii a pointerilor la functii. Sa consideram functia

? ? sum ?a, b? daca fx ? ? ? ? dif ?a, b? daca

x?0 x?0

Se poate folosi o singura functie f() pentru definirea lui fx, care va avea ca argumente pe x si un pointer la functii notat p; acest pointer poate primi att adresa functiei sum() ct si cea a functiei dif().

Programul ilustreaza folosirea pointerilor catre functii

#include "stdio.h" #include "conio.h" int sum(int a,int b);

int dif(int a, int b); void f(float x,int a,int b,int (*p)(int a,int b)); void main(void) { float x; int a=9,b=4; printf("\nx="); scanf("%f",&x); if (x>0) f(x,a,b,sum); else f(x,a,b,dif); getch(); } int sum(int a,int b) { return a+b; } int dif(int a,int b) { return a-b; } void f(float x,int a,int b,int (*p)(int a,int b)) { printf("\n f are valoarea %i: ", (*p)(a,b)); }

FUNCTII RECURSIVE Recursivitatea reprezinta procesul prin care o entitate de un anumit tip se poate defini, descrie sau prelucra folosind entitati de acelasi tip. n cele ce urmeaza ne vom referi la functii recursive. Exista doua tipuri de functii recursive: direct recursive si indirect recursive. Functia f() se numeste direct recursiva daca n corpul ei apar apeluri la ea nsasi (autoapeluri), adica este de forma: tip f(lista_declaratii_parametri) { . . . . . apel f() . . . . . } Functiile f() si g() se numesc indirect recursive (mutual recursive) daca f() contine apeluri la g() iar g() contine apeluri la f(). Schematic acest lucru se poate reprezenta astfel:

tip f(lista_declaratii_parametri) { apel g() }

tip g(lista_declaratii_parametri) { apel f() }

ntelegerea notiunii de functie recursiva se sprijina pe cunoasterea modului n care se face apelul unei functii. Orice program C compilat mparte spatiul de memorie destinat n patru zone distincte (vezi Figura 8.1): zona cod program, zona variabile globale, zona de manevra (heap) destinata alocarii dinamice a variabilelor si zona de stiva (stack).

Stiva (Stack) Variabile dinamice (Heap) Variabile globale Cod program

Figura 8.1 mpartirea spatiului de memorie de catre un program C Stiva este un caz particular de lista, n care principalele operatii de introducere si extragere sunt guvernate de disciplina LIFO (Last In First Out), adica ultimul element intrat este primul extras. De regula, stiva se poate reprezenta sub forma

V`rf

x x x

Figura 8.2 Reprezentarea statica a stivei unde vrf reprezinta pointerul care arata spre ultimul element introdus n stiva. Facem precizarea ca, n memoria calculatorului, stiva (stack) creste de la adrese superioare la adrese inferioare. La apelul unei functii, n stiva se salveaza starea curenta a functiei apelante (adresa instructiunii cu care se va continua executia dupa ce se revine la functia apelata, valorile variabilelor locale si ale parametrilor) si se aloca spatiu pentru parametrii actuali si variabilele locale ale functiei apelate.

FUNCTII DIRECT RECURSIVE Cazul de recursivitate cel mai des ntlnit este cel al functiilor direct recursive. Este situatia n care functia apelanta si functia apelata coincid. Adncimea recursivitatii este data de numarul de autoapeluri. Pentru a nu se autoapela la infinit, corpul functiei trebuie sa contina o instructiune if, care, pe baza testarii unei conditii, asigura dupa un timp oprirea autoapelului si executia instructiunilor amnate prin autoapel. Conform mecanismului general de apelare, la fiecare autoapel al functiei se salveaza n stiva starea curenta a executiei sale, adica: adresa de revenire n program (adresa instructiunii cu care se va continua executia ntrerupta), valorile variabilelor locale si valorile parametrilor. Instructiunile din functie care se gasesc naintea instructiunii if considerate se cicleaza pna la oprirea autoapelului. Oprirea autoapelului nseamna >coborrea> pas cu pas n stiva: pentru fiecare pas se preiau din stiva valorile corespunzatoare si se executa instructiunile amnate prin autoapel. Pentru o mai buna ntelegere, prezentam mai jos exemplul clasic al calculului recursiv al factorialului. Functia factorial se poate defini recursiv astfel:

?1 f ?n ? ? ? ? n ? f ?n ? 1?
O posibila implementare este:

daca daca

n?1 n?1

int f(int n) { if(n>1)return n*f(n-1); return 1; } sau folosind operatorul conditional int f(int n) { return(n>1)?n*f(n-1):1; } Sa consideram apelul f(4). Acest apel va activa succesiv instructiunea n*f(n-1) pentru n=4,3,2. Pentru fiecare din aceste apeluri n stiva se vor depune parametrii a ctuali 4,3,2,1. Apelul f(1) furnizeaza rezultatul 1 pentru rezolvarea instructiunii amnate 2*f(1), care este valoarea lui f(2). Cu aceasta valoare se calculeaza valoarea f(3)=3*f(2) si n final valoarea f(4)=4*f(3). Se observa ca rezolvarea fiecarui autoapel nseamna deplasarea pe un nivel inferior al stivei, adica parcurgerea n ordine inversa a sirului de parametri actuali 4,3,2,1. Schematic, la fiecare apel, stiva va arata succesiv astfel:

Stiv[ vid[

? 4 Apel f(4)

? 3 4 Apel f(3)

? 2 3 4 Apel f(2)

? 1 2 3 4 Apel f(1)

Figura 8.3 Starea stivei n timpul executiei succesive a autoapelului

Rezolvarea apelurilor se face dupa urmatoarea schema:

1 2 3 4 f(1)=1

? 2 3 4 f(2)=1*2

? 3 ? 4 4 f(3)=1*2* f(4)=1*2*3* 3 4

Stiva vid[

Figura 8.4 Starile succesive ale stivei dupa iesirea din autoapel

Programul constituie un exemplu simplu de utilizare a recursivitatii

#include "stdio.h" #include "conio.h" void g(int x); void main(void)

{ int x=6; g(x); getch(); } void g(int x) { if (x) { g(x-2); printf("%2i",x); } }


Rezultatul executiei programului este 2 4 6. Pentru a ntelege modul de lucru al programului sa presupunem la modul ideal o multiplicare a functiei g(). Se poate imagina astfel urmatoarea schema de apel:

main() { int x=6 g(6); getch(); }

g(6)

g(4)

g(2)

g(0)

if(6) { g(4); printf


("%i",x) ;

if(4) { g(2); printf


("%i",x);

if(2) { g(0); printf


("%i",x) ;

if(0) iesire din autoapel

} x=6 x=4

} x=2 x=0

Figura 8.5 Schema de executie a functiei recursive g() Conditia de iesire din recursivitate este realizata cnd argumentul x al functiei g() devine 0. n acest moment se revine n functie la prima instructiune de dupa apel, adica la printf(). Se executa printf() cu valoarea corespunzatoare lui x, adica 2. Se revine n functie la prima instructiune de dupa apel, adica se executa printf() pentru x=4. Analog se revine n functie la printf() care se executa pentru x=6. n final se revine n functia main() si procesul se ncheie. Atragem atentia ca >multiplicarea> functiei recursive nu se realizeaza n realitate; exista o singura functie care se executa pentru diverse valori ale parametrilor. Programele care folosesc functii recursive nu sunt ntotdeauna cele mai bune variante de rezolvare a unor probleme sub aspectul timpului de calcul si al memoriei folosite. Se poate spune ca n general metodele recursive, mai ales cnd adncimea recursivitatii este mare, sunt neperformante. De exemplu, pentru functiile prezentate mai sus, variantele iterative sunt simple si eficiente. De asemenea, aceste variante sunt mai usor de depanat. Totusi, de cele mai multe ori functiile recursive pun n lumina mult mai bine esenta problemei de rezolvat. Implementarea recursiva a factorialului reprezinta un astfel de exemplu. Pentru o serie de probleme, nsa, rezolvarea folosind functii recursive reprezinta singura solutie viabila din punct de vedere practic. De exemplu, algoritmul de sortare rapida QuickSort sau binecunoscuta problema a turnurilor din Hanoi sunt dificil de implementat fara a utiliza recursivitatea. Ne vom opri cu exemplificarea la problema turnurilor din Hanoi, care are urmatorul enunt: Se dau trei tije a,b,c. Pe tija a se afla n discuri perforate, de diametre diferite, asezate unul peste celalalt n ordinea descrescatoare a diametrelor. Se cere sa se gaseasca toate mutarile prin care cele n discuri de pe tija a sunt aduse pe tija b n aceeasi ordine, utiliznd tija c ca o tija ajutatoare. O mutare nseamna deplasarea unui singur disc de pe o tija pe alta, peste un disc de diametru mai mare. Vom nota prin ab, deplasarea unui disc de pe tija a pe tija b si n mod analog orice deplasare de pe o tija pe alta. Evident, pentru n=1 solutia problemei este ab, pentru n=2, sirul de mutari ac,ab,cb, iar pentru n=3,ab,ac,bc,ab,ca,cb,ab. Pe masura ce n creste se observa si cresterea dificultatii de rezolvare. Problema se rezolva relativ simplu, daca punem n evidenta caracterul ei recursiv. Astfel, se poate spune ca mutarea a n discuri de pe tija a pe tija b utiliznd tija c este echivalenta cu succesiunea de pasi: ?? mutarea a n-1 discuri de pe tija a pe tija c utiliznd b; ?? mutarea singurului disc ramas, de pe tija a, pe tija b; ?? mutarea celor n-1 discuri de pe tija c pe tija b utiliznd a.

Grafic, rezolvarea se poate schita astfel:

n-1 1 a b c

<=>

n-1 1

Figura 8.6. Schema deplasarilor celor n discuri de pe tija a, pe tija b, utiliznd tija c

Notnd cu Hanoi (n, a, b, c) solutia problemei, tinnd cont de cele spuse anterior obtinem exprimarea sa recursiva.

? ab n ? 1 Hanoi ?n, a , b, c ? ? ? ? Hanoi ?n ? 1, a , c, b?abHanoi ?n ? 1, c, b, a ?

Programul implementeaza functia Hanoi (n, a, b, c)

#include "stdio.h" #include "conio.h" void hanoi(int n,char a, char b, char c); void main(void)

{ int n; clrscr(); printf("\n Numarul de discuri, n="); scanf("%i",&n); printf("\n Solutia este "); hanoi(n,'a','b','c'); getch(); } void hanoi(int n,char a,char b,char c) { if (n==1)

printf("\n %c,%c",a,b); else { hanoi(n-1,a,c,b); printf("\n %c,%c",a,b); hanoi(n-1,c,b,a); } }

9. Clase de memorare pentru variabile


Asa cum s-a vazut, tipul variabilei determina semnificatia valorilor pe care la poate lua variabila si operatiile permise asupra sa. Clasa de memorare a unei variabile indica locul unde i se va aloca spatiu si durata de viata a zonei de memorie asociate. Declaratia completa a unei variabile are forma:

specificator_clasa tip variabila;


SPECIFICATORII CLASELOR DE MEMORARE
Exista patru specificatori pentru clase de memorare: auto, register, static, extern respectiv pentru clasele automatic, register, static, extern. Atunci cnd declaratia variabilei nu contine n mod explicit clase de memorare, aceasta este stabilita implicit pe baza locului unde este declarata variabila. Variabilele din clasa automatic sunt declarate n interiorul functiilor si nu sunt recunoscute n afara blocului de declarare (sunt locale blocului respectiv). Durata de viata a unei astfel de variabile coincide cu timpul de executie al blocului; ea este creata la intrarea n bloc si distrusa la iesirea din bloc. Locul de memorare al variabilelor auto este stiva; la nceputul executiei unui bloc ele sunt create pe stiva, iar la sfrsitul executiei distruse prin descarcarea stivei. n mod uzual locul de declarare al variabilelor locale este la nceputul blocului format din corpul ntregii functii, nsa, exista si posibilitatea de a declara variabile n blocuri strict incluse n corpul functiei. Variabilele declarate n interiorul unei functii care nu au specificata clasa de memorare sunt considerate n mod implicit ca apartinnd clasei automatic.

Functia

citeste() din programul urmator citeste un numar ntreg x daca n=1 numere ntregi (n<=100) pe care le depune ntr-un vector x[100] daca n>1
#include "stdio.h" #include "conio.h" void citeste(int n); void main(void) { int n; printf("\n n="); scanf("%i",&n); citeste(n); getch(); } void citeste(int n) { if (n==1) { int x; printf("\n x=");

si

scanf("%i",&x); } else { int x[100],i; for (i=0;i<n;i++) { printf("\n x[%i]= ",i); scanf("%i",&x[i]); } } printf("\n S-au alocat %i octeti",sizeof(int)*n); }

Se observa ca alocarea memoriei att pentru ntregul x ct si pentru vectorul x se face conditionat. Avantajul declararii ntr-un bloc conditional consta n faptul ca se va aloca memorie numai daca este nevoie. Folosirea aceluiasi nume x pentru cele doua zone alocate este posibila deoarece alocarile se fac n blocuri diferite. Parametrii formali ai unei functii apartin de asemenea clasei automatic. Ei se comporta ca orice variabila locala functiei respective avnd n plus posibilitatea de a primi valorile corespunzatoare ale parametrilor actuali. O variabila din clasa automatic sau register poate fi initializata; initializarea se face la fiecare intrare n bloc. Specificatorul de clasa register este indicat a se aplica variabilelor locale si parametrilor formali ai u nei functii care sunt frecvent utilizati de program. n mod normal, o variabila care apartine clasei register ar trebui sa fie memorata n registrii unitatii centrale si nu n memoria RAM, fapt de natura sa mbunatateasca substantial viteza operatiilor facute asupra sa. Totusi, este evident ca, desi putem declara oricte variabile cu specificatorul register, doar o parte din ele vor avea loc n registrii unitatii centrale. De asemenea, n general, specificatorul are efect doar asupra variabilelor de tip char sau int. Din motive de portabilitate, cele care sunt n plus vor fi tratate normal. Datorita specificului lor (sunt folosite intens, sunt de obicei de tip ntreg) variabilele contor se preteaza destul de bine, pentru a fi declarate cu specificatorul register. Unei variabile din clasa register nu i se poate aplica operatorul de luare a adresei & (nu are adresa proprie). Asemanator cu variabilele din clasa automatic, variabilele din clasa register sunt create la intrarea n bloc. Variabilele auto si register care nu sunt initializate explicit contin valori initiale nedefinite. Exista situatii cnd este necesar ca o variabila locala sa-si pastreze valoarea de la un apel la altul al functiei n care este declarata. n acest caz variabila trebuie sa fie declarata cu specificatorul static. Ea este recunoscuta, de asemenea, numai n interiorul functiei unde a fost declarata, doar ca memorarea sa se va face n locatii fixe de memorie, iar durata sa de viata coincide cu durata de executie a programului. O variabila declarata static poate fi initializata, dar initializarea se face o singura data, la compilare.

Calculul sumei primelor

n numere impare folosind o variabila declarata static

#include "stdio.h" #include "conio.h" suma(void);

void main(void) { int n,i,y; printf("\n Numarul de elemente din suma="); scanf("%i",&n); for (i=1,y=1;i<n;i++) y+=suma(); printf("\n Suma primelor %i numere impare %i",n,y); getch(); } suma(void) { static int s=1; s+=2; return s; }

este

Avantajul folosirii variabilelor locale consta n imposibilitatea de a modifica o astfel de variabila n afara blocului unde a fost declarata. Folosirea n plus a specificatorului static permite crearea unor functii independente (vezi exemplul functiei suma() de mai sus) ce pot fi inserate, fara probleme, n biblioteci. Alternativa la folosirea specificatorului static pentru a pastra valoarea sumelor partiale ntre doua apeluri era folosirea unei variabile globale. O variabila globala se declara totdeauna n afara oricarei functii (nainte de prima sa utilizare, dar cel mai frecvent la nceputul programului). Ea poate fi folosita n orice functie din program si are o adresa fixa de memorie. Drept exemplu consideram programul anterior, usor modificat.

Calculul sumei primelor

n numere impare folosind o variabila globala

#include "stdio.h" #include "conio.h" /* variabila globala s */ int s=1;

suma(void); void main(void) { int n,i,y; printf("\n Numarul de elemente din suma="); scanf("%i",&n); for (i=1,y=1;i<n;i++) y+=suma(); printf("\n Suma primelor %i numere impare este %i",n,y); getch(); } suma(void) { s+=2; return s; }

Se observa ca valorile intermediare ale sumei calculate sunt memorate succesiv n variabila globala s, unde sunt pastrate de la un apel la altul al functiei suma(). Folosirea variabilelor globale este recomandata n cazul n care functiile care le folosesc sunt n numar mare. Ele pot servi ca valori de comunicare ntre functii. Deoarece pot fi folosite de orice functie, exista nsa pericolul sa fie alterate accidental. Daca la acest lucru adaugam faptul ca variabilele globale ocupa memorie pe toata durata executiei programului, chiar si dupa ce nu mai sunt necesare, tragem concluzia ca, n general, nu se va prefera o variabila globala n locul unei variabile locale. Variabilele globale afecteaza aspectul de sine statator al unei functii: functia va face referire nu numai la variabile declarate n corpul sau, ci si la variabile declarate n afara sa. Variabilele globale fac parte implicit din clasa extern. Exista posibilitatea ca un program C sa fie alcatuit din functii editate n fisiere separate (program multifisier). Ca o variabila globala declarata tr-un fisier sa fie recunoscuta si n functiile celorlalte fisiere n trebuie ca n acestea din urma sa fie folosit n mod explicit, la declarare, specificatorul extern. n felul acesta se evita alocarea de memorie pentru aceeasi variabila n fiecare fisier, fapt ce ar produce eroare la linkeditarea modulelor de program. O variabila globala poate fi initializata, iar initializarea ei trebuie facuta o singura data si anume n fisierul n care apare declarata fara specificatorul extern. Pentru exemplificare sa transformam programul anterior ntr-un program multifisier presupunnd ca functia main() va fi editata n fisierul princ.c, iar functia suma() n fisierul

sum.c.

Exemplu de program multifisier

/* fisierul princ.c */ #include "stdio.h" #include "conio.h" /* variabila globala s */ int s=1; suma(void); void main(void) { int n,i,y; printf("\n Numarul de elemente din suma="); scanf("%i",&n); for (i=1,y=1;i<n;i++) y+=suma(); printf("\n Suma primelor %i numere impare %i",n,y); getch(); } /* fisierul sum.c */ /* variabila globala s */ extern int s; suma(void) { s+=2; return s; }

este

Se observa ca variabila globala

a fost definita (declarata si initializata) n fisierul

princ.c,

unde i se aloca si memorie, iar n fisierul sum.c a fost declarata folosind specificatorul extern (fara a i se mai aloca memorie). O variabila externa declarata ntr-un program multifiser poate fi recunoscuta doar de fisierul n care a fost declarata (ascunsa) daca i se aplica specificatorul static. Avantajul este ca functiile din celelalte fisiere nu pot afecta accidental valoarea unei astfel de variabile. Ca o consecinta, declararea n doua fisiere distincte a aceleiasi variabile externe cu specificatorul static va avea ca efect obtinerea a doua entitati distincte: modificarea uneia dintre ele nu va afecta cealalta entitate.

Pentru a exemplifica efectul specificatorului static asupra unei variabile globale modificam problema anterioara, cernd ca functia suma() sa poata calcula la cerere fie suma primelor n numere naturale pare, fie suma primelor

n numere naturale impare. n acest scop, adaugam o functie de initializare a variabilei s, init(). Functiile init() si suma() vor face parte dintr-un fisier distinct sum_init.c, iar variabila globala s va fi declarata static n scopul de a fi recunoscuta doar n aceste doua functii. Acest lucru permite definirea n fisierul princ.h a variabilei globale s fara nici o legatura cu variabila s declarata n fisierul sum_init.c.

Programul ilustreaza efectul specificatorului static asupra unei variabile globale

/* fisierul princ.c */ #include "stdio.h" #include "conio.h" /* variabila globala s */ int s=0; suma(void); void init(int); void main(void)

{ int n,i; printf("\n Doriti suma primelor numere pare(P) sau impare(I)? "); if (toupper(getche()) == 'P' ) init(-2); else init(-1); printf("\n Numarul de termeni ai sumei="); scanf("%i",&n); for (i=0;i<n;i++) s+=suma(); printf("\n Suma numerelor este %i",s); getch(); } /* fisier sum_init.c */ static int s; suma(void) { s+=2; return s;

} void init(int v_init) { s=v_init; }


Variabilele locale sau globale declarate static si neinitializate explicit sunt initializate implicit cu valoarea 0. Daca o variabila externa si o variabila locala (sau parametru formal) au aceleasi nume, n functia unde este declarata variabila locala referirea la numele comun are n vedere variabila locala.

Programul afiseaza valoarea variabilei

x, declarata local, adica x=1.42

#include "stdio.h" #include "conio.h"

/* variabila globala x */ int x=1; void main(void) { /* variabila locala x */ float x=1.42; printf("\n x=%.2f",x); getch(); }

n general, declaratiile facute ntr-un bloc asupra unei variabile sunt mai puternice dect cele facute asupra aceleiasi variabile n exterior, n sensul ca referirea la variabila n cadrul blocului vizeaza declaratia locala blocului.Programul ilustreaza faptul ca declaratia unei variabile ntr-un bloc interior este mai puternica dect declaratia sa ntr-un bloc exterior: se va afisa succesiv

x=4.13 x=3
#include "stdio.h" void main(void) { float x=4.13; printf("\n x=%.2f",x);

{ int x=3; printf("\n x=%i",x); } getch(); }


O functie poate sa apartina clasei extern sau static. Daca specificatorii extern sau static sunt omisi, functia se considera implicit n clasa extern. O functie care apartine clasei extern este recunoscuta (vizibila) n fisierul unde este definita sau declarata, ca si n toate fisierele care alcatuiesc programul. O functie declarata static este recunoscuta doar n fisierul unde este declarata, nu si n celelalte fisiere ale programului. ntr-un program multifisier, pot exista doua functii cu acelasi nume cu conditia sa fie declarate static n fisiere diferite.

10. Functii pentru prelucrarea fisierelor


Limbajul C nu are instructiuni de intrare/iesire. Operatiile de intrare/iesire sunt realizate prin intermediul functiilor incluse n biblioteca standard a limbajului. Acest lucru i confera o mare flexibilitate stiind ca n general operatiile de intrare/iesire depind puternic de sistemul de operare folosit. Operatiile de intrare/iesire sunt realizate cu ajutorul unor dispozitive foarte diverse: tastatura, display, unitati de disc, unitati de banda etc. Pentru tratarea lor unitara, fisierele pe astfel de dispozitive sunt privite ca secvente ordonate de octeti. Pozitia octetului curent (octetul care va fi prelucrat) este memorata ntr-o variabila speciala numita indicator de pozitie. Pe baza indicatorului de pozitie se poate face modificarea octetului curent, citirea sau scrierea la o anumita pozitie n fisier. Dupa efectuarea operatiilor de citire sau scriere indicatorul de pozitie este incrementat cu un numar de octeti. De asemenea, este posibila citirea valorii indicatorului de pozitie sau setarea lui la o anumita valoare. Practic, un dispozitiv fizic este transformat de sistemul de fisiere al limbajului C ntr-un dispozitiv logic numit flux (stream). Astfel, functiile de intrare/iesire si pastreaza generalitatea deoarece nu se refera la dispozitive fizice s pecifice n mod direct, ci prin intermediul fluxului conectat la ele. Acest fapt asigura portabilitatea codului generat de compilator. Un flux se reprezinta printr-un pointer la o entitate de tip FILE care contine informatii despre: pozitia curenta n flux, indicatori de eroare si de sfrsit de fisier, zone tampon (buffere) asociate. Exista doua tipuri de fluxuri: text si binare. Fluxul text presupune transferul de caractere organizate n linii caractere (o linie se termina prin caracterul newline). ntr-un flux text pot interveni anumite conversii de caracter si din acest motiv este posibil sa existe anumite nepotriviri ntre caracterele introduse n flux si cele rezultate n urma transferului (de exemplu, un caracter newline poate fi convertit ntr-o pereche de caractere retur de car-avans rand). Fluxul binar reprezinta o succesiune de octeti care nu suporta nici o conversie n timpul transferului.

FUNCTIILE DE DESCHIDERE fopen() SI DE NCHIDERE fclose() Lansarea n executie a unui program C are drept consecinta si crearea n mod automat a trei fluxuri text standard: stdin pentru intrare, stdout pentru iesire si stderr pentru iesire erori. Denumirile stdin, stdout, stderr sunt de fapt numele unor pointeri constanti catre tipul FILE. Celelalte fisiere ale programului trebuie deschise n mod explicit folosind functia fopen(), care conecteaza un flux la un anumit fisier. n aceasta lucrare vor fi considerate doar fisiere pe disc. Prototipul functiei fopen() este FILE *fopen(const char *numefis, const char *mod); unde: numefis este numele fisierului, precedat eventual de calea de acces, iar mod este un sir de caractere care precizeaza modul deschiderii. Variabila mod precizeaza caracterul text sau binar al deschiderii precum si scopul deschiderii (citire, scriere, adaugare, citire-scriere). Valorile posibile pentru mod sunt:
??

deschidere fisier n mod text "r" pentru citire "w" pentru scriere "a" pentru adaugare "r+" pentru citire si scriere "w+" pentru citire si scriere "a+" pentru citire si scriere (actualizare) prin adaugare la sfrsitul fisierului

deschidere fisier n mod binar Se adauga la valorile aratate mai sus sufixul b, adica se obtin valorile: "rb","wb","ab","r+b","w+b","a+b", pentru fiecare valoare explicatia fiind similara cu cea prezentata la modul text.
??

Observatii. Pentru a indica explicit modul text se poate adauga sufixul t. De asemenea, valorile mod de tipul "r+b" sau "r+t" se pot scrie si sub forma "rb+","rt+". ncercarea de a deschide un fisier care nu exista, n mod citire, conduce la eroare. Daca un fisier este deschis pentru scriere n modul "w"("wt"sau"wb") atunci informatia continuta n el va fi distrusa. Daca fisierul este deschis pentru scriere si nu exista deja un fisier cu acel nume, atunci el va fi creat. Pentru actualizare (citire si scriere) fisierul trebuie deschis folosind caracterul '+' n cadrul modului; n acest fel, daca nu exista, el va fi creat, iar daca exista, nu va fi distrus. Daca se deschide un fisier n modul a, datele vor fi adaugate la sfrsitul fisierului daca acesta deja exista, iar daca nu exista se creeaza un fisier nou. Daca operatia de deschidere a unui fisier este ncununata de succes functia fopen() creeaza o structura FILE si ntoarce adresa sa. Indicatorul de pozitie ia valoarea 0 daca fisierul este deschis n modurile r sau wsi o valoare egala cu numarul de octeti ai fisierului, daca fisierul este deschis n modul a. Daca operatia de deschidere a fisierului nu a avut succes (de exemplu fisierul nu exista, este protejat la scriere sau discul este plin) functia fopen() ntoarce valoarea NULL. Operatia simetrica deschiderii unui fisier, adica operatia de nchidere a fisierului este realizata cu ajutorul functiei fclose(). Ea are urmatorul prototip: int fclose(FILE *fp); unde: fp este pointerul returnat la apelul functie fopen(). Operatia de nchidere a unui fisier nseamna: ?? deconectarea fluxului de fisier (fapt important deoarece exista o limitare a numarului de fisiere deschise simultan); ?? golirea buferului de iesire n fisier (transferul pe disc a datelor aflate nca n bufer) daca fisierul e deschis n mod scriere sau actualizare; ?? abandonarea datelor necitite din buferul de actualizare, daca fisierul este deschis pentru citire sau actualizare. Daca operatia de nchidere are succes functia ntoarce valoarea 0, iar n caz contrar EOF (macroul EOF are n general valoarea -1).

FUNCTII PENTRU CITIREA SI SCRIEREA UNUI CARACTER Functia pentru citirea unui caracter dintr-un fisier are prototipul: int fgetc(FILE *fp); Echivalent poate fi folosita si functia getc(). Existenta celor doua functii cu acelasi efect este motivata de compatibilitatea cu versiuni mai vechi ale limbajului C. Efectul functiei fgetc() este urmatorul: functia returneaza valoarea caracterului (de fapt, din motive istorice, un ntreg cu octetul de rang superior egal cu zero) daca operatia a avut succes sau EOF cnd s-a atins sfrsitul fisierului sau are loc o eroare. Functia pentru scrierea unui caracter ntr-un fisier are prototipul: int fputc(int ch,FILE *fp); Ca si n cazul functiei fgetc(), exista o functie echivalenta numita putc(). Efectul functiei este urmatorul: functia scrie n fisier un caracter (de fapt, din motive istorice, un ntreg cu octetul de rang superior zero) pe care-l returneaza daca operatia a avut succes. n caz de eroare se va returna EOF.

Programul creeaza un fisier de caractere pe disc, avnd numele dat ca argument n linia de comanda; fisierul va fi citit caracter cu caracter, iar caracterele diferite de cifrele zecimale 0,1,2,...,9 vor fi afisate.

#include "stdio.h" #include "conio.h" void main(int argc, char *argv[]) { FILE *f; char ch; if (argc!=2) { printf("\n Numele fisierului nu e pe linia de comanda"); exit(1); } /* deschiderea fisierului pentru scriere */ if ( (f=fopen(argv[1],"w")) == NULL) { printf("\n Eroare la deschidere fisier"); exit(1); } /* scrierea fisierului */ printf("\n Tastati un sir de caractere! \n"); while ( (ch=getchar()) != '\n') putc(ch,f); fclose(f); /* Deschiderea fisierului pentru citire */ if ( !(f=fopen(argv[1],"r")) ) { puts("\n Nu se poate deschide fisierul"); exit(1); } /* citirea din fisier si afisarea caracterelor diferite de cifre zecimale */ printf("\n Sirul fara cifre zecimale este:\n"); while ( (ch=fgetc(f)) != EOF) { if(ch<'0' || ch>'9') putchar(ch); } fclose(f); }

FUNCTII PENTRU TRANSFERUL SIRURILOR DE CARACTERE O generalizare a functiilor care activeaza la nivel caracter o reprezinta functiile care realizeaza transferuri de siruri de caractere. Prototipurile acestor functii se afla n fisierul antet stdio.h si au urmatoarea forma:
??

pentru scriere n fisier int fputs(const char *sir, FILE *fp); pentru citire din fisier char *fgets(char *str, int lg, FILE *fp);

??

Apelul acestor functii are urmatorul efect: Functia fputs() scrie n fisier sirul precizat la adresa sir. n caz de succes ntoarce ultimul caracter scris, iar n caz de insucces se ntoarce caracterul EOF. Functia fgets() citeste un sir de lungime cel mult lg-1 caractere si le transfera n sirul str. Daca se ntlneste un caracter newline, citirea e oprita iar caracterul va fi incorporat n str. Sirul va fi completat automat cu caracterul \0. n caz de succes functia ntoarce adresa sirului str, iar n caz de insucces sau sfrsit de fisier, se ntoarce valoarea NULL.

Se creeaza un fisier prin adaugare citind siruri de la tastatura. Se listeaza apoi fisierul n linii de n caractere. Numele fisierului si valoarea lui n se citesc de la tastatura.

#include "stdio.h" #include "conio.h" void main(void) { int n; char x[200],*fc; char numefis[11]; FILE *f; printf("\n Dati numele fisierului: "); gets(numefis); printf("\n Dati lungimea liniilor de afisare: "); scanf("%i",&n); if ( (f=fopen(numefis,"a")) == NULL) { printf("\n Nu se poate deschide fisierul"); exit(1); } printf("\n Adaugati siruri?(d/n)"); if (tolower(getche()) == 'd') { printf("\n Tastati siruri caractere despartite prin Enter,"); printf(" pentru sfarsit tastati cuvantul stop\n"); gets(x); while (strcmp(x,"stop"))

{ fputs(x,f); gets(x); } fclose(f); /* se deschide fisierul pentru citire */ if ( (f=fopen(numefis,"r")) == NULL) { printf("\n Nu se poate deschide fisierul! "); exit(1); } /* afisarea fisierului */ printf("\n Fisierul in linii de %d caractere este:\n",n); fc=fgets(x,n+1,f); while (fc) { puts(x); fc=fgets(x,n+1,f); } fclose(f); } getch(); }

FUNCTIILE PENTRU TRANSFER CU FORMAT, fscanf() SI fprintf() O modalitate simpla de a realiza operatii de transfer cu format, este oferita de functiile fprintf() si fscanf(). Dupa cum sugereaza si numele lor ele se comporta la nivelul fisierelor exact ca functiile scanf() si printf() la nivelul consolei. Datele formatate scrise ntr-un fisier cu ajutorul functiei fprintf() pot fi examinate cu ajutorul unui editor text. Prototipurile functiilor fscanf() si fprintf() sunt urmatoarele: int fscanf(FILE *fp,const char *sir_format); int fprintf(FILE *fp,const char *sir_format); Functia fscanf() ntoarce numarul de valori pentru care citirea, conversia si memorarea datelor a fost facuta corect. n cazul n care apare o eroare nainte de a se ncepe citirea, se returneaza EOF. Functia fprintf() returneaza numarul de caractere scrise efectiv sau o valoare negativa n caz de insucces.

Programul creeaza fisierul formatat pe disc avnd numele formtext cuprinznd numele, prenumele si nota la examen pentru studentii unei grupe. Pentru verificarea corectitudinii informatiei din fisier, continutul acestuia se afiseaza pe ecran.

#include "stdio.h" #include "conio.h" #include "string.h" void main(void) { FILE *f; int n,i; typedef struct catalog { char nume[15]; char prenume[20]; short nota; }struc; struc x; if ( !(f=fopen("grupa","w")) ) { printf("\n Nu se poate deschide fisierul"); exit(1); } printf("\n Introduceti numarul de studenti din grupa "); scanf("%i",&n); /* citirea de la tastatura si scrierea in fisier */ for (i=0;i<n;i++) { printf("\n Nume student=");scanf("%s",x.nume); printf("\n Prenume student="); scanf("%s",x.prenume); printf("\n Nota=");scanf("%i",&x.nota); fprintf(f,"%s %s %i",x.nume,x.prenume,x.nota); } fclose(f); if ( !(f=fopen("grupa","r")) ) { printf("\n Nu se poate deschide fisierul!"); exit(1); } /* afisarea pe ecran a continutului fisierului */ printf("\n Continutul fisierului este:\n"); for (i=0;i<n;i++) {

fscanf(f,"%s %s %i",x.nume,x.prenume,&x.nota); printf("\n%s %s %i",x.nume,x.prenume,x.nota); } getch(); }


Observatie. Functia de citire scanf() este echivalenta cu fscanf(stdin, ... ), iar functia de scriere printf() cu fprintf (stdout, ... ).

FUNCTIILE fread() SI fwrite() PENTRU TRANSFERUL BLOCURILOR DE DATE n ciuda avantajelor evidente, transferul cu format al datelor este mai lent dect transferul datelor sub forma binara datorita conversiilor n format ASCII. De asemenea, n general, fisierul creat cu date ASCII formatate foloseste memorie mai multa dect un fisier binar. O modalitate eficienta pentru transferul unui volum mare de date o reprezinta folosirea functiilor fread() si fwrite(). Prototipurile acestor functii sunt: size_t fread(void *buf, size_t nr_oct, size_t nb, FILE *fp); size_t fwrite(const void *buf, size_t nr_oct, size_t nb,

FILE *fp); Tipul de date size_t este definit n stdio.h si aici are semnificatia unui ntreg fara semn. Functia fread() citeste din fisierul asociat pointerului fp, nb blocuri, fiecare bloc avnd lungimea nr_oct octeti si le transfera n zona de memorie indicata de pointerul buf. Valoarea ntoarsa de functia fread() este numarul de blocuri citite efectiv. Daca s ajuns la sfrsitul -a fisierului sau daca a intervenit o eroare aceasta valoare este strict mai mica dect nb. Functia fwrite() scrie n fisierul asociat pointerului fp, din zona indicata de pointerul buf, nb blocuri, fiecare bloc avnd lungimea egala cu nr_octeti. Valoarea ntoarsa de functia fwrite() este egala cu numarul de blocuri scrise efectiv; ea va fi mai mica dect nb daca la scriere intervine o eroare.

FUNCTIA feof() Functia feof() permite determinarea momentului cnd a fost ntlnit sfrsitul fisierului. Acest lucru este util deoarece n anumite situatii returnarea valorii EOF poate avea si alte cauze. Astfel, s-a vazut ca functia fgetc() returneaza EOF att la ntlnirea sfrsitului de fisier ct si n caz de eroare. De asemenea, n cazul citirii dintr-un fisier binar este posibil ca valoarea EOF sa fie returnata n mod natural indicndu-se n mod eronat sfrsit de fisier sau eroare. Prototipul functiei feof() este: int feof(FILE *fp); Functia ntoarce valoarea adevarat (valoare diferita de zero), daca s-a ntlnit sfrsitul fisierului si valoarea fals (egala cu zero) daca nu s-a ntlnit sfrsitul fisierului. Functia feof() se poate folosi att la fisiere binare ct si la fisiere text.

FUNCTIA rewind() Functia rewind() pozitioneaza indicatorul de pozitie al fisierului la nceputul fisierului. Prototipul sau este: void rewind(FILE *fp);

EXEMPLU DE PROGRAM CARE UTILIZEAZA FUNCTIILE: fread(), fwrite(), feof(), rewind() Programul de mai jos creeaza un fisier cu n nregistrari, al carui nume este dat pe linia de comanda. Dupa ce indicatorul de pozitie este adus la nceputul fisierului cu ajutorul functiei rewind(), continutul nregistrarilor este citit si afisat pe ecran. Valoarea n se citeste de la tastatura.

Exemplu de program care utilizeaza functiile: fread(), fwrite(), feof(), rewind()

#include "stdio.h" #include "conio.h" void main(int argc, char * argv[]) { FILE *f; int i,n; typedef struct catalog { char nume[20]; char prenume[25]; short nota; }struc; struc x; if (argc!=2) { printf("\n Nu ati dat numele fisierului!"); exit(1); } if ( !(f=fopen(argv[1],"w+b")) ) { printf("\n Nu se poate deschide fisierul!"); exit(1); } printf("\n Dati va rog numarul de inregistrari: "); scanf("%i",&n);

/* crearea fisierului */ for (i=0;i<n;i++) { printf("\n Nume="); scanf("%s",x.nume); printf("\n Prenume="); scanf("%s",x.prenume); printf("\n Nota="); scanf("%i",&x.nota); if (fwrite(&x,sizeof(x),1,f) != 1) { printf("\n Eroare la scriere"); exit(1); } } /* aducerea indicatorului de pozitie la valoarea 0 */ rewind(f); /* afisarea continutului fisierului */ for (i=0;i<n;i++) if (fread(&x,sizeof(x),1,f) != 1) { if feof(f) break; printf("\n Eroare la citire"); } else printf("\n Nume=%s Prenume=%s Nota=%i", x.nume,x.prenume,x.nota); fclose(f); }

FUNCTII PENTRU CITIREA S MODIFICAREA INDICATORULUI DE POZITIE AL FISIERULUI: I fseek(), fgetpos(), fsetpos(), ftell() Pe lnga functia rewind() deja prezentata, standardul ANSI C cuprinde urmatoarele functii care pot fi folosite n prelucrarea fisierelor: fseek(), fgetpos(), fsetpos(), ftell(). Functia fseek() are prototipul: int fseek(FILE *fp, long nr_octeti, int origine); Efectul functiei este pozitionarea indicatorului de pozitie al fisierului asociat pointerului fp, la valoarea specificata de origine la care se adauga deplasarea egala cu nr_octeti. Valoarea pentru origine trebuie sa fie unul din urmatoarele nume de macrocomanda definite n fisierul stdio.h:

nume SEEK_SET SEEK_CUR SEEK_END

semnificatie nceputul fisierului pozitia curenta sfrsitul fisierului

n cazul n care operatia de pozitionare reuseste, functia returneaza 0, iar n caz de insucces o valoare diferita de 0. Functia fgetpos() are prototipul: int fgetpos(FILE *fp, fpos_t *poz); si are ca efect memorarea valorii curente a indicatorului de pozitie n obiectul indicat de poz. n caz de eroare functia returneaza o valoare diferita de 0, iar n caz contrar valoarea 0. Tipul fpos_t e definit n stdio.h ca typedef long fpos_t. Functia fsetpos() are prototipul: int fsetpos(FILE *fp,const long int *poz); Apelul are urmatorul efect: indicatorul de pozitie va primi valoarea memorata n obiectul indicat de poz. Aceasta valoare trebuie sa fie obtinuta anterior printr-un apel fgetpos(). n caz de succes functia returneaza valoarea 0 iar n caz contrar o valoare diferita de 0. Functia ftell() are prototipul: long int ftell(FILE *fp); Ea returneaza valoarea curenta a indicatorului de pozitie al fisierului asociat pointerului fp. n caz de eroare valoare returnata este 0, iar daca fluxul nu este asociat unui dispozitiv care sa permita cautari aleatoare, valoarea returnata este nedefinita.

Programul permite pozitionarea pe o nregistrare a fisierului construit cu programul de la Exemplul 10.4; n functie de optiunea utilizatorului se modifica sau nu nota nregistrarii.

#include "stdio.h" #include "conio.h" void main(void) { char numefis[8]; long int *p; int k; FILE *f; typedef struct catalog { char nume[20]; char pren[25]; short nota; }struc;

struc x; printf("\nNume fisier ="); scanf("%s",numefis); f=fopen(numefis,"r+b"); printf("\n Dati va rog numarul inregistrarii "); scanf("%d",&k); fseek(f,0,SEEK_END); if ( 1<=k && sizeof(struc)*(k-1) < ftell(f) ) { fseek(f,sizeof(struc)*(k-1),SEEK_SET); fread(&x,sizeof(struc),1,f); printf("\n Inregistrarea %i este:\n",k); printf("\n Nume=%s,Prenume=%s,Nota=%d",x.nume,x.pren, x.nota); printf("\n Doriti modificarea notei ?(d/n)"); if (toupper(getche())=='D') { printf("\nNota noua este="); scanf("%d",&x.nota); fgetpos(f,p); *p-=sizeof(struc); fsetpos(f,p); fwrite(&x,sizeof(struc),1,f); } } else printf("\nInregistrarea nu este in fisier!"); printf("\n Inregistrarile din fisier sunt:\n"); fseek(f,0,SEEK_SET); while( !feof(f) ) { fread(&x,sizeof(struc),1,f); if ( !feof(f) ) printf("\nNume=%s Prenume=%s Nota=%d", x.nume,x.pren,x.nota); } fclose(f); getch(); }

11. Directive catre preprocesor


De regula, instructiunile unui program sunt destinate procesorului. Un program C poate contine nsa si instructiuni care se adreseaza compilatorului (preprocesorului). Aceste instructiuni se numesc directive si permit prelucrari ale textului fisierului sursa nainte de compilare sau chiar influentarea procesului de compilare. Astfel, cu ajutorul directivelor preprocesor se poate include un fisier sursa n alt fisier sursa, se pot nlocui diferite simboluri n textul fisierului sursa sau se pot ignora la compilare anumite blocuri din text. Standardul ANSI C pune la dispozitia programatorului urmatoarele directive: #if #ifdef #ifndef #else #elif #endif #include #define #undef #line #error #pragma

O directiva preprocesor este totdeauna precedata de simbolul # (diez), trebuie sa fie singura pe o linie si nu se termina cu semnul ; . Cele mai utilizate directive sunt #define si #include.

DIRECTIVELE #define, #undef SI #include


Directiva #define are forma generala: #define nume_macro secventa_caractere unde: nume_macro reprezinta identificatorul care va fi nlocuit n text, ori de cte ori apare, prin secventa_caractere. De exemplu, n secventa de mai jos #define M 20 #define eroared eroare la scriere pe disc int x[M],i; . . . . . for(i=0;i<M;i++) printf(%i,x[i]); if(y) printf(eroare); . . . . . efectul directivelor #define consta n nlocuirea identificatorilor M si eroared prin secventele 20, respectiv eroare la scriere pe disc oriunde apar ei n program. Se observa ca folosirea directivei #define mareste lizibilitatea programului, usurnd ntretinerea sa. Astfel, pentru a modifica dimensiunea vectorului si toate secventele de program care o privesc, este suficient sa schimbam doar linia #define M 20. Daca lungimea sirului secventa_caractere depaseste un rnd, se poate continua pe rndul urmator daca se insereaza un backslash (\) la sfrsitul primului rnd.

Exemplu: #define LUNG Acest sir este insuportabil \ de lung Deoarece directiva #define permite nlocuirea unui simbol printr-un text, n particular acel text poate fi o instructiune sau un nume macro prezent ntr-o directiva #define anterioara. Exemplu: #define #define #define #define #define #define . . . . . v=VOLUM . . . . . if (x<11) EROARE; . . . . . B C D ARIA VOLUM EROARE 20 30 40 B*C ARIA*D printf(\n Eroare)

Nu trebuie confundata substitutia n text care se face utiliznd directiva #define, cu o atribuire. De exemplu, expresia: A=(B+5)*D nu este descrisa corect prin secventa #define #define #define . . . . A=C*D . . . . altceva. Corect este: #define #define #define . . . . A=C*D B C D . 20 (B+5) 10 B C D . . 20 B+5 10

n realitate, C se nlocuieste textual prin B+5, adica avem A=B+5*D ceea ce este cu totul

Directiva #define poate fi parametrizata obtinndu-se o macrodefinitie (sau macro). n aceasta situatie directiva are forma: #define nume_macro(parametri) str Aparitia n program a secventei formata din identificatorul nume_macro urmat de argumente efective conduce la nlocuirea acestei secvente cu textul str n care parametrii formali sunt nlocuiti cu argumente efective. Efectul folosirii unei macrodefinitii este asemanator utilizarii unei functii. De exemplu, functia f(x)=x2+1 poate fi scrisa cu ajutorul macroului #define f(x) ((x)*(x)+1) Daca n programul n care este prezenta aceasta macrodefinitie avem de exemplu: y=3*f(2);

valoarea atribuita lui y va fi 15. Practic, nainte de compilare textul f(2) este nlocuit cu ((2)*(2)+1), ceea ce explica rezultatul. Parantezele rotunde sunt doar aparent redundante. Ele permit obtinerea unui rezultat corect n orice situatie. Astfel, daca macrodefinitia ar fi: #define f(x) x*x +1 rezultatul evaluarii expresiei y=3*f(3-1); nu este, cum ne-am astepta, valoarea 15 ci 6. Acest lucru se datoreaza faptului ca macrodefinitia nseamna nainte de toate o nlocuire de text. Asadar, litera x va fi nlocuita cu 3-1, iar n membrul drept al expresiei vom avea 3*3-1*3-1+1=6, ceea ce explica rezultatul. Pentru a evita astfel de situatii se recomanda ncadrarea parametrilor si a textului care va nlocui nume_macro ntre paranteze rotunde, asa cum s-a facut de altfel initial. Folosirea unui macro n locul unei functii are avantajul reducerii timpului de executie, deoarece apelul functiei implica >urcarea> n stiva la apel si >coborrea> n stiva la revenirea n programul principal. Pe de alta parte, folosirea macrodefinitiei este avantajoasa si din punct de vedere al memoriei folosite numai daca textul care reprezinta functia nu este ntins: macrodefinitia nseamna marirea codului prin substituirile de text care se fac, n timp ce codul unei functii apare o singura data, indiferent de numarul de apeluri care se fac. O directiva #define poate fi anulata cu ajutorul directivei #undef. n felul acesta se delimiteaza asocierea facuta ntre un nume_macro cu o anumita secventa_caractere, oferind posibilitatea unei noi asocieri. Forma generala a directivei #undef este: #undef nume_macro Exemplu: #define M #undef M #define M 20 30

Directiva #include are forma generala: #include <nume_fisier> sau #include nume_fisier Efectul directivei consta n includerea fisierului sursa n fisierul unde este activata directiva. Fisierul nume_fisier se numeste fisier header (sau fisier antet) si are extensia <.h>. n cazul n care nume_fisier are specificata si o cale de cautare completa, preprocesorul cauta direct n calea respectiva indiferent de prezenta ghilimelelor sau a parantezelor unghiulare. Daca nu este specificat dect fisierul nume_fisier, n cautarea fisierului se tine cont de ghilimele sau paranteze unghiulare astfel: cnd se utilizeaza parantezele unghiulare, fisierul destinat includerii va fi cautat ntr-unul sau mai multe directoare standard definite la implementare; daca se folosesc ghilimelele cautarea se va face mai nti n directorul curent si apoi n directoarele standard. Un fisier antet poate contine definitiile unor tipuri de date, prototipuri de functii, declaratii de variabile si de constante, macrodefinitii si directive de includere. Se constata ca directivele de includere pot fi imbricate. Exemplu de fisier antet: /* fisier antet ex.h */ typedef struct pers

{ char num x[35]; int varsta }persoana; float copiere(int sursa, float dest); extern x; const ore_zi=24; #define LMIN=200; #include ante.h Observatii. Un fisier antet nu poate sa contina definitii de functii, de date sau constante structurate. Dupa cum se poate vedea modulele antet au rol declarativ. Cu ajutorul directivei de includere orice program C poate utiliza entitatile deja cuprinse ntr-un fisier antet ceea ce este evident un avantaj. n cazul programelor multifisier prin interfata pe care o realizeaza ntre modulele acestora, el asigura consistenta declaratiilor relative la aceeasi entitate. Prototipurile functiilor standard se gasesc grupate n diferite fisiere antet situate ntr-un director special numit Standard Header Directory. Astfel n stdio.h se declara functiile standard de intrare/iesire, n string.h functiile de prelucrare a fisierelor, n math.h functiile matematice obisnuite etc.. Includerea acestor prototipuri n program, prin intermediul fisierelor corespunzatoare, permite folosirea corecta a functiilor aflate n biblioteca C standard. antet

DIRECTIVELE DE COMPILARE #ifdef, #ifndef SI DIRECTIVA #error

CONDITIONATA:

#if, #elif,

Directivele de compilare conditionata permit compilarea anumitor sectiuni de program functie de valoarea unei expresii. Deoarece testele asupra expresiei se fac n faza de compilare expresia trebuie sa contina numai constante. Directiva #if are forma generala: #if expresie_constanta sectiune_program #endif Efectul directivei consta n compilarea sectiunii_program numai daca expresie_constanta este adevarata. n caz contrar sectiune_program este ignorata. O varianta cu alternativa se poate construi folosind si directiva #else. n acest caz se poate scrie: #if expresie_constanta sectiune_program1 #else sectiune_program2 Daca expresie_constanta este adevarata se compileaza sectiune_program1, iar n caz contrar sectiune_program2. Exista posibilitatea de a obtine o scara if_else_if folosind directiva #elif ca mai jos: #if expresie_constanta_1 instructiuni #elif expresie_constanta_2 instructiuni #elif expresie_constanta_3 instructiuni . . . . . . .

#elif expresie_constanta_n secventa_instructiuni #endif Efectul este acela ca, pentru prima expresie_constanta_i, i ? {1,2,,n}, adevarata (considerata de sus n jos), se compileaza instructiunile asociate, iar restul se ignora. n exemplul de mai jos, pentru calculul unei sume se poate opta, pentru varianta cu for (numar de iteratii cunoscut) sau varianta cu while (numar de iteratii necunoscut) functie de valoarea macroului ITER.

Programul ilustreaza utilizarea facilitatii de compilare conditionata #include "stdio.h" #include "conio.h" /* forme de iteratie: 0 se foloseste while 1 se foloseste for */ #define ITER 0 void main(void) { int x,s=0; #if ITER { int i,n; printf("\n n="); scanf("%i",&n); for (i=0;i<n;i++) { printf("\n x="); scanf("%i",&x); s+=x; } } #else { printf("\n x="); scanf("\n %i",&x); while (x) { s+=x; printf("\n x="); scanf("\n %i", &x); } } #endif printf("\n Suma este %i",s); getch(); }

Evident, folosind eventual directiva #elif, n exemplul dat se poate introduce si optiunea de folosire a structurii do_while. Directiva #ifdef are forma generala: #ifdef nume_macro sectiune_program #endif Daca nume_macro este definit se compileaza sectiune_program, n caz contrar aceasta nu se compileaza. n mod analog, directiva #ifndef are forma generala: #ifndef nume_macro sectiune_program #endif cu ntelesul evident: daca nume_macro nu este definit se compileaza sectiune_program, iar n caz contrar nu. Att #ifdef ct si #ifndef pot avea alternative cu #else, dar nu pot fi folosite cu directiva #elif. Aceste directive pot fi nlocuite de directiva #if daca expresia care se testeaza este de forma: defined nume sau defined(nume) Att prima ct si a doua expresie iau valoarea adevarat daca nume a fost definit si valoarea fals daca nume nu a fost definit.

Programul ilustreaza folosirea directivei #ifdef #include "stdio.h" #include "conio.h" #define M 10 void main(void) { #ifdef M printf("\n Este definit M"); #else printf("\n M nu e definit"); #endif getch(); }

O forma echivalenta este data n exemplul urmator:

Programul ilustreaza folosirea directivei #if asociata cu defined #include "stdio.h" #include "conio.h" #define M 10 void main(void) { #if defined(M) printf("\n Este definit M"); #else printf("\n M nu e definit"); #endif getch(); } Ambele versiuni afiseaza acelasi mesaj: Este definit M. Directiva #error se foloseste de obicei la depanarea programului si are forma generala: #error mesaj_eroare unde mesaj_eroare este un text care nu se pune ntre ghilimele. Efectul directivei este ntreruperea compilarii si afisarea mesajului.

12. Functii standard


Biblioteca C standard cuprinde o mare varietate de functii, instrumente puternice si eficiente n elaborarea programelor. O parte din aceste functii, cea mai uzuala, a fost deja prezentata de-a lungul lucrarii. n continuare prezentam prototipurile functiilor standard C, mai importante, nsotite de un scurt comentariu privind argumentele lor, valorile ntoarse si efectul actiunii lor. Se poate face o clasificare a functiilor C dupa cum urmeaza: ?? functii pentru operatii de intrare/iesire ?? functii pentru prelucrarea caracterelor ?? functii pentru prelucrarea sirurilor de caractere ?? functii pentru gestiunea memoriei heap ?? functii matematice ?? functii pentru gestiunea timpului

FUNCTII PENTRU OPERATII DE INTRARE/IESIRE


FUNCTII DESCHIDERE/ NCHIDERE FISIER FILE * fopen (const char * numefis, const char mod); deschide fisierul cu numele numefis si l asocieaza unui flux identificat printr-un pointer. Modul de deschidere este specificat de mod (vezi Capitolul 10). Intoarce un pointer catre o structura FILE daca operatia a avut succes si un pointer de valoare NULL, daca operatia a esuat. int fclose(FILE *fp); nchide fisierul deschis cu fopen(). Pointerul fp este pointerul fisier returnat la apelarea functiei fopen(). FUNCTII CARE CONTROLEAZA INDICATORUL DE POZITIE N FISIER int fseek(FILE *fp,long nr_octeti,int origine); pozitioneaza indicatorul de pozitie n fisierul fp la valoarea specificata de origine la care se adauga deplasarea egala cu nr_octeti (vezi Capitolul 10). int fgetpos(FILE *fp,long int *poz); memoreaza valoarea curenta a indicatorului de pozitie n obiectul indicat de poz. n caz de succes se ntoarce o valoare diferita de 0, iar n caz de eroare valoarea 0. int fsetpos(FILE *fp,const long int *poz); seteaza indicatorul de pozitie la valoarea *poz, obtinuta anterior cu functia fgetpos(). n caz de succes se ntoarce o valoare diferita de 0, iar n caz de eroare valoarea 0. long ftell(FILE *fp); ntoarce valoarea curenta a indicatorului de fisier sau -1 n caz de eroare. void feof(f); ntoarce o valoare diferita de 0 daca indicatorul de pozitie se afla la sfrsitul fisierului si 0 n caz contrar.

FUNCTII PENTRU CITIREA SI SCRIEREA CARACTERELOR int fgetc(FILE *fp); ntoarce valoarea caracterului cnd operatia de citire din fisierul fp are succes sau EOF cnd s-a atins sfrsitul fisierului sau a avut loc o eroare. int fputc(int c,FILE *fp); ntoarce caracterul scris n fisierul fp, n caz de succes sau EOF n caz de eroare. int ungetc(int c,FILE *fp); se repune un caracter n bufferul asociat fisierului fp. Se ntoarce c n caz de succes sau EOF n caz de eroare. int getc(FILE *fp); este similara cu fgetc() (este macro-instructiune). int getchar(void); este echivalenta cu fgetc(stdin); int putc(int c, FILE *fp); este similara cu fputc() (este macro-instructiune). int putchar(int c); este echivalenta cu fputc(int c,stdout); FUNCTII PENTRU CITIREA SI SCRIEREA SIRURILOR DE CARACTERE char *fgets(char *str,int lg, FILE *fp); citeste din fisier un sir de lungime cel mult lg-1 caractere si le transfera n sirul str. n caz de succes este ntoarsa adresa sirului str, iar n caz de eroare sau daca se atinge sfrsit de fisier se ntoarce valoarea NULL. int fputs(const char *sir, FILE *fp); scrie n fisierul fp sirul precizat la adresa sir. n caz de succes ntoarce ultimul caracter scris, iar n caz de insucces ntoarce caracterul EOF. char *gets(char *sir); citeste un sir de caractere de la tastatura si l depune n sir. Caracterul \n nu se include n sir si se adauga terminatorul \0. Functia ntoarce sir n caz de succes sau EOF n caz de eroare. int puts(const char *sir); scrie sirul sir pe ecran ntorcnd ultimul caracter scris sau EOF n caz de eroare. FUNCTII DE INTRARE/I ESIRE CU FORMAT int fscanf(FILE *fp, const char *sir_format); ntoarce numarul de valori pentru care citirea, conversia si memorarea datelor a fost facuta corect. Daca apare o eroare nainte de a ncepe citirea se ntoarce EOF. int scanf(const char *sir_format);

este echivalenta cu functia int fscanf(stdin, const char *sir_format); int sscanf(char *sir, const char *sir_format); citeste date din sirul de caractere sir conform formatului. Valorile ntoarse de functiile scanf() si sscanf() sunt similare celor ntoarse de functia fscanf().

int fprintf(FILE *fp, const char *sir_format); ntoarce numarul de caractere scrise efectiv sau o valoare negativa n caz de insucces. int printf(const char *sir format); este echivalenta cu functia fprintf(stdout, const char *sir_format); int sprintf(char *sir,const char *sir_format); scrie date conform formatului n sirul sir. Similare cu functiile fprintf(), printf(), sprintf() sunt functiile vfprintf(), vprintf() respectiv vsprintf(), pe care nu le prezentam n aceasta lucrare. Pentru documentare se poate consulta [2]. FUNCTII PENTRU CITIREA SI SCRIEREA BLOCURILOR DE DATE size_t fread(void *buf, size_t nr_oct, size_t nb, FILE *fp); citeste din fisierul fp, nb blocuri, fiecare cu lungimea nr_oct si le transfera n zona specificata de buf. ntoarce numarul de blocuri citite efectiv. size_t fwrite(const void *buf,size_t nr_oct, size_t nb, FILE *fp); scrie n fisierul fp, nb blocuri preluate din zona specificata de buf, fiecare bloc avnd lungimea egala cu nr_octeti. Se ntoarce numarul de blocuri scrise efectiv. FUNCTII PENTRU TESTAREA APARTENENTEI LA CLASE DE CARACTERE Prototipurile acestor functii apar n fisierul antet ctype.h. Functiile se aplica unui parametru de tip ntreg si ntorc valori diferite de 0 daca e satisfacuta conditia de apartenenta si 0 n caz contrar. int isalnum(int c); testeaza daca c e alfanumeric int isalpha(int c); testeaza daca c e alfabetic int iscntrl(int c); testeaza daca c e caracter de control (FF, NL, CR, HT, VT, BEL, BS); int isdigit(int c); testeaza daca c este cifra zecimala int isgraph(int c); testeaza daca c este caracter grafic fara spatiu int islower(int c); testeaza daca c este litera mica int isprint(int c); testeaza daca c e caracter tiparibil inclusiv spatiu int ispcmct(int c); testeaza daca c este caracter tiparibil fara spatiu sau caracter alfanumeric int isspace(int c); testeaza daca c este CR, FF, HT, VT, NL, spatiu int isupper(int c); testeaza daca c este litera mare int isxdigit(int c); testeaza daca c este cifra hexazecimala (0-9, A-F sau a-f);

Tot aici amintim functiile tolower() si toupper(): int tolower(int c); converteste o litera mica la litera mare corespunzatoare int toupper(int c); converteste o litera mare la litera mica corespunzatoare Pentru ambele functii, c ramne nemodificat daca nu este litera!

FUNCTII PENTRU PRELUCRAREA SIRURILOR DE CARACTERE


Se deosebesc trei categorii de functii:
?? ?? ??

functii pentru citirea si scriere sirurilor, prezentate deja (care au prototipurile n stdio.h) functii cu prototipul in string.h (copiere, comparare, concatenare, initializare siruri) functii de conversie a sirurilor (cu prototipul in stdlib.h)

FUNCTII CU PROTOTIPUL IN STRING.H Exista doua categorii de functii: functii care se refera la siruri care se termina prin \0 si functii n care sirurile sunt vazute pur si simplu ca tablouri de caractere (fara terminatorul \0). Functiile care se refera la siruri cu terminator \0 sunt: char *strcat(char *sir1, const char *sir2); concateneaza sir2 la sir1 si returneaza sir1. char *strchr(const char *sir, int c); cauta c n sirul de caractere sir ntorcnd n caz de succes pozitia lui, iar n caz contrar valoarea NULL. int strcmp(const char sir1, const char sir2); compara lexicografic sirurile sir1 si sir2 si ntoarce o valoare <0 daca sir1<sir2 =0 daca sir1=sir2 >0 daca sir1>sir2 char *strcpy(char *sir1, const char *sir2); copiaza sirul sir2 n sirul sir1 si ntoarce sir1. size_t strcspn(const char *sir1, const char *sir2); ntoarce lungimea prefixului sirului sir1 care nu contine nici unul din caracterele sirului sir2. char *strerror(int er_cod); ntoarce un pointer catre un sir ce reprezinta un mesaj de eroare corespunzator codului er_cod. size_t strlen(const char *sir); ntoarce numarul de caractere din sirul sir, exclusiv terminatorul \0. char *strncat(char *sir1, const char *sir2, size_t nr); concateneaza cel mult nr caractere din sir2 la sir1 si ntoarce sir1. int *strncmp(const char *sir1, const char *sir2, size_t nr); compara cel mult nr caractere din sir1 si sir2 si ntoarce valori la fel ca functia strcmp().

char *strncpy(char *sir1, const char *sir2, size_t nr); copiaza cel mult nr caractere din sirul sir2 n sirul sir1 si ntoarce sir1. char *strpbrk(const char *sir1, const char *sir2); ntoarce un pointer la prima aparitie n sir1 a oricarui caracter din sir2 sau NULL daca nici un caracter din sir2 nu se afla n sir1. char * strrchr(const char *sir, int c); ntoarce pointerul la ultima aparitie a lui c n sir sau NULL daca c nu apare n sir. size_t strspn(const char *sir1, const char *sir2); ntoarce lungimea prefixului sirului sir1 care contine numai elemente din sir2. char *strstr(const char *sir1, const char *sir2); ntoarce un pointer la prima aparitie n sir1 a sirului sir2 sau NULL daca sir2 nu este subsir al sirului sir1. char *strtok(char *sir1, const char sir2); cauta n sir1 subsiruri delimitate de caractere din sirul sir2. Urmatoarele functii se refera la tablouri de caractere (fara a avea terminatorul \0 ca la siruri). void *memchr(const void *tablou, int c, size_t nr); cauta n tabloul indicat prin tablou, prima aparitie a lui c printre cele nr caractere. ntoarce adresa lui n caz de succes sau NULL n caz contrar. void memcmp(const void *tablou1, const void *tablou2, size_t nr); compara n sens lexicografic primele nr caractere ale tablourilor tablou1 si tablou2. ntoarce un ntreg calculat ca la functia strcmp(). void *memcpy(void *tablou1, const void *tablou2, size_t nr); copiaza nr caractere din tablou2 n tabloul indicat de tablou1. Daca tablourile se suprapun efectul este nedefinit. void *memmove(void *tablou1, const void *tablou2, size_t nr); copiaza nr caractere din tablou2 n tablou1. Tablourile se pot suprapune. void *memset(void *tablou, int c, size_t nr); copiaza c (octetul sau inferior) n primele nr caractere ale tabloului tablou. FUNCTII DE CONVERSIE A SIRURILOR Urmatoarele functii realizeaza conversii ale sirurilor de caractere n valori numerice. double atof(const char *str); ntoarce rezultatul conversiei sirului de caractere indicat de pointerul str la un numar real. Sirul trebuie sa contina o valoare numerica scrisa corect, n caz contrar se ntoarce o valoare nedefinita. Delimitatorul final poate fi orice caracter diferit de punct si de literele e sau E (care intervin n mod natural n reprezentarea unui numar n virgula mobila). int atoi(const char *str);

ntoarce rezultatul conversiei sirului de caractere indicat de pointerul str la un numar ntreg. Sirul trebuie sa contina o valoare ntreaga corect scrisa, n caz contrar se ntorce o valoare nedefinita. Delimitatorul final poate fi orice caracter diferit de cifra. int atol(const char *str); ntoarce rezultatul conversiei sirului de caractere indicat de pointerul str la o valoare long int. Sirul trebuie sa contina o valoare ntreaga valida, n caz contrar se ntoarce o valoare nedefinita. Delimitatorul final poate fi orice caracter diferit de cifra. double strtod(char *str, char **sf); ntoarce rezultatul conversiei sirului de caractere indicat de pointerul str la un numar n format double, ignornd spatiile libere initiale. Sirul trebuie sa contina o valoare numerica corect scrisa, n caz contrar se ntoarce valoarea zero. Delimitatorul final poate fi orice caracter diferit de punct si de literele e sau E. n *sf se depune pointerul la delimitatorul final din sir. long strtol(const char *str, char **sf, int radix); ntoarce rezultatul conversiei sirului de caractere indicat de pointerul str ignornd spatiile libere initiale ntr-un numar de tip long, reprezentat n baza de numeratie stabilita de radix. Daca radix este zero se considera ca baza este 8, 10 sau 16 n caz contrar baza este data de valoarea radix aflata obligatoriu ntre 2 si 36. Delimitatorul poate fi orice caracter care nu poate intra n componenta unui numar ntreg. Daca nu se poate realiza conversia se ntoarce valoarea zero. n *sf se depune pointerul la delimitatorul final din sir. unsigned long strtoul(const *start, char **sf, int radix); actioneaza similar cu functia strtol() cu exceptia faptului ca ntoarce n caz de succes o valoare unsigned long.

FUNCTII PENTRU GESTIUNEA MEMORIEI HEAP


void *calloc(size_t nr, size_t nr_oct); aloca o zona compacta din memoria heap pentru un tablou avnd nr elemente fiecare de lungime nr_oct octeti. ntoarce un pointer catre primul octet al tabloului sau NULL daca nu exista suficienta memorie pentru alocare. void *free(void *ptr); dealoca zona de memorie indicata de ptr, alocata anterior cu functiile calloc(), malloc() sau realloc(). void *malloc(size_t nr_oct); aloca o zona compacta din memoria heap de dimensiune nr_oct octeti. ntoarce un pointer la primul octet din zona de memorie alocate sau NULL daca nu exista spatiu disponibil. void *realloc(void *ptr, size_t nr_oct); realoca zona de memorie indicata de ptr (alocata anteriror cu ajutorul functiilor calloc() si malloc()) la valoarea data de nr_oct octeti. ntoarce un pointer la zona realocata sau NULL daca nu exista suficient spatiu de memorie pentru alocarea celor nr_oct octeti, situatie n care zona initiala de memorie ramne nemodificata.

FUNCTII MATEMATICE
Standardul ANSI C defineste 22 functii matematice frecvent utilizate n programe. Prototipurile lor sunt continute de fisierul antet math.h. Argumentele functiilor sunt, cu mici exceptii, de tip double iar valorile ntoarse sunt de tip double. Principalele erori care pot apare la apelul functiilor sunt: eroare de domeniu si eroare de reprezentare. Eroarea de domeniu apare atunci cnd argumentele functiei sunt n afara domeniului sau de definitie (de exemplu valori negative folosite drept argumente pentru functia logaritm), iar eroarea de reprezentare apare atunci cnd rezultatul ntors de functie nu poate fi reprezentat. Aceste erori provoaca setarea variabilei globale incorporate errno la valorile EDOM respectiv ERANGE. Cele doua macrocomenzi sunt definite n fisierul antet errno.h. n situatiile de eroare descrise se ntoarce o valoare definita la implementare, n cazul erorii de domeniu si respectiv valoarea HUGE_VAL (o valoare <mare> definita n fisierul antet math.h) n cazul erorii de reprezentare. Argumentele functiilor trigonometrice care sunt unghiuri sunt considerate n radiani. double double double double double double double double double double double double acos(double x); arccos de x asin(double x); arcsinus de x atan(double x); arctangenta de x atan2(double y,double x); arctangenta de y/x ceil(double x); cel mai mic ntreg >= x cos(double x); cosinus de x cosh(double x); cosinus hiperbolic de x exp(double x); exponentiala e la x fabs(double x); valoarea absoluta a lui x floor(double x); cel mai mare ntreg <= x fmod(double x,int y); restul mpartirii lui x la y vazuti ca ntregi (x modulo y) frexp(double x,double exp);intoarce valoarea mantisei numarului x si memoreaza valoarea exponentului n exp astfel nct x=mantisa*2exp (mantisa e cuprinsa ntre 0.5 si 1). ldexp(double x,int exp); calculeaza x*2exp log(double x); calculeaza logaritm natural de x log10(double x); calculeaza logaritm zecimal de x modf(double x,double *pin); descompune un numar x de tip double n partea sa fractionara pe care o ntoarce si partea ntreaga pe care o memoreaza n pin pow(double x,double y); ntoarce xy pentru x si y apartinnd domeniul de definitie sin(double x); sinus de x sinh(double x); sinus hiperbolic de x sqrt(double x); radacina patrata a lui x tan(double x); tangenta de x tanh(double x); tangenta hiperbolica de x

double double double double

double double double double double double

FUNCTII PENTRU GESTIUNEA TIMPULUI


Prototipurile functiilor care gestioneaza timpul se gasesc n fisierul header time.h. Acest header contine definitiile a patru tipuri de date: size_t, clock_t, time_t si tm.

Tipul size_t reprezinta un ntreg fara semn. Tipurile clock_t si time_t se folosesc la reprezentarea orei si a datei sistemului, ca date de tip long integer. Structura standard de tip struct tm are alcatuirea urmatoare: struct tm { int tm_sec;/* secunde 0...59 */ int tm_min;/* minute 0...59 */ int tm_hour;/* ore 0...23 */ int tm_mday;/* ziua din luna 1...31 */ int tm_mon;/* luna 0...11 */ int tm_year;/* anul >=1900 */ int tm_wday;/* ziua saptamanii 0...6 */ int tm_yday;/* ziua din an 0...365 */ int tm_isdst;/* indicator al orei de vara este: >0 daca functioneaza =0 daca nu functioneaza <0 daca nu exista informatii in domeniu */ } Listam mai jos prototipurile celor mai uzuale functii cuprinse n fisierul time.h char *asctime(const struct tm *ptr); converteste timpul din structura indicata de pointerul ptr ntr-un sir de forma zi luna data ore:minute:secunde an\n\0, unde pointerul ptr este obtinut cu ajutorul functiei localtime() sau cu functia gmtime(). clock_t clock(void); ntoarce numarul de impulsuri de ceas, efectuate o data cu lansarea programului n executie. Transformarea n secunde se face mpartind aceasta valoare la macroul CLOCKS_PER_SEC care da numarul de batai pe secunda ale ceasului. char *ctime(const time_t *ptr); converteste timpul de calendar din structura indicata de pointerul ptr ntr-un sir de forma zi luna data ore:minute:secunde an\n\0 Pointerul ptr este obtinut cu ajutorul functiei time(). Ea este echivalenta cu asctime(local(time)); double diff_time(time_t timp2, timp_t timp1); ntoarce valoarea timp2-timp1 exprimata n secunde. struct tm *gmtime(const time_t *ptr); converteste timpul de calendar indicat de pointerul ptr n Timp Coordonat Universal (Universal Coordinated Time) ntorcnd un pointer la o structura de tip tm. Daca nu este disponibil se ntoarce NULL. Pointerul ptr este obtinut, printr-un apel al functiei time(). struct tm *localtime(const time_t *ptr); converteste tipul de calendar indicat de pointerul ptr n timp local ntorcnd un pointer la o structura de tip tm. Daca nu e disponibil se ntoarce NULL. Pointerul ptr e obtinut printr-un apel al functiei time(). size_t strftime(char *sir, size_t dimmax, const char *sir_format, const struct tm *ptr);

converteste datele din structura indicata de pointerul ptr n sirul sir, conform cu formatul sir_format. time_t time(time_t *ptr); ntoarce ora curenta calendaristica a sistemului sau 1 daca nu exista aceasta facilitate. Se poate apela cu ptr=NULL sau cu o adresa a unei variabile de tip time_t. n ultimul caz valoarea ntoarsa se atribuie de asemenea variabilei indicate de ptr.

ALTE FUNCTII UTILE


int abs(int intr); /* fisier header stdlib.h */ ntoarce un ntreg egal cu valoarea absoluta a ntregului intr. void clearerr(FILE *fp); /* fisier header stdio.h */ sterge indicatorii de eroare si de sfrsit de fisier. div_t div(int numarator, int numitor); /* fisier header stdlib.h */ ntoarce o structura de tip div_t care contine ctul si restul mpartirii numarator la numitor n membrii quot, respectiv rem ai acestei structuri. void exit(int cod); /* fisier header graph.h */ produce iesirea dintr-un program si revenirea n sistemul de operare. Daca valoarea parametrului cod este 0 sau EXIT_SUCCES se indica terminarea cu succes a programului, iar daca este diferit de 0 sau EXIT_FAILURE se indica terminarea cu eroare a programului. int ferror(FILE fp); /* fisier header stdio.h */ ntoarce fie valoarea 0 indicnd faptul ca nu are loc o eroare sau o valoare diferita de 0 daca indicatorul de eroare asociat lui fp este pozitionat. int fflush(FILE fp); /* fisier header stdio.h */ goleste bufferul asociat unui fisier deschis pentru scriere sau citire/scriere. Efectul este nedefinit daca fisierul a fost deschis numai pentru citire. ntoarce EOF n caz de eroare sau 0 n caz normal. long labs(long lintr); /* fisier header stlib.h */ ntoarce un ntreg egal cu valoarea absoluta a ntregului lintr. ldiv_t ldiv(long numarator, long numitor); /* fisier header stdlib.h */ ntoarce o structura de tip ldiv_t care contine ctul si restul mpartirii ntre numarator si numitor n membrii quot si rem de tip long ai acestei structuri. int remove(const char *numefis); /* fisier header stdio.h */ sterge fisierul cu nume numefis. ntoarce 0 n caz de succes si o valoare diferita de 0 n caz de eroare. int rename(const char *nume_nou, header stdio.h */ const char *nume_vechi); /* fisier

redenumeste un fisier. ntoarce 0 n caz de succes si o valoare diferita de 0 n caz de eroare. int rand(void); /* fisier header stdlib.h */

ntoarce un numar ntreg uniform aleator din intervalul [0,RAND_MAX]. int srand(unsigned int seed); /* fisier header stdlib.h */ initializeaza generatorul de numere aleatoare cu valoarea seed. void qsort(void *buf, size_t nr, size_t lung, int(*cmp)(const void *arg1, const void *arg2)); /* fisier header stdlib.h */ sorteaza n ordine crescatoare tabloul buf[0],buf[1],,buf[nr-1] folosind metoda QuickSort. Parametrul lung reprezinta marimea n octeti a fiecarui element. Functia de comparare este *cmp si trebuie sa ntoarca o valoare<0 daca arg1<arg2 o valoare=0 daca arg1=arg2 o valoare>0 daca arg1>arg2 void bsearch(const void *cheie, const void *buf,size_t nr, size_t lung, int(*cmp)(const void *arg1, const void *arg2)); /* fisier header stdlib.h */ cauta n tabloul ordonat buf[0],buf[1],,buf[nr-1] ntorcnd un pointer catre primul membru care corespunde cheii indicata de cheie, n caz de succes, sau NULL n caz contrar. Semnificatia parametrilor este cea descrisa la functia qsort().

13. Functii video C


Un program care beneficiaza de o interfata prietenoasa cu utilizatorul, are sanse mai mari sa fie cumparat, fata de un program care ignora acest <detaliu> foarte important azi. De asemenea, prezentarea rezultatelor numerice nsotite de grafice, tabele, histograme desene etc., reprezinta o modalitate de informare mai atractiva si eficienta a utilizatorului. Iata de ce, calitatea interfetei progam-utilizator este, alaturi de timpul de executie si necesarul de memorie, unul din indicatorii principali prin care se poate aprecia un program. Controlul complet asupra ecranului se realizeaza cu ajutorul unor functii speciale, numite de obicei functii video. Din cauza diferentelor existente ntre diferite tipuri de calculatoare, standardul ANSI C nu defineste astfel de functii. Totusi, tinnd cont de importanta lor deosebita, prezentam n aceasta sectiune, pentru familiarizare, cteva functii video furnizate de Borland Turbo C++ pentru calculatoare IBM PC si compatibile, echipate cu adaptoare grafice sub sistemul de o perare DOS. Evolutia adaptoarelor video sau interfetelor grafice, este marcata de dorinta de a obtine pe ecran reprezentari la nivelul calitatii fotografice. Adaptoarele video contin memorie RAM video, cu ajutorul careia se realizeaza remprospatarea continua a imaginii de pe videomonitor si logica necesara generarii semnalelor de comanda pentru functionarea videomonitorului. Ele implementeaza cele doua moduri de lucru: grafic si text. Adaptorul MDA (Monochrome Display Adapter) permite numai afisare n mod text. Alte tipuri de adaptoare video (n ordinea aparitiei lor) sunt: Hercules Monochrome Graphics Adapter, CGA (Color Graphics Adapter), EGA (Enhanced Graphics Adapter), VGA (Video Graphics Array 0Adapter). Ele permit afisarea n mod text si n mod grafic. Cele doua moduri de lucru nu sunt active simultan. n modul text fiecare caracter de pe monitor este reprezentat n memoria video prin doi octeti: primul octet contine codul ASCII al caracterului, iar al doilea atributele de afisare. Structura octetului care codifica atributele de afisare este urmatoarea:

B unde: CCCC FFF B

reprezinta codul culorii caracterului; reprezinta codul culorii fondului; indica afisare continua (valoarea 0) sau intermitenta (valoarea 1).

Ecranul este privit ca o retea celulara, fiecare celula putnd memora un caracter. Originea retelei este n coltul din stnga sus si are coordonatele (1,1). Coordonata x creste de la stnga spre dreapta, iar coordonta y creste de sus n jos n planul ecranului. n modul grafic ecranul este vazut ca o retea foarte fina de puncte numite pixeli. Pixelul este cea mai mica zona ce poate fi <aprinsa> pe ecran. Culoarea unui pixel este reprezentata printr-un numar de biti n memoria video. Astfel, imaginea afisata este construita punct cu punct n memoria video. Originea se afla n coltul din stnga sus si are coordonatele (0,0). Coordonatele x si y cresc si descresc asemanator ca n modul text. Adaptoarele video sunt caracterizate prin rezolutie spatiala si rezolutie de culoare. Rezolutia spatiala n modul text se refera la numar de rnduri si numar de coloane, iar n modul grafic nseamna numar de linii si numar de pixeli pe linie. Rezolutia de culoare are n vedere numarul de culori afisabile simultan. Tinnd cont de simplitatea modului de reprezentare, modul text se caracterizeaza prin viteza mare de afisare si necesar de memorie mic. Este evident nsa ca afisarea n mod text nu permite reprezentari pe ecran de mare finete. Din acest punct de vedere modul grafic este ideal. Pretul platit consta ntr-un necesar mare de memorie si viteza de afisare redusa. Ambele probleme au fost nsa rezolvate cu succes, iar tendinta este de a nlocui definitiv modul text cu modul grafic. Att n modul text ct si n modul grafic exista posibilitatea de a preciza zone dreptunghiulare de ecran n care se pot afisa texte, imagini grafice, n timp ce restul ecranului

ramne nemodificat. Aceste zone, active la un moment dat, se numesc ferestre. Definirea unei ferestre se face cu ajutorul unor functii speciale care precizeaza coordonatele coltului stnga sus si a coltului dreapta jos pentru fereastra respectiva. O data definita fereastra, coordonatele folosite de celelalte functii video vor fi relative la aceasta fereastra. Noua origine va fi situata n coltul din stnga sus a ferestrei, iar coordonatele x si y relative, la fereastra activa, cresc sau descresc asemanator cu situatia n care este activat tot ecranul. De altfel, fereastra implicita pentru ambele moduri este tot ecranul.

FUNCTII VIDEO N MODUL TEXT


Functiile video n modul text au prototipul n conio.h si se mpart n urmatoarele categorii:
?? ?? ?? ??

Functii pentru stabilirea modului; Functii pentru definirea si gestionarea ferestrelor; Functii pentru controlul atributului; Functii pentru afisarea si modificarea textului.

FUNCTII PENTRU STABILIREA MODULUI


void textmode(int mod); unde mod poate lua una din valorile BW40 (40 coloane alb-negru), C40 (40 coloane color), BW80 (80 coloane alb-negru), C80 (80 coloane color), MONO (80 coloane monocrom), LASTMODE (modul precedent). Valorile BW40, C40, BW80, C80, MONO, LASTMODE pot fi nlocuite cu 0, 1, 2, 3, 7 respectiv -1.

FUNCTII PENTRU DEFINIREA SI GESTIONAREA FERESTRELOR


FUNCTII PENTRU DEFINIREA FERESTRELOR void window(int x1, int y1, int x2, int y2); defineste o fereastra cu coltul stnga-sus n punctul de coordonate (x1, y1) si cu coltul dreaptajos n punctul de coordonate (x2, y2). FUNCTII PENTRU GESTIONAREA FERESTRELOR void gettextinfo(struct text_info *ptr); completeaza o structura de tip text_info, continuta n conio.h, cu informatii despre fereastra. Structura contine coordonatele colturilor stnga-sus, dreapta-jos, atributul curent, modul text curent, dimensiunile ecranului, pozitia curenta a cursorului si se afla la adresa indicata de ptr. void gotoxy(int x, int y); pozitioneaza cursorul, n fereastra activa, n punctul de coordonate (x,y).

int wherex(void); ntoarce coordonata x a pozitiei curente a cursorului. int wherey(void); ntoarce coordonata y a pozitiei curente a cursorului.

FUNCTII PENTRU CONTROLUL ATRIBUTULUI


void textattr(int atribut); modifica valorile atributului (culoare caracter, culoare fond, caracterul continuu sau intermitent al afisarii) printr-un singur apel. void textbackground(int culoare); seteaza culoarea fondului pe care apar caracterele. Valorile posibile sunt: BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY sau 1,2,3,4,5,6, care nlocuiesc corespunzator constantele simbolice enumerate. void textcolor(int culoare); seteaza culoarea caracterelor. Valorile posibile pentru culoare sunt cele listate la functia textbackground() la care se adauga: DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE sau 8,9,10,11,12,13,14,15, care nlocuiesc corespunzator constantele simbolice enumerate. void highvideo(void); are ca efect afisarea cu intensitate sporita a caracterelor. void lowvideo(void); are ca efect afisarea cu intensitate redusa a caracterelor. void normvideo(void); are ca efect afisarea caracterelor cu intensitatea existenta la intrarea n executie a programului.

FUNCTII PENTRU AFISAREA SI MODIFICAREA TEXTULUI


int cprintf(); afiseaza un text formatat pe ecran, n fereastra curenta. Este asemanatoare cu functia printf(). int cputs(const char *sir); afiseaza sirul sir n fereastra curenta, ntorcnd ultimul caracter afisat. int getch(void); int getche(void); int putch(int c); au efectul prezentat la functiile cu aceleasi sume din standardul ANSI C. int gettext(int x1,int y1,int x2,int y2, void *tablou_dest);

copiaza un text cuprins n zona desemnata de coltul stnga-sus de coordonate (x1,y1) si coltul dreapta-jos de coordonate (x2,y2) n zona de memorie tablou_dest. ntoarce 0 daca operatia nu a avut succes si diferit de 0 n caz contrar. int puttext(int x1,int y1,int x2,int y2,void *tablou_sursa); afiseaza continutul tabloului tablou_sursa n zona de ecran definita de coltul stnga-sus de coordonate (x1,y1) si coltul dreapta-jos de coordonate (x2,y2). void clreol(void); sterge caracterele de la pozitia curenta a cursorului pna la sfrsitul liniei. void clrscr(void); sterge fereastra curenta si pozitioneaza cursorul n originea (1,1). void delline(void); sterge linia pe care se afla cursorul. void insline(void); insereaza o linie goala sub linia pe care se afla cursorul. int movetext(int x1,int y1,int x2,int y2, int x,int y); copiaza continutul din zona definita de coltul din stnga-sus de coordonate (x1,y1) si coltul din dreapta-jos de coordonate (x2,y2), n zona de aceeasi dimensiune care are coltul din stnga-sus de coordonate (x,y).

FUNCTII VIDEO PENTRU MODUL GRAFIC


Limbajul Turbo C++ are peste 70 functii video ce pot fi folosite n modul grafic (pe scurt, functii grafice) care se gasesc grupate n biblioteca grafica graphics.lib. Folosirea lor necesita includerea fisierului header graphics.h. Operatia de linkeditare nu include implicit (ca n cazul bibliotecii standard) module obiect din biblioteca graphics.lib. Din acest motiv, daca se lucreaza sub un mediu integrat trebuie bifata caseta Graphics Library din meniul Option/Linker. Daca se lucreaza n mod comanda trebuie folosita o comanda de forma tcc nume_program graphics.lib Trecerea n modul grafic nu este posibila fara ncarcarea n memorie a unor rutine speciale destinate adaptorului video prezent, numite si drivere. Prezentam mai jos cteva din functiile grafice, cele mai utile, grupate astfel:
?? ?? ?? ?? ?? ??

Functii pentru controlul sistemului grafic; Functii pentru controlul ecranului si al ferestrelor ; Functii pentru manevrarea imaginilor si a pixelilor; Functii pentru afisarea textelor n modul grafic; Functii pentru controlul culorii; Functii pentru desenare si umplere.

Functiile din graphics.lib sunt functii far, iar pointerii manipulati de ele sunt de tip far (far este un modificator de adresa utilizat pentru a referi adrese situate n afara zonei de date de 64 Ko).

FUNCTII PENTRU CONTROLUL SISTEMULUI GRAFIC


void far closegraph(void); nchide sistemul grafic, elibereaza memoria alocata la ncarcarea driverului si revine la modul text existent anterior apelului functiei initgraph(). void far detectgraph(int far *grdriver, int far *gmode); ntoarce n grdriver numarul asociat driverului, iar gmode va indica modul cu rezolutie maxima pentru adaptorul si driverul existent. char *far getdrivername(void); ntoarce numele driverului ncarcat. int for getmaxx(void); int far getmaxy(void); ntorc numarul maxim de pixeli pe orizontala si pe verticala (rezolutia spatiala curenta). char *far grapherrormsg(int cod_eroare); ntoarce adresa mesajului asociat codului de eroare. int far graphresult(void); ntoarce codul erorii rezultat din executia unei functii grafice. void far initgraph(int far *grdriver, int far *gmode, char far *cale); initializeaza sistemul grafic. Parametrul cale reprezinta un sir de caractere prin care se precizeaza calea de cautare a fisierului care contine driverul. Daca sirul este vid, cautarea se face n driverul curent. void far setgraphmode(int gmode); seteaza un nou mod grafic pentru driverul instalat, sterge ecranul si restaureaza toate valorile implicite. void far restorecrtmode(void); restabileste modul video existent anterior apelului functiei initgraph() fara a nchide sistemul grafic.

FUNCTII

PENTRU

CONTROLUL

ECRANULUI,

MANEVRAREA

IMAGINILOR SI A PIXELILOR
CONTROLUL ECRANULUI void far cleardevice(void); sterge tot ecranul si aduce cursorul n pozitia (0,0). void far clearviewport(); sterge fereastra curenta si aduce cursorul n pozitia (0,0). void far getviewsettings(struct viewporttype far *viewport);

determina dimensiunile ferestrei curente. Informatiile sunt date de structura viewporttype declarata n fisierul graphics.h si are forma: struct viewporttype { int left,top,right,bottom; int clip; }; void far setactivepage(int pag); specifica pagina activa pentru iesiri grafice (pot fi pna la opt pagini, parametrul pag putnd lua una din valorile 0,1,...7). void far setviewport(int x1, int y1, int x2, int y2, int clip); defineste o fereastra cu coltul stnga-sus, n punctul de coordonate (x1,y1) si coltul dreapta-jos, n punctul de coordonate (x2,y2). Daca clip are o valoare diferita de 0, desenele care depasesc fereastra sunt trunchiate, n caz contrar nu. void far setvisualpage(int pag); specifica pagina care va fi vizualizata pe ecran. MANEVRAREA IMAGINILOR void far getimage(int x1, int y1, int x2, int y2,void far *buf); copiaza zona de memorie video corespunzatoare zonei dreptunghiulare de pe ecran determinata de coordonatele (x1,y1), (x2,y2) n tabloul buf. unsigned far imagesize(int x1,int y1,int x2,int y2); ntoarce numarul de octeti necesari pentru memorarea imaginii dreptunghiulare de pe ecran determinata de coordonatele (x1,y1), (x2,y2). void far putimage(int x1,int y1,void far *buf,int operator); afiseaza pe ecran, ntr-o zona dreptunghiulara, avnd coltul din dreapta sus n punctul de coordonate (x1,y1), imaginea memorata n buf, afectata nsa de operator. Operatorul poate lua valorile COPY_PUT (copiere simpla), XOR_PUT (SAU exclusiv ntre imagini), OR_PUT (SAU ntre imagini), AND_PUT (SI ntre imagini), NOT_PUT (negativul imaginii). Constantele simbolice pot fi nlocuite cu valorile numerice 0,1,2,3 respectiv 4. Operatorii XOR_PUT, OR_PUT si AND_PUT se refera la operatiile bit cu bit, corespunzatoare, dintre imaginea existenta pe ecran si imaginea memorata n buf, iar NOT_PUT se refera la negarea bit cu bit a imaginii de pe ecran). MANEVRAREA PIXELILOR unsigned far getpixel(int x,int y); ntoarce culoarea pixelului aflat n pozitia (x,y). void far putpixel(int x,int y,int culoare); afiseaza pe ecran pixelul de coordonate (x,y) de culoarea data de parametrul culoare.

FUNCTII PENTRU AFISAREA TEXTELOR N MODUL GRAFIC


void for gettextsettings(struct textsettingstype far *textinfo); permite obtinerea unor informatii despre fontul curent, directie, marime, aliniere prin intermediul pointerului textinfo care indica o structura de forma: struct textsettingstype { int font; int direction; int charsize; int horiz; int vert; }; void far outtext(char far *text); afiseaza text n pozitia punctului curent. void far outtextxy(int x, int y, char far *text); afiseaza text n pozitia data de (x,y). void far settingsjustify(int oriz, int vert); aliniaza textul relativ la pozitia curenta. Implicit alinierea orizontala este la stnga textului, iar cea verticala la baza textului. Sunt urmatoarele posibilitati: orizontal LEFT_TEXT (0) punct curent la stnga textului CENTER_TEXT (1) punct curent n centrul textului RIGHT_TEXT (2) punct curent la dreapta textului vertical BOTTOM_TEXT (0) punct curent la baza textului CENTER_TEXT (1) punct curent n centrul textului TOP_TEXT (2) punct curent deasupra textului Valorile 0,1,2 din paranteze reprezinta alternative la folosirea constantelor simbolice. void far settingsstyle(int font, int directie, int marime); precizeaza fontul, directia (orizontala sau verticala) si marimea caracterelor care compun textul. int far textheight(char far *text); int far textwidth(char far *text); ntorc naltimea, respectiv lungimea masurata n pixeli pentru text.

FUNCTII PENTRU CONTROLUL CULORII


int far getbkcolor(void); ntoarce culoarea curenta a fondului. int far getcolor(void); ntoarce culoarea curenta de desenare.

int far getmaxcolor(void); ntoarce indexul ultimului element din paleta reprezentnd o culoare n modul grafic curent. void far getpalette(struct palettetype *far paleta); ntoarce paleta curenta si dimensiunea ei prin intermediul pointerului paleta catre o structura de forma: struct palettetype { unsigned char dimens_paleta; signed char colors[16]; }; void far setallpalette(struct palettetype far *paleta); schimba toata paleta. int far setbkcolor(int culoare_noua); schimba culoarea fondului n culoarea_noua. void far setcolor(int culoare_noua); seteaza culoarea curenta de desenare drept culoare_noua. void far setpalette(int index, int culoare); arata pozitia din paleta unde se seteaza culoarea culoare.

FUNCTII PENTRU DESENARE SI UMPLERE


FUNCTII PENTRU DESENARE void far arc(int x, int y, int unghi_inceput,int unghi_sfarsit, int raza); deseneaza un arc de cerc de raza raza, ncepnd cu unghi-inceput si terminnd cu unghisfarsit. Cele doua unghiuri pot lua valori ntre 0 si 360 grade. Cnd unghi-inceput este 0, iar unghi-sfarsit 360 grade se obtine un cerc. void far circle(int x, int y, int raza); deseneaza un cerc de raza raza si centru (x,y). void far drawpoly(int nr, int far *tab_coord); deseneaza o linie frnta care trece prin nr puncte ale caror coordonate se afla n tabelul tab_coord de forma: tab_coord[nr][2]; Se uneste punctul de coordonate (tab_coord[0][0],tab_coord[0][1]) cu punctul de coordonate tab_coord[1][0],tab_coord[1][1] s.a.m.d.. void far getaspectratio(int far *xaspect, int far *yaspect); permite obtinerea valorilor pentru parametrii xaspect si yaspect. Aceste valori folosesc la corecta dimensionare a desenelor, care, n mod normal ies deformate din cauza diferentei (ntlnite la majoritatea adaptoarelor) ntre lungimea si latimea unui pixel.

void far getlinesettings(struct linesettingstype far *inf_line); ofera informatii despre stilul, modelul si lungimea liniei prin intermediul pointerului inf_line. Acest pointer arata catre structura linesettingstype declarata n graphics.h. int far getx(void); int far gety(void); ntorc coordonatele (x,y) ale punctului curent. void far line(int x1, int y1, int x2, int y2); deseneaza o linie din punctul de coordonate (x1,y1) n punctul de coordonate (x2,y2). void far linerel(int dx, int dy); deseneaza o linie ntre punctul curent de coordonate (x,y) si punctul de coordonate (x+dx,y+dy). void far lineto(int x, int y); deseneaza o linie ntre punctul curent si punctul de coordonate (x,y). void far moverel(int dx, int dy); muta pozitia curenta din punctul de coordonate (x,y) n punctul de coordonate (x+dx,y+dy). void far moveto(int x, int y); muta pozitia punctului curent n punctul de coordonate (x,y). void far rectangle(int x1, int y1, int x2, int y2); deseneaza un dreptunghi cu coltul din stnga-sus n punctul de coordonate (x1,y1) si coltul din dreapta-jos n punctul de coordonate (x2,y2). void far setlinestyle(int stil, unsigned model, int grosime); seteaza stilul si grosimea liniei. Parametrul stil poate lua una din valorile 0,1,2,3,4 sau corespunzator constantele simbolice: SOLID_LINE (linie continua), DOTTED_LINE (linie punctata), CENTER_LINE (linie centrata), DASHED_LINE (linie ntrerupta), USERBIT_LINE (linie definita de utilizator). Daca stilul este USERBIT_LINE trebuie dat modelul utilizatorului n parametrul model, sub forma unui cod pe 16 biti. Parametrul grosime poate lua 2 valori: NORM_WIDTH (sau 1) si THICK_WIDTH (sau 3) pentru 1 pixel latime respectiv 3 pixeli latime. FUNCTII PENTRU UMPLERE void far bar(int x1, int y1, int x2, int y2); deseneaza si umple o bara delimitata de punctul (x1,y1) stnga-sus si (x2,y2) dreapta-jos. void far bar3d(int x1, int y1, int x2, int y2, int adancime, int capac); deseneaza si umple o bara tridimensionala delimitata de punctul (x1,y1) stnga-sus, (x2,y2) dreapta-jos si avnd adancime pixeli. Daca capac are valoarea 0 se deseneaza si >capacul> barei, iar n caz contrar nu. Ultima varianta permite plasarea n >stiva> a mai multor bare. void far fillellipse(int x,int y, int razax, int razay); deseneaza si umple o elipsa de centru (x,y) si raze razax si razay. void far fillpoly(int nr, int far *tab_coord_poligon); deseneaza si umple un poligon ale carui vrfuri au coordonatele n punctele tabloului tab_coord_poligon.

void far floodfill(int x,int y, int culoare_contur); umple suprafata care contine punctul (x,y) si este marginita de un contur de culoare, culoare_contur. void far getfillsettings(struct fillsettingstype, far *filinfo); ofera informatii despre modelul curent de umplere si culoarea de umplere. Structura fillesettingstype este declarata n graphics.h si are forma: struct fillsettingstype { int patern; int color; }; void far pieslice(int x, int y, int unghi_inceput, int unghi_sfarsit, int raza); deseneaza si umple un sector de cerc de centru (x,y), raza raza, delimitat de unghi_inceput si unghi_sfarsit. void sector(int x,int y, int raza_x, int raza_y, int unghi_inceput, int unghi_sfarsit); deseneaza si umple un sector de elipsa de centru (x,y), de raze raza_x si raza_y delimitat de unghi_inceput si unghi_sfarsit. void far setfillpattern(char *model, int culoare); defineste un model de umplere al utilizatorului. void far setfillstyle(int model, int culoare); seteaza un model de umplere si o culoare de umplere. Exemple de modele de umplere: EMPTY_FILL (sau 0) - umplere cu culoarea de fond, SOLID_FILL (sau 1) - umplere uniforma, LINE_FILL (sau 2) - hasura orizontala etc.. far

S-ar putea să vă placă și