[Www.fisierulmeu.ro] Programarea in Limbajul c

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 coborât 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

Transcript of [Www.fisierulmeu.ro] Programarea in Limbajul c

Page 1: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 coborât 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

Page 2: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 3: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.2-c 2.3-b 2.4-a 2.5-b 2.6-b 2.7-b 2.8-c 2.9-b 2.10-c 2.11-c

Page 4: [Www.fisierulmeu.ro] Programarea in Limbajul c

TESTE DE CONTROL

3.1 În C exista tipurile fundamentale de date:

a) char, int, float, double, void b) char, integer, real, double, void c) char, int, float, double, nul, boolean d) 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

float x,y; a) este echivalenta cu float x;float y; b) este gresita c) este echivalenta cu x,y:float; d) 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);

Page 5: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) este gresita deoarece %d este descriptor pentru tipul int, nu pentru tipul char b) este corecta si afiseaza codul ASCII al caracterului q c) este corecta si afiseaza codul ASCII al caracterului a d) 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

Page 6: [Www.fisierulmeu.ro] Programarea in Limbajul c

c) numarul 439 scris în baza 16

3.16 Secventa de program

int x=1011; printf(”\n %x”,x);

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 float x; scanf(”%f”,&x);

se poate afirma ca: a) sunt corecte si au acelasi efect b) prima secventa este corecta si a doua incorecta c) 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

Page 7: [Www.fisierulmeu.ro] Programarea in Limbajul c

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;

Page 8: [Www.fisierulmeu.ro] Programarea in Limbajul c

c) este gresita deoarece operatorul de atribuire apare de mai multe ori

3.30 Expresia

x+=1;

a) este gresita b) este corecta si echivalenta cu x=x+1; c) este corecta si echivalenta cu x++; d) este corecta si echivalenta cu ++x;

3.31 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; 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

Page 9: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) a|b ia valoarea 1 b) a&b ia valoarea 1 c) a^b ia valoarea 0 d) ~a ia valoarea 0

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

a) a|b ia valoarea 1 b) a&b ia valoarea 0 c) a^b ia valoarea 1 d) ~b ia valoarea 1

3.42 Secventa de program

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:

Page 10: [Www.fisierulmeu.ro] Programarea in Limbajul c

a) ~i+1 b) ~i-1 c) 2-i

3.45 Operatia x<<3 echivaleaza cu:

a) o înmultire a lui x cu 3 b) o împartire a lui x la 3 c) o înmultire a lui x cu 23 d) o împartire a lui x cu 23

3.46 Operatia x>>2 echivaleaza cu:

a) o înmultire a lui x cu 2 b) o împartire a lui x la 2 c) o înmultire a lui x cu 22 d) o împartire a lui x cu 22

3.47 Secventa de program

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

RASPUNSURI

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

Page 11: [Www.fisierulmeu.ro] Programarea in Limbajul 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

Page 12: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 13: [Www.fisierulmeu.ro] Programarea in Limbajul c

a) amic b) Amare c) amic

Amare

4.9 Secventa de program

int x=3; if (x=4) printf(”\n x=%i”,x);

a) este gresita b) este corecta, dar nu afiseaza nimic c) este corecta si afiseaza valoarea 4 d) este corecta si afiseaza valoarea 3

4.10 Secventa de program

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

a) afiseaza amic b) afiseaza Amare c) afiseaza

amic Amare

d) este gresita deoarece al doilea case nu se termina cu break 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);

Page 14: [Www.fisierulmeu.ro] Programarea in Limbajul c

i--; a) afiseaza valoarea 3 de 3 ori b) afiseaza valoarea 3 de o infinitate de ori (bucla eterna) c) contine o bucla eterna si nu afiseaza nimic d) 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

int i=0; for (;i<3;i++); printf(”%3i”,i);

a) este gresita sintactic deoarece lipseste componenta de initializare a instructiunii for

b) afiseaza valorile 0 1 2 3 c) afiseaza valorile 0 1 2 d) afiseaza valoarea 3 e) contine o bucla eterna afisându-se permanent valoarea 0 f) 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

Page 15: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

int x=1,i; for(i=1;x<=3;i++)

scanf(”%i”,&x); a) functia scanf() se executa de 3 ori b) functia scanf() se executa de o infinitate de ori c) instructiunea for este gresita deoarece contorul i nu se testeaza

niciodata d) 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);

Page 16: [Www.fisierulmeu.ro] Programarea in Limbajul c

while (x>0); a) citeste x pâna când x>0 b) citeste x cât timp x>0 c) este gresita deoarece functia scanf() nu este pusa între acolade d) nu executa nici o citire a lui x

4.24 Secventa de program

int j; do

j=3; while (j<=4);

{ printf(”%3i”,j); j++; }

a) afiseaza valoarea 3 b) afiseaza valorile 3 4 c) contine o bucla do-while eterna d) contine o bucla while eterna e) este gresita

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 j=1 i=2 j=2

b) i=1 j=1 i=1 j=2 i=2 j=1 i=2 j=2

c) i=1 j=1 i=2 j=1 i=1 j=2 i=2 j=2

Page 17: [Www.fisierulmeu.ro] Programarea in Limbajul c

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;

Page 18: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 19: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.2-d 4.3-c 4.4-b 4.5-c 4.6-c 4.7-b 4.8-c 4.9-c 4.10-a 4.11-c 4.12-b 4.13-c 4.14-b 4.15-c 4.16-d 4.17-c 4.18-a 4.19-d 4.20-a 4.21-a 4.22-b 4.23-b 4.24-c 4.25-b 4.26-b 4.27-b 4.28-b 4.29-a 4.30-b 4.31-c 4.32-a 4.33-a 4.34-b, c, d

Page 20: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 întâmplatoare 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 decât 2 sau 3

Page 21: [Www.fisierulmeu.ro] Programarea in Limbajul c

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”;

Page 22: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 decât zero c) mai mare strict decât 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);

Page 23: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.3-b 5.4-b 5.5-b 5.6-b 5.7-a 5.8-b 5.9-b 5.10-c 5.11-b 5.12-b 5.13-c 5.14-a 5.15-a, b

5.16-a, b, c 5.17-a, b

Page 24: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 25: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 26: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

6.13 În secventa de program

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) afiseaza valoarea 3 b) afiseaza valoarea 2 c) este gresita, deoarece în expresia p=x; se atribuie un nume de tablou unui pointer d) este gresita, deoarece adunarea unui întreg la un pointer (expresia p+=2;) este

interzisa

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

a) x[i] b) x+i c) &x[i] d) *(x+i)

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

Page 27: [Www.fisierulmeu.ro] Programarea in Limbajul c

a) x[i] b) x+i c) &x[i] d) *(x+i)

6.17 Secventa de program

int *p,**q,x=10; p=&x; q=&p; printf(”\n %d”,**q);

a) 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

RASPUNSURI

6.1-b 6.2-a 6.3-c 6.4-b 6.5-a 6.6-b 6.7-b 6.8-b, c 6.9-a, b, c 6.10-c, e 6.11-b 6.12-c 6.13-b 6.14-a 6.15-b, c 6.16-a, d 6.17-c, d

Page 28: [Www.fisierulmeu.ro] Programarea in Limbajul 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(), câmpul x este tratat ca o variabila

7.2 Selectarea unui câmp din structura se face utilizând 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) *p.x

b) (*p).x

c) *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 câmpurilor structurii x câmpurilor 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 câmp b) câmpul x nu este selectat cu ? ci cu ajutorul operatorului punct c) câmpul y nu este selectat cu operatorul punct, ci cu ajutorul operatorului ?

7.6 Structura

Page 29: [Www.fisierulmeu.ro] Programarea in Limbajul c

struct persoana { int salariu; struct persoana *p; struct inform

{ long int telefon; char *adresa; }inf;

}pers;

este: a) gresita, deoarece câmpul 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 câmp cu nume comun (câmpul x) b) este corecta, deoarece doua structuri diferite pot avea câmpuri cu nume comune

7.8 Structura

struct a {

int x; float y[3][2];

}a;

a) este incorecta, deoarece contine un câmp 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 câmpurile pu si valoare nu sunt declarate sub forma

float pu; float valoare;

c) este corecta

Page 30: [Www.fisierulmeu.ro] Programarea in Limbajul c

7.10 Lungimea unei structuri se afla

a) adunând lungimile truturor câmpurilor care compun structura b) utilizând operatorul sizeof

7.11 Structura

struct a {

unsigned adm:1; int nota;

}elev;

a) este gresita deoarece contine un câmp de biti de lungime 1 bit b) este corecta, deoarece o structura poate contine atât câmpuri obisnuite cât si

câmpuri de biti

7.12 Structura

struct a {

unsigned:4; unsigned x:2; float y:3;

}; este gresita deoarece a) primul câmp de biti nu are nume b) al treilea câmp 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 câmpurilor

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 câmpului a i se atribuie valoarea 10 c) este corecta iar câmpului b i se atribuie valoare 10

7.16 Lungimea unei uniuni se afla

a) calculând maximul dintre lungimile membrilor uniunii b) utilizând 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

Page 31: [Www.fisierulmeu.ro] Programarea in Limbajul c

b) este corecta si afiseaza cuvântul Dacia c) este incorecta deoarece este ilegala atribuirea masina_ta=Dacia;

7.18 Numele simbolice dintr-o lista de enumerare

a) se pot atribui unei variabile de tip corespunzator b) se pot citi de la tastatura c) se pot scrie nemijlocit d) pot figura ca o constanta case într-o instructiune switch

7.19 În secventa de program

enum luna_an{ianuarie,martie=3,mai};

valoarea atasata numelui simbolic mai este:

a) 3

b) 4

c) 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.2-a, c 7.3-b 7.4-a, b 7.5-b, c 7.6-b 7.7-b 7.8-b 7.9-c 7.10-b 7.11-b 7.12-b 7.13-a 7.14-b 7.15-b 7.16-b 7.17-a 7.18-a, d 7.19-b 7.20-b

Page 32: [Www.fisierulmeu.ro] Programarea in Limbajul c

TESTE DE CONTROL

8.1 Definitia unei functii C se poate face

a) numai în forma moderna b) atât în forma clasica cât 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 }

b) float min(a,b)

int a,b; { instructiuni }

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) poate sa lipseasca complet, dar trebuie pastrate parantezele rotunde b) poate sa lipseasca si acest lucru sa fie semnalizat prin cuvântul void c) poate sa contina doar numele tipurilor de date ale parametrilor d) poate sa contina doar numele parametrilor

Page 33: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

a) 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 întâlnirea acoladei }

8.9 Functia

void f(float x) {

float y=x; return y*y;

}

a) 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

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

Page 34: [Www.fisierulmeu.ro] Programarea in Limbajul c

b) daca lista declaratii parametrii este vida trebuie scris obligatoriu cuvântul void între parantezele rotunde

c) daca lista declaratii parametrii este vida este recomandabil sa se scrie cuvântul 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) atât prototip cât 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 atât fix cât 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 întâmplare 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

a) 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

Page 35: [Www.fisierulmeu.ro] Programarea in Limbajul c

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];

Page 36: [Www.fisierulmeu.ro] Programarea in Limbajul c

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]);

}

a) este incorecta, deoarece parametrul tablou x nu poate avea prima dimensiune neprecizata

b) este incorecta deoarece parametrul tablou x nu poate avea atât prima cât si a doua dimensiune neprecizata

c) este corecta

8.25 Secventa de program

void f(int x);

Page 37: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) numai prin referinta b) numai prin valoare c) si prin valoare si prin referinta d) numai o data cu întreaga structura

Page 38: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) 7 6 5 4 3 2 1 0

b) 0 1 2 3 4 5 6 7

8.33 Secventa de program

Page 39: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.2-b 8.3-a 8.4-a 8.5-a, b, c 8.6-b 8.7-c 8.8-b 8.9-a 8.10-b, c 8.11-a, c, d 8.12-a 8.13-b 8.14-c 8.15-a, b 8.16-b 8.17-b 8.18-c 8.19-a 8.20-c 8.21-c 8.22-c 8.23-b 8.24-b 8.25-a 8.26-b 8.27-a 8.28-c 8.29-c 8.30-b, c 8.31-a 8.32-b 8.33-c 8.34-c

Page 40: [Www.fisierulmeu.ro] Programarea in Limbajul 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 atât de tip float cât 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 când 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

Page 41: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 când

a) este declarata în afara functiei main() b) este declarata în afara oricarei functii

9.8 Variabilele globale fac parte implicit din clasa

a) register

b) auto

c) 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.2-b 9.3-b 9.4-b 9.5-a, b, c 9.6-b, c, d 9.7-b 9.8-c 9.9-c, d

Page 42: [Www.fisierulmeu.ro] Programarea in Limbajul 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)

a) 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

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 sfârsitul 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) fgetc()

b) getch()

c) getc()

10.7 Apelurile de functii

fgetc(stdin); si

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) fputc()

b) putc()

c) putch()

Page 43: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) fgets()

b) gets()

10.11 Pentru scrierea unui sir de caractere într-un fisier creat pe disc se poate folosi functia

a) puts()

b) fputs()

10.12 Apelurile de functii

fgets(nume_sir,nr_caractere,stdin); si

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 sfârsitul fisierului si fals în caz contrar b) întorce valoarea fals daca nu s-a atins sfârsitul fisierului si adevarat în caz contrar

10.17 Functia rewind() pozitioneaza indicatorul de pozitie al fisierului

a) la începutul sau b) la sfârsitul sau

10.18 Functia fseek()

a) pozitioneaza indicatorul de pozitie al fisierului la o valoare calculata relativ fata de una din valorile de referinta: începutul fisierului, sfârsitul 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

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

Page 44: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

RASPUNSURI

10.1-a 10.2-b 10.3-a, b, c 10.4-a, b 10.5-a, b 10.6-a, c 10.7-a 10.8-a, b 10.9-a 10.10-a 10.11-b 10.12-a 10.13-b 10.14-a, b 10.15-a 10.16-b 10.17-a 10.18-a 10.19-c, d

Page 45: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 N cu 10

11.2 Secventa de program

#define A 10 #define B A+20

a) 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

11.3 Secventa de program

#define X 10 #define Y X+5 . . . . . . int v; v=X+3*Y; . . . . . .

a) 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

11.4 Pentru a anula efectul directivei

#define M 20

se foloseste

a) #define

b) #define M

c) #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

Page 46: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) atât sub forma 1 cât si sub forma 2

11.9 Un fisier antet

a) poate contine prototipuri de functii b) poate contine definitii de functii c) poate contine definitii de constante d) poate contine definitii de date sau constante structurate e) 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.2-b 11.3-b 11.4-c 11.5-b 11.6-a 11.7-b, c 11.8-c 11.9-a, c, e 11.10-b, c 11.11-b, c

Page 47: [Www.fisierulmeu.ro] Programarea in Limbajul 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.

Raspândirea 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 raspândirea 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 târziu, î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 coborât, 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. Limbajul 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 coborât, 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 coborât. Limbajul C este un limbaj de nivel mediu.

CÂTEVA 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

Page 48: [Www.fisierulmeu.ro] Programarea in Limbajul c

2

(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 coborât (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.

Page 49: [Www.fisierulmeu.ro] Programarea in Limbajul c

3

?? 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) obtinându-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 Functie apelanta apelata

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.

Apel functie

Page 50: [Www.fisierulmeu.ro] Programarea in Limbajul c

4

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 utilizând 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:

Se observa ca programul este alcatuit dintr-o singura functie, functia main() si foloseste

functia standard printf() cu prototipul în ”stdio.h”.

Primul program C

#include ”stdio.h”

void main(void) {

printf(”\nPrimul program C!”); }

Page 51: [Www.fisierulmeu.ro] Programarea in Limbajul c

2. Elemente de baza ale limbajului C Asa cum se întâmpla cu orice limbaj artificial, temelia pe care se cladesc programele C

este alcatuita din alfabet si vocabular (atomi lexicali). Combinând 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:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

?? litere mici ale alfabetului englez:

a b c d e f g h i j k l m n o p q r s t u v w x y z

?? 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 Câteva semne folosite în C si denumirea lor

Semn Nume Semn Nume | bara verticala / slash \ backslash ~ tilda # diez _ liniuta de subliniere (underscore) & ampersand ^ 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.

Page 52: [Www.fisierulmeu.ro] Programarea in Limbajul c

6

Tabelul 2.2 Codurile backslash din C =i semnifica\ia lor

Coduri backslash Semnificatie \a Alarma \b backspace (recul cu o pozitie) \f form feed (salt pagina noua) \n newline (salt la rând nou si de la capat) \r carriage return (retur car) \t horizontal tab (tab orizontal) \v vertical tab (tab vertical) \’ apostrof \” ghilimele \0 caracter nul \\ backslash

\ddd caracter ASCII în notatie octala \xdd 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, provocând erori; ?? Standardul ANSI C nu limiteaza numarul de caractere (lungimea) unui identificator. Un

compilator C va ignora însa caracterele aflate pe pozitii mai mari decât un numar prestabilit.

Page 53: [Www.fisierulmeu.ro] Programarea in Limbajul c

7

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 break case char continue default do double else extern

float for goto if Int long register return short Sizeof

static struct switch typedef Union unsigned while

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 lânga 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 cdecl far huge interrupt Near

pascal _cs _ds _es _ss

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 când 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.

Page 54: [Www.fisierulmeu.ro] Programarea in Limbajul c

8

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’ reprezentând respectiv a,newline, apostrof,7.

O constanta sir este o secventa de litere, cifre si simboluri incluse între ghilimele. Exemple de constante sir: ”\n Acesta e un sir”, ”Str. Cameliei, nr.3” etc.. Constantele sir se memoreaza în octeti consecutivi (un octet pentru fiecare caracter). Sfârsitul 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 Semnificatie [] paranteze drepte (stânga si dreapta) () paranteze rotunde (stânga si dreapta) . membru structura

? referinta indirecta la membru de structura ++ incrementare (prefix sau postfix) -- Decrementare (prefix sau postfix)

sizeof 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 stânga >> 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

op= atribuire compusa % modul , virgula (operator de secventiere)

Page 55: [Www.fisierulmeu.ro] Programarea in Limbajul c

9

Observatie. Caracterul op de la atribuirea compusa poate fi unul din semnele * / % + - >> << ?& ^ rezultând 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), sfârsit de linie, sfârsit 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 usurând întelegerea si depanarea lui. Un comentariu poate fi plasat oriunde în program si se poate întinde pe unul sau mai multe rânduri.

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.

Page 56: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 decât 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 utilizând 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 atât a întregilor cu semn cât 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].

Având î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.

Page 57: [Www.fisierulmeu.ro] Programarea in Limbajul c

11

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 întâi 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? Când 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 adaugând 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.

Page 58: [Www.fisierulmeu.ro] Programarea in Limbajul c

12

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;

?? Atât la scriere cât si la citire, descriptorii de format sunt pusi în corespondenta de la stânga 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 decât numarul descriptorilor de format, datele de iesire sunt nedefinite; daca numarul argumentelor este mai mare decât 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 Utilizare %u numere întregi zecimale fara semn %d sau %i numere întregi zecimale cu semn %c caracter %f numere reale în notatie uzuala %e sau % E numere reale în notatie stiintifica (e sau E) %x sau %X hexazecimal fara semn (litere mici sau majuscule) %o octal fara semn %s sir de caractere %g sau %G 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)

%p valoare pointer

Page 59: [Www.fisierulmeu.ro] Programarea in Limbajul c

13

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 utilizând %ld,%li,%lu,%lo sau %lx. Valorile de tip short int se afiseaza utilizând %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 stânga 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 câte un rând.

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

Page 60: [Www.fisierulmeu.ro] Programarea in Limbajul c

14

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

Page 61: [Www.fisierulmeu.ro] Programarea in Limbajul c

15

Dimensiunea câmpului 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 câmpului de afisare. Daca sirul sau numarul care se afiseaza are mai multe caractere decât dimensiunea minima precizata, atunci afisarea va fi integrala, în caz contrar se completeaza cu spatii pâna 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 câmpului de afisare:

x=76.5432; printf("\n x=%f",x); /* x=76.543198 */ printf("\n x=%e",x); /* x=7.65320e+01 */ printf("\n x=%E",x); /* x=7.65320E+01 */ printf("\n x=%g",x); /* x=76.543200 */ printf("\n x=%G",x); /* x=76.543200 */ x=-0.123456789; printf("\n x=%f",x); /* x=-0.123457 */ printf("\n x=%e",x); /* x=-1.234568e-01 */ printf("\n x=%E",x); /* x=-1.234568E-01 */ printf("\n x=%g",x); /* x=-0.123457 */ printf("\n x=%G",x); /* x=-0.123457 */ printf("\n %s","testare"); }

Page 62: [Www.fisierulmeu.ro] Programarea in Limbajul c

16

Precizia de afisare se specifica printr-un punct urmat de un întreg pozitiv. Specificatorul de precizie astfel obtinut se plaseaza imediat dupa dimensiunea câmpului de afisare (când 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 stânga a datelor plasând semnul – (minus) imediat dupa semnul % în cadrul descriptorului.

Afisarea unor valori folosind descriptori de format cu precizarea dimensiunii câmpului de afisare

#include "stdio.h" void main(void) { int i; float x; i=4567; printf("\n i=%4i",i); /* i=4567 */ printf("\n i=%6i",i); /* i= 4567 */ printf("\n i=%3i",i); /* i=4567 */ printf("\n i=%06i",i); /* i=004567 */ x=76.123001; printf("\n x=%10f",x); /* x= 76.123001 */ printf("\n x=%010f",x); /* x=076.123001 */ printf("\n %3s","testare"); /* testare */ printf("\n %10s","testare"); /* testare */ }

Page 63: [Www.fisierulmeu.ro] Programarea in Limbajul c

17

Programul urmator ilustreaza toate cazurile prezentate:

Functia de citire scanf() are forma generala:

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(), îndepartând %E,%X,%G. Folositi cu scanf(), descriptorii %f,%e,%g sunt echivalenti. ?? Lista de argumente este citita de la stânga 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.

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); /* i=0004567 */ printf("\n i=%7.3i",i); /* i= 4567 */ printf("\n i=%-7.3i",i); /* i=4567 */ x=76.123401; printf("\n x=%10.3f",x); /* x= 76.123 */ printf("\n x=%-10.3f",x); /* x=76.123 */ printf("\n x=%3.7f",x); /* x=76.1234010 */ printf("\n x=%10.2e",x); /* x= 7.61e+01 */ printf("\n x=%-10.1E",x); /* x=7.6E+01 */ printf("\n x=%10.3g",x); /* x= 76.1 */ printf("\n x=%-10.4G",x); /* x=76.12 */ printf("\n %.4s","testare"); /* test */ printf("\n %10.4s","testare"); /* test */ printf("\n %-10.4s","testare");/* test */ printf("\n %-1.10s","testare");/* testare */ }

Exemplu 3.6

Page 64: [Www.fisierulmeu.ro] Programarea in Limbajul c

18

?? 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 stânga 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;

?? 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.

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

Page 65: [Www.fisierulmeu.ro] Programarea in Limbajul c

19

FUNCTII SPECIALE PENTRU CITIREA/SCRIEREA CARACTERELOR LA NIVELUL 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 sfârsitul 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.

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

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

Page 66: [Www.fisierulmeu.ro] Programarea in Limbajul c

20

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

Tinând 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 Operatori 1 (paranteze, op. de selectie) () [] -> . 2 (op.unari) ++ -- ! ~ - + & * sizeof cast 3 (op. multiplicativi) % / * 4 (op. aditivi) + - 5 (op. shift) << >> 6 (op. relationali) < <= > >=

Page 67: [Www.fisierulmeu.ro] Programarea in Limbajul c

21

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) = += -= *= etc. 15 (secventiere) ,

Functioneaza, de asemenea, reguli de asociere de la stânga la dreapta sau de la dreapta la stânga. Singurii operatori care se asociaza de la dreapta la stânga sunt operatorii unari si operatorul ?:, restul se asociaza de la stânga 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 stâng si membrul drept al unei atribuiri se mai numesc valoare stâng[ (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 stâng (tipul lui v). Deoarece sizeof (int) <= sizeof (float) <= sizeof (double), se spune ca int este mai >slab> decât float, care este la rândul sau mai >slab> decât double. Daca membrul stâng este de un tip mai >slab> decât 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).

Page 68: [Www.fisierulmeu.ro] Programarea in Limbajul c

22

Î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. Când operandul apare în cadrul unei expresii, daca este precedat de operatorul ++ sau -- se executa întâi 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 stânga 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 întâi 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;

Page 69: [Www.fisierulmeu.ro] Programarea in Limbajul c

23

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

Page 70: [Www.fisierulmeu.ro] Programarea in Limbajul c

24

Modul de actiune al acestor operatori este prezentat în Tabelul 3.3.

Tabelul 3.3 Tabla valorilor de adevar pentru operatori logici

A B a && b a || b !a 0 0 0 0 1 0 1 0 1 1 1 0 0 1 0 1 1 1 1 0

Observatii:

Din Tabelul 3.2 rezulta ca operatorii logici && si || au o prioritate mai mica decât 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 întâi !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.

Afisarea valorilor unor expresii în care intervin operatori relationali

#include "stdio.h" #include "conio.h" void main(void) { float x=0, y=2.3; printf("\n x<y are valoarea %d",x<y) ; /*1*/ printf("\n x<=y are valoarea %d",x<=y); /*1*/ printf("\n x>y are valoarea %d",x>y) ; /*0*/ printf("\n x>=y are valoarea %d",x>=y); /*0*/ printf("\n x==y are valoarea %d",x==y); /*0*/ printf("\n x!=y are valoarea %d",x!=y); /*1*/ getch(); }

Page 71: [Www.fisierulmeu.ro] Programarea in Limbajul c

25

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 apartinând 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 stânga (shift stânga)

Modul de actiune al primilor 4 operatori pe bit e rezumat în Tabelul 3.4.

Tabelul 3.4 Tabla valorilor de adevar pentru operatorii pe bit &,|,^ =i ~

A b a & b a | b a ^ b ~a 0 0 0 0 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 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 0000 0101 0010 0100 0010 0100

x&y= 0000 0100 x|y= 0010 0101

0000 0101 0010 0100 0000 0101

x^y= 0010 0001 ~x= 1111 1010

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 stânga) 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

Page 72: [Www.fisierulmeu.ro] Programarea in Limbajul c

26

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 de 1 devin nuli ( codificarea). La o noua rulare este evident ca se obtine fisierul initial (decodificarea). ?? O posibilitate mai puternica de codificare si decodificare este operatorul ^. În acest caz pentru aceste operatii se foloseste o cheie. Reluând 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 stânga

si variabila >> numar intreg, pentru deplasare la dreapta.

Aici, numar_intreg se refera la numarul de pozitii cu care vor fi deplasati spre stânga, 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 stânga, 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.

Page 73: [Www.fisierulmeu.ro] Programarea in Limbajul c

27

Daca deplasarea este spre dreapta, bitii liberi din stânga 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 stânga se completeaza cu 1.

Exemplu. Sa consideram x=-9. Reprezentarea sa în binar se face folosind codul complementar fata de 2. Acesta se obtine adunând 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 se reprezinta prin 0000 1001 ~9 este 1111 0110 ~9+1 este 1111 0111 -9 se reprezinta prin 1111 0111

În consecinta, tinând cont de precizarea facuta mai sus

x>>2 va produce 1111 1101

Observatii. Deplasarea spre stânga 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 decât 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.

Page 74: [Www.fisierulmeu.ro] Programarea in Limbajul c

28

CONVERSII DE TIP EXPLICITE (OPERATORUL CAST)

Operatorul de conversie explicita (cast) actioneaza temporar, fortând 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).

Page 75: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Page 76: [Www.fisierulmeu.ro] Programarea in Limbajul c

30

Observatii: Sintaxa generala a instructiunii if cere pe ambele alternative câte 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

Expresie

Instruc\iune_1 Instruc\iune_2

Fals Adev[rat

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

Page 77: [Www.fisierulmeu.ro] Programarea in Limbajul c

31

{ 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.

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

Expresie

Instruc\iune

Adev[rat Fals

Page 78: [Www.fisierulmeu.ro] Programarea in Limbajul c

32

?? 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 încât expresia expri sa fie adevarata, se executa instrn+1, în caz contrar se executa prima instructiune (considerând 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 adâncime mare (mult deplasata spre dreapta). Din acest motiv se foloseste de obicei forma:

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

Page 79: [Www.fisierulmeu.ro] Programarea in Limbajul c

33

if(expr1) instructiune1;

else if(expr2) instructiune2;

else if(expr3) instructiune3;

. . . . . . else

instructiunen+1;

numita si scara if-else-if.

?? Tinând 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. ?? Cuvântul 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.

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 cadranului I"); else if (x<0 && y>=0) printf("\n Numarul apartine cadranului II"); else if(x<0 && y<0) printf("\n Numarul apartine cadranului III"); else printf("\n Numarul apartine cadranului IV"); getch(); }

Page 80: [Www.fisierulmeu.ro] Programarea in Limbajul c

34

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 formele 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)

Page 81: [Www.fisierulmeu.ro] Programarea in Limbajul c

35

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

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;

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

Page 82: [Www.fisierulmeu.ro] Programarea in Limbajul c

36

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 încât expresia selectoare este egala cu ci se executa secventa instructiunik,k>=i pâna la primul break întâlnit sau pâna la sfârsitul 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.

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 neântâlnindu-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 întâmpla si cu grupurile de litere m,M si p,P.

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

Page 83: [Www.fisierulmeu.ro] Programarea in Limbajul c

37

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 cât timp conditie e

adevarata (diferita de zero). Atunci când 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.

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 ramâne 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.

Fals

Instruc\iune

Adev[rat

Condi\ie

Figura 4.3. Modul de lucru al instructiunii while

Page 84: [Www.fisierulmeu.ro] Programarea in Limbajul c

38

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 câte 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 sfârsitul 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 instructiune–actualizare cât timp conditia este îndeplinita.

Calculul lungimii unui sir de caractere citit de la tastatura; sfârsitul 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(); }

Page 85: [Www.fisierulmeu.ro] Programarea in Limbajul c

39

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.

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

Page 86: [Www.fisierulmeu.ro] Programarea in Limbajul c

40

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

Page 87: [Www.fisierulmeu.ro] Programarea in Limbajul c

41

for(;;);

creeaza un ciclu infinit sau o bucla eterna.

INSTRUCTIUNEA DO-WHILE

Este recomandabil sa se foloseasca instructiunea do-while când instructiunea care reprezinta corpul ciclului trebuie executata cel putin o data.

Forma generala a acestei instructiuni este:

do { instructiuni }

while(conditie);

Când 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 cât timp expresia conditionala conditie este adevarata. Organigrama de mai jos ilustreaza cu claritate modul de executie al ciclului 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).

Instruc\iune

Fals Adev[rat Condi\ie

Figura 4.4. Modul de executie al instructiunii do-while

Page 88: [Www.fisierulmeu.ro] Programarea in Limbajul c

42

Programul citeste doua numere a si b de la tastatura si afiseaza suma lor. Procesul continua cât 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 când este posibil) sau se iese din meniu.

#include "stdio.h" #include "conio.h" void main(void) { float a,b; char ch; 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:"); do

Page 89: [Www.fisierulmeu.ro] Programarea in Limbajul c

43

{ 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 când numarul citit de la tastatura este pozitiv.

#include "stdio.h" #include "conio.h" #include "math.h" void main(void) { float x; do

Page 90: [Www.fisierulmeu.ro] Programarea in Limbajul c

44

{ 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:

1 1 2 1 2 3 1 2 3 4 1 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:

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

Page 91: [Www.fisierulmeu.ro] Programarea in Limbajul c

45

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 târziu î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 când se doreste încheierea fortata a unui ciclu. În acest caz întâlnirea instructiunii break în corpul ciclului determina întreruperea ciclarii si trecerea controlului la prima instructiune care urmeaza dupa ciclu.

Programul rezolva urmatoarea problema: cât 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'); }

Page 92: [Www.fisierulmeu.ro] Programarea in Limbajul c

46

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

Programul tipareste de 10 ori câte 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(); }

Page 93: [Www.fisierulmeu.ro] Programarea in Limbajul c

47

INSTRUCTIUNEA CONTINUE

Instructiunea continue se foloseste numai în corpul unui ciclu; ea forteaza executia urmatoarei iteratii a ciclului, omitându-se instructiunile prezente între cele doua iteratii (locul unde se afla plasata în corpul ciclului si sfârsitul acestuia). Efectul instructiunii este urmatorul:

?? Când continue este întâlnita într-un ciclu for se continua cu executia componentei de actualizare si apoi a testului conditional. ?? Când este întâlnita într-un while sau do-while, se trece la executia expresiei conditionale.

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, îngreunând întelegerea lor. Ea poate

Programul citeste de la tastatura 10 numere întregi însumându-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(); }

Page 94: [Www.fisierulmeu.ro] Programarea in Limbajul c

48

fi însa evitata în orice situatie utilizând 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(); }

Page 95: [Www.fisierulmeu.ro] Programarea in Limbajul c

49

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.

Page 96: [Www.fisierulmeu.ro] Programarea in Limbajul c

5. Tablouri, siruri de caractere Notatia indiciala a variabilelor nu este noua; ea a fost si este folosita în special de matematicieni ori

de câte ori este nevoie ca o prelucrare sa fie exprimata cât mai sintetic. Este evident ca prelucrarea

S=a+b+c+d+e+f arata mult mai putin elegant decât ??

?6

1iixS În ultimul caz cele sase variabile

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 reprezentând 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 precizând 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 atât mai grava, cu cât, tinând 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).

Page 97: [Www.fisierulmeu.ro] Programarea in Limbajul c

51

TABLOURI MULTIDIMENSIONALE Daca elementele unui tablou unidimensional sunt la rândul 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.

Generalizând, 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.

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

Page 98: [Www.fisierulmeu.ro] Programarea in Limbajul c

52

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, considerând 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(); }

Page 99: [Www.fisierulmeu.ro] Programarea in Limbajul c

53

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 sfârsit linia 2. Matricea a va arata astfel:

1 2 3 4 5 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.

Page 100: [Www.fisierulmeu.ro] Programarea in Limbajul c

54

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]; Sfârsitul 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 decât 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 decât 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, permitând 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);

Page 101: [Www.fisierulmeu.ro] Programarea in Limbajul c

55

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.

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 pâna la apasarea tastei Enter.

FUNCTII CU PROTOTIPUL ÎN “STRING.H” Fisierul ”string.h” contine prototipurile functiilor specializate pentru manipularea sirurilor de

caractere. Iata câteva dintre acestea: ?? Functia strcpy() cu forma general:

strcpy(sir_destinatie, sir_sursa); copiaza sir_sursa în sir_destinatie;

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

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

Page 102: [Www.fisierulmeu.ro] Programarea in Limbajul c

56

?? Functia strcmp() cu forma generala:

int strcmp(sir1, sir2);

compara cele doua siruri caracter cu caracter si întoarce o valoare: <0 dac[ sir1<sir2; =0 dac[ sir1=sir2; >0 dac[ 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 sfârsitul sirului sir1 (concatenare).

Se citeste de la tastatura un numar neprecizat de siruri de caractere. Citirea se termina când se întâlneste 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(); }

Page 103: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 amândoua. 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.

Page 104: [Www.fisierulmeu.ro] Programarea in Limbajul c

58

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 stânga (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 este=%p",&x); printf("\n Valoarea lui x este=%i",x); printf("\n Valoarea lui x_ptr=%p",x_ptr); printf("\n Valoarea lui x=%i",*x_ptr); getch(); }

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 cât 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).

Page 105: [Www.fisierulmeu.ro] Programarea in Limbajul c

59

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 utilizând 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 ramân ocupate pâna 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;

Page 106: [Www.fisierulmeu.ro] Programarea in Limbajul c

60

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, întâlnind-o, compilatorul va memora sirul într-un tabel de siruri generând 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:

Page 107: [Www.fisierulmeu.ro] Programarea in Limbajul c

61

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: ?? când se lucreaza cu pointeri catre tablouri de dimensiune dim componente de tip tip, aritmetica folosita are unitatea egala cu dim*sizeof(tip). ?? când 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]);

Page 108: [Www.fisierulmeu.ro] Programarea in Limbajul c

62

/* 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();

Page 109: [Www.fisierulmeu.ro] Programarea in Limbajul c

63

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 câteva din efectele acestor combinatii (rezultatele afisarii sunt date în comentarii).

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.

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++); /* val=5 */ printf("\n Valoarea=%i",*p); /* val=4 */ p=x; printf("\n Valoarea=%i",(*p)++); /* val=5 */ printf("\n Valoarea=%i",*p); /* val=6 */ p=x; printf("\n Valoarea=%i",*++p); /* val=4 */ p=x; printf("\n Valoarea=%i",++*p); /* val=7 */ getch();

Page 110: [Www.fisierulmeu.ro] Programarea in Limbajul c

64

În Figura 6.1 se prezinta schema acestui tip de adresare.

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 câte 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].

Adresa Adresa Valoare

Pointer

Pointer

Page 111: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 câmpuri, membre ale

structurii; ?? tip reprezinta tipul câmpului 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 rând din cartea de telefoane.

Page 112: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 câmp din structura se face utilizând operatorul . sub forma: nume_variabila_structura.nume_camp

De exemplu, accesul la informatia continuta în câmpul adresa al variabilei pers1 se face utilizând constructia

pers1.adresa Asupra unui câmp selectat se pot aplica toate operatiile care se pot aplica unei variabile de

acelasi tip cu câmpul. 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 câmp al structurii se face utilizând 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 câmpul nume al acestei variabile se face sub forma

pers3->nume

Remarcam faptul ca operatorul -> este o sinteza a operatorilor * (indirectare) si . (selectie). Asadar, expresia

pers3->nume

este echivalenta cu expresia

(*pers3).nume

Marimea unei structuri se calculeaza utilizând 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 (însumând octetii ocupati de fiecare câmp) atât din motive de portabilitate cât si din ratiuni de corectitudine: în unele situatii, compilatorul, prin alinierile pe care le face, obtine un necesar de memorie mai mare decât dimensiunea obtinuta manual.

O variabila de tip structura poate fi initializata pe linia de declarare în maniera obisnuita:

Declaratie structura={lista_valori_initiale};

Page 113: [Www.fisierulmeu.ro] Programarea in Limbajul c

De exemplu, prin declaratia

Struct rand_tel pers1={”Dan”,”Ion”,”Ploiesti,str.Tei”,165239};

câmpurile 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 obtinându-se structuri imbricate. De exemplu, se poate imagina structura:

struct info_pers {

struct rand_tel info; int varsta; float salariu;

}angajat;

Referirea la un câmp al structurii incluse se face urmând modelul constructiei

angajat.info.nume

care indica numele unui angajat.

În interiorul unei structuri numele câmpurilor trebuie sa fie diferite, însa doua structuri diferite pot avea nume de câmpuri comune. De asemenea, atât numele structurii cât si numele unui câmp 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.

Câmpurile 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 sfârsit spre început.

Construirea unei liste si afisarea elementelor sale.

#include "stdio.h" #include "conio.h" #include "alloc.h"/*

Page 114: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 câmp 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.

Page 115: [Www.fisierulmeu.ro] Programarea in Limbajul c

Un rând 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);

Page 116: [Www.fisierulmeu.ro] Programarea in Limbajul c

getch(); }

CÂMPURI DE BITI

În C exista posibilitatea sa accesam un bit din memorie prin intermediul câmpurilor de biti. Forma generala a declaratiei unui câmp 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 câmpului, iar tip poate fi unsigned sau signed. Dupa cum se observa, un câmp 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 câmp se face folosind operatorii consacrati . sau ->.

Totusi, nu toate facilitatile oferite de structuri sunt valabile si în cazul special al câmpurilor de biti. De exemplu, nu se poate utiliza operatorul & pentru a lua adresa unui câmp. De asemenea, numele unui câmp nu poate fi numele unui tablou, iar ordinea câmpurilor este dependenta de implementare.

Câmpurile de biti îsi gasesc utilitatea în special în programarea de sistem (gestionarea 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 câmp 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;

Page 117: [Www.fisierulmeu.ro] Programarea in Limbajul c

Sa observam ca bitii comp5 si comp6 au fost omisi, lucru acceptat de compilator. O structura poate contine atât câmpuri obisnuite, cât si câmpuri 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 câmpurilor, numele variabilelor_uniune si numele oricarei variabile ramân valabile si aici. Pentru selectarea câmpurilor 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 câmp al uniunii.

Deosebirea fundamentala dintre o uniune si o structura consta însa în felul în care câmpurile folosesc memoria. La o structura, zonele de memorie rezervate câmpurilor sunt diferite pentru câmpuri diferite. La o uniune, toate câmpurile 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. Câmpurile 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.

Page 118: [Www.fisierulmeu.ro] Programarea in Limbajul c

Folosirea memoriei se face conform urmatoarei schite:

Figura 7.1. Modul de folosire a memoriei de catre câmpurile uniunii una

Este posibila initializarea unei uniuni cu o constanta care trebuie sa aiba tipul primului câmp declarat.

De exemplu, secventa de program

union {

int i; float f;

}x={324};

va avea ca efect initializarea primului câmp (x.i=324).

Folosind uniunile si câmpurile 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)

v. f

v. i

Page 119: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 sfârsitul 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:

?? câmpul npr pentru memorarea numelui si prenumelui studentului (40 de caractere); ?? câmpul tip (un caracter) unde aflam informatii despre continutul curent al uniunii care

urmeaza; ?? uniunea sit cu urmatoarele câmpuri:

?? media studentului, când câmpul tip are valoarea ’m’; ?? vectorul note[5], cu cele 5 note de la examene, când câmpul tip are valoarea

’e’; ?? un pointer catre prima componenta a unei liste liniare simplu înlantuita, care

contine numele disciplinelor restante, când câmpul tip are valoarea ’r’.

#include "stdio.h" #include "conio.h" #include "string.h" #include "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

Page 120: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 121: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 122: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 decât 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 decât, 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(); }

Page 123: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 rând, î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, considerând 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 modificând doar declaratia întregilor astfel:

Page 124: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Page 125: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 sfârseste cu o instructiune din main(). Pentru a usura întelegerea elementelor de limbaj prezentate pâna 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

Page 126: [Www.fisierulmeu.ro] Programarea in Limbajul c

care lista este vida. Mai mult, recomandarea este ca precizarea unei liste vide de parametri sa se faca prin scrierea cuvântului 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 ramâne 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 }

Page 127: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 târziu 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();

Page 128: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 cuvântul 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.

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

Page 129: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Page 130: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 ramân 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 vizeaza 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 utilizând 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;

Page 131: [Www.fisierulmeu.ro] Programarea in Limbajul c

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() a=3 b=5

apel comut2() a=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

Transfer prin referin\[

main()

comut1()

main()

comut2()

a = 3 x = 5 a ? 5 ? x b = 5 y = 3 b ? 3 ? y

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

Page 132: [Www.fisierulmeu.ro] Programarea in Limbajul c

TRANSFERUL TABLOURILOR CATRE FUNCTII

O exceptie de la regula transmiterii prin valoare o reprezinta cazul când 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]) {

Page 133: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 134: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Page 135: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 136: [Www.fisierulmeu.ro] Programarea in Limbajul c

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, simulând functia de alocare, sub forma

((int*)y)[n*i+j]

Introducând 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 utilizând algoritmul de sortare prin selectie.

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

Page 137: [Www.fisierulmeu.ro] Programarea in Limbajul c

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]);

}

Page 138: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 impara",i); else printf("\n Componenta %i este 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 supraîncarcarii 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.

Page 139: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 câmp de-al ei în functia modi(). Transmiterea se face prin referinta.

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

Page 140: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Page 141: [Www.fisierulmeu.ro] Programarea in Limbajul c

Functiei afis_val() i se furnizeaza câmpul 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 cuvântul 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.

Page 142: [Www.fisierulmeu.ro] Programarea in Limbajul c

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..

Presupunând 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.

Page 143: [Www.fisierulmeu.ro] Programarea in Limbajul c

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"};

Page 144: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

? ?? ??

??

?adacbadifadacbasum

fx ??

,,

00

??

xx

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 atât adresa functiei sum() cât si cea a functiei dif().

Programul ilustreaza folosirea pointerilor catre functii

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

Page 145: [Www.fisierulmeu.ro] Programarea in Limbajul c

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:

Page 146: [Www.fisierulmeu.ro] Programarea in Limbajul c

tip f(lista_declaratii_parametri) tip g(lista_declaratii_parametri) { { apel g() 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

Figura 8.2 Reprezentarea statica a stivei

unde vârf 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.

x x x

V`rf

Page 147: [Www.fisierulmeu.ro] Programarea in Limbajul c

FUNCTII DIRECT RECURSIVE

Cazul de recursivitate cel mai des întâlnit este cel al functiilor direct recursive. Este situatia în care functia apelanta si functia apelata coincid. Adâncimea 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 amânate 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 pâna la oprirea autoapelului. Oprirea autoapelului înseamna >coborârea> pas cu pas în stiva: pentru fiecare pas se preiau din stiva valorile corespunzatoare si se executa instructiunile amânate prin autoapel. Pentru o mai buna întelegere, prezentam mai jos exemplul clasic al calculului recursiv al factorialului. Functia factorial se poate defini recursiv astfel:

? ? ? ????

????

?1111

ndacanfnndaca

nf

O posibila implementare este:

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 actuali 4,3,2,1. Apelul f(1) furnizeaza rezultatul 1 pentru rezolvarea instructiunii amânate 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:

? 1 ? 2 2 ? 3 3 3 ? 4 4 4 4 Stiv[ vid[ Apel f(4) Apel f(3) Apel f(2) Apel f(1)

Figura 8.3 Starea stivei în timpul executiei succesive a autoapelului

Page 148: [Www.fisierulmeu.ro] Programarea in Limbajul c

Rezolvarea apelurilor se face dupa urmatoarea schema:

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

3 f(4)=1*2*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:

Page 149: [Www.fisierulmeu.ro] Programarea in Limbajul c

main()

g(6)

g(4) g(2) g(0)

{ int x=6 if(6) if(4) if(2) if(0) g(6); { { { iesire

din getch(); g(4); g(2); g(0); autoapel } printf

("%i",x); }

printf ("%i",x); }

printf ("%i",x); }

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 când 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 când adâncimea 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, utilizând 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 utilizând tija c este echivalenta cu succesiunea de pasi:

?? mutarea a n-1 discuri de pe tija a pe tija c utilizând b; ?? mutarea singurului disc ramas, de pe tija a, pe tija b; ?? mutarea celor n-1 discuri de pe tija c pe tija b utilizând a.

Page 150: [Www.fisierulmeu.ro] Programarea in Limbajul c

Grafic, rezolvarea se poate schita astfel:

Figura 8.6. Schema deplasarilor celor n discuri de pe tija a, pe tija b, utilizând tija c

Notând cu Hanoi (n, a, b, c) solutia problemei, tinând cont de cele spuse anterior obtinem exprimarea sa recursiva.

? ? ? ? ? ?Hanoi n a b cab n

Hanoi n a c b abHanoi n c b a, , ,

, , , , , ,?

?

? ????

1

1 1

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)

<=>

n-1

n-1

a b c

n

a b c

1

1

n

Page 151: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 152: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 când 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 sfârsitul 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 apartinând clasei automatic.

Functia citeste() din programul urmator citeste un numar întreg x daca n=1 si n

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=");

Page 153: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 atât pentru întregul x cât 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 având î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 unei 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 oricâte 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 când 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);

Page 154: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) { static int s=1; s+=2; return s; }

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;

Page 155: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 într-un fisier sa fie recunoscuta si în functiile celorlalte fisiere 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 presupunând ca functia main() va fi editata în fisierul princ.c, iar functia suma() în

fisierul sum.c.

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

Page 156: [Www.fisierulmeu.ro] Programarea in Limbajul 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 este %i",n,y); getch(); } /* fisierul sum.c */ /* variabila globala s */ extern int s; suma(void) { s+=2; return s; }

Se observa ca variabila globala s 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.

Page 157: [Www.fisierulmeu.ro] Programarea in Limbajul c

Pentru a exemplifica efectul specificatorului static asupra unei variabile globale modificam problema anterioara, cerând 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;

Page 158: [Www.fisierulmeu.ro] Programarea in Limbajul c

} 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 decât 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 decât 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);

Page 159: [Www.fisierulmeu.ro] Programarea in Limbajul c

{ 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.

Page 160: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 specifice î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 sfârsit 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 sfârsitul fisierului

Page 161: [Www.fisierulmeu.ro] Programarea in Limbajul c

?? 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 sfârsitul 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 ”w”si 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 când s-a atins sfârsitul 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.

Page 162: [Www.fisierulmeu.ro] Programarea in Limbajul c

Programul creeaza un fisier de caractere pe disc, având 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); }

Page 163: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 întâlneste 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 sfârsit 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"))

Page 164: [Www.fisierulmeu.ro] Programarea in Limbajul c

{ 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.

Page 165: [Www.fisierulmeu.ro] Programarea in Limbajul c

Programul creeaza fisierul formatat pe disc având numele ”formtext” cuprinzând 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++) {

Page 166: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 decât transferul datelor sub forma binara datorita conversiilor în format ASCII. De asemenea, în general, fisierul creat cu date ASCII formatate foloseste memorie mai multa decât 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 având 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-a ajuns la sfârsitul fisierului sau daca a intervenit o eroare aceasta valoare este strict mai mica decât nb.

Functia fwrite() scrie în fisierul asociat pointerului fp, din zona indicata de pointerul buf, nb blocuri, fiecare bloc având lungimea egala cu nr_octeti. Valoarea întoarsa de functia fwrite() este egala cu numarul de blocuri scrise efectiv; ea va fi mai mica decât nb daca la scriere intervine o eroare.

FUNCTIA feof()

Functia feof() permite determinarea momentului când a fost întâlnit sfârsitul 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 atât la întâlnirea sfârsitului de fisier cât 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 indicându-se în mod eronat sfârsit de fisier sau eroare.

Prototipul functiei feof() este:

int feof(FILE *fp); Functia întoarce valoarea adevarat (valoare diferita de zero), daca s-a întâlnit sfârsitul

fisierului si valoarea fals (egala cu zero) daca nu s-a întâlnit sfârsitul fisierului. Functia feof() se poate folosi atât la fisiere binare cât si la fisiere text.

Page 167: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 168: [Www.fisierulmeu.ro] Programarea in Limbajul c

/* 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 SI MODIFICAREA INDICATORULUI DE POZITIE AL FISIERULUI: fseek(), fgetpos(), fsetpos(), ftell()

Pe lânga 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:

Page 169: [Www.fisierulmeu.ro] Programarea in Limbajul c

nume

semnificatie

SEEK_SET

începutul fisierului

SEEK_CUR

pozitia curenta

SEEK_END

sfârsitul 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;

Page 170: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 171: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 #else #include #line

#ifdef #elif #define #error

#ifndef #endif #undef #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 câte 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, usurând î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 rând, se poate continua pe rândul urmator daca se insereaza un backslash (\) la sfârsitul primului rând.

Page 172: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 B 20 #define C 30 #define D 40 #define ARIA B*C #define VOLUM ARIA*D #define EROARE printf(”\n Eroare”) . . . . . v=VOLUM . . . . . if (x<11)

EROARE; . . . . .

Nu trebuie confundata substitutia în text care se face utilizând directiva #define, cu o atribuire. De exemplu, expresia:

A=(B+5)*D

nu este descrisa corect prin secventa

#define B 20 #define C B+5 #define D 10 . . . . . A=C*D . . . . .

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

Corect este:

#define B 20 #define C (B+5) #define D 10 . . . . . A=C*D

Directiva #define poate fi parametrizata obtinându-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);

Page 173: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 >coborârea> î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 20 #undef M #define M 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 decât fisierul nume_fisier, în cautarea fisierului se tine cont de ghilimele sau paranteze unghiulare astfel: când 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 întâi î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

Page 174: [Www.fisierulmeu.ro] Programarea in Limbajul c

{ 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 antet corespunzatoare, permite folosirea corecta a functiilor aflate în biblioteca C standard.

DIRECTIVELE DE COMPILARE CONDITIONATA: #if, #elif, #ifdef, #ifndef SI DIRECTIVA #error

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

. . . . . . .

Page 175: [Www.fisierulmeu.ro] Programarea in Limbajul c

#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(); }

Page 176: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Atât #ifdef cât 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)

Atât prima cât 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(); }

Page 177: [Www.fisierulmeu.ro] Programarea in Limbajul c

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.

Page 178: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 sfârsitul fisierului si 0 în caz contrar.

Page 179: [Www.fisierulmeu.ro] Programarea in Limbajul c

FUNCTII PENTRU CITIREA SI SCRIEREA CARACTERELOR

int fgetc(FILE *fp);

întoarce valoarea caracterului când operatia de citire din fisierul fp are succes sau EOF când s-a atins sfârsitul 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 sfârsit 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 întorcând ultimul caracter scris sau EOF în caz de eroare.

FUNCTII DE INTRARE/IESIRE 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);

Page 180: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 având 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);

Page 181: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 ramâne 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 întorcând î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().

Page 182: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 183: [Www.fisierulmeu.ro] Programarea in Limbajul c

î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, ignorând 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 ignorând 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 având 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 ramâne nemodificata.

Page 184: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 când 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 când 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 acos(double x); arccos de x double asin(double x); arcsinus de x double atan(double x); arctangenta de x double atan2(double y,double x); arctangenta de y/x double ceil(double x); cel mai mic întreg >= x double cos(double x); cosinus de x double cosh(double x); cosinus hiperbolic de x double exp(double x); exponentiala e la x double fabs(double x); valoarea absoluta a lui x double floor(double x); cel mai mare întreg <= x double fmod(double x,int y); restul împartirii lui x la y vazuti ca întregi (x modulo y) double frexp(double x,double exp);intoarce valoarea mantisei numarului x si

memoreaza valoarea exponentului în exp astfel încât x=mantisa*2exp (mantisa e cuprinsa între 0.5 si 1).

double ldexp(double x,int exp); calculeaza x*2exp double log(double x); calculeaza logaritm natural de x double log10(double x); calculeaza logaritm zecimal de x double 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

double pow(double x,double y); întoarce xy pentru x si y apartinând domeniul de definitie

double sin(double x); sinus de x double sinh(double x); sinus hiperbolic de x double sqrt(double x); radacina patrata a lui x double tan(double x); tangenta de x double tanh(double x); tangenta hiperbolica de x

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.

Page 185: [Www.fisierulmeu.ro] Programarea in Limbajul c

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) întorcând 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 întorcând 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);

Page 186: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 sfârsit de fisier.

div_t div(int numarator, int numitor); /* fisier header stdlib.h */

întoarce o structura de tip div_t care contine câtul 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 indicând 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 câtul 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, const char *nume_vechi); /* fisier header stdio.h */

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

Page 187: [Www.fisierulmeu.ro] Programarea in Limbajul c

î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] întorcând 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().

Page 188: [Www.fisierulmeu.ro] Programarea in Limbajul c

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, tinând cont de importanta lor deosebita, prezentam în aceasta sectiune, pentru familiarizare, câteva functii video furnizate de Borland Turbo C++ pentru calculatoare IBM PC si compatibile, echipate cu adaptoare grafice sub sistemul de operare 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 reîmprospatarea 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 F F F C C C C

unde:

CCCC reprezinta codul culorii caracterului;

FFF reprezinta codul culorii fondului;

B indica afisare continua (valoarea 0) sau intermitenta (valoarea 1).

Ecranul este privit ca o retea celulara, fiecare celula putând memora un caracter. Originea retelei este în coltul din stânga sus si are coordonatele (1,1). Coordonata x creste de la stânga 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 stânga 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 rânduri 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.

Tinând 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.

Atât în modul text cât 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

Page 189: [Www.fisierulmeu.ro] Programarea in Limbajul c

ramâne 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 stânga 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 stânga 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 stânga-sus în punctul de coordonate (x1, y1) si cu coltul dreapta-jos î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 stânga-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).

Page 190: [Www.fisierulmeu.ro] Programarea in Limbajul c

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, întorcând 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);

Page 191: [Www.fisierulmeu.ro] Programarea in Limbajul c

copiaza un text cuprins în zona desemnata de coltul stânga-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 stânga-sus de coordonate (x1,y1) si coltul dreapta-jos de coordonate (x2,y2).

void clreol(void);

sterge caracterele de la pozitia curenta a cursorului pâna la sfârsitul 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 stânga-sus de coordonate (x1,y1) si coltul din dreapta-jos de coordonate (x2,y2), în zona de aceeasi dimensiune care are coltul din stânga-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 câteva 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).

Page 192: [Www.fisierulmeu.ro] Programarea in Limbajul c

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

Page 193: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 pâna la opt pagini, parametrul pag putând 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 stânga-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, având 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.

Page 194: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 stânga textului, iar cea verticala la baza textului. Sunt urmatoarele posibilitati:

orizontal

LEFT_TEXT (0) punct curent la stânga 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.

Page 195: [Www.fisierulmeu.ro] Programarea in Limbajul c

int far getmaxcolor(void);

întoarce indexul ultimului element din paleta reprezentând 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, începând cu unghi-inceput si terminând cu unghi-sfarsit. Cele doua unghiuri pot lua valori între 0 si 360 grade. Când 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 frânta 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 (întâlnite la majoritatea adaptoarelor) între lungimea si latimea unui pixel.

Page 196: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 stânga-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) stânga-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) stânga-sus, (x2,y2) dreapta-jos si având 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 vârfuri au coordonatele în punctele tabloului tab_coord_poligon.

Page 197: [Www.fisierulmeu.ro] Programarea in Limbajul c

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 far 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..