Sunteți pe pagina 1din 130

Limbajul de programare C

6.2. Limbajul de programare C


6.2.1. Vocabularul limbajului. Caractere
La scrierea programelor TURBO C se folosete setul de caractere al codului ASCII.
Caracterele din acest set se codific prin ntregi din intervalul [0,127]. Un astfel de ntreg
poate fi pstrat n binar pe un octet (8 bii). Mulimea caracterelor poate fi mprit n trei
grupe: caractere negrafice, spaiu i caractere grafice.
Spaiul are codul 32. Caracterele negrafice, exceptnd caracterul DEL, care are codul
ASCII 127, au coduri ASCII mai mici dect 32. Caracterele grafice au codurile ASCII mai
mari dect 32. n grupa caracterelor grafice distingem: litere mari, litere mici, cifre, caractere
speciale.
Literele mari i mici sunt cele ale alfabetului englez. Literele mari au codurile ASCII
n intervalul [65,90]. Valoarea 65 este atribuit literei A, iar 90 literei Z. Celelalte valori
corespund celorlalte litere n aa fel nct ordinea alfabetic a lor s induc ordinea cresctoare
a codurilor ASCII.
n mod analog, literele mici au codurile ASCII n intervalul [97,122]. Cifrele 0-9 se
codific prin codurile ASCII 48-57.
Codul ASCII de valoare zero definete caracterul NUL. Acesta este un caracter
impropriu i spre deosebire de celelalte caractere el nu poate fi generat de la tastatur i nu are
efect nici la ieire. El poate fi utilizat pentru a termina un ir arbitrar de caractere deoarece nici
un caracter de la intrare nu poate coincide cu el.
Restul caracterelor negrafice au diferite funcii. Aa, de exemplu, codul ASCII 10
realizeaz deplasarea cursorului n coloana 1 de la linia urmtoare, iar codul 13 deplaseaz
cursorul n coloana ntia de pe aceeai linie. Caracterul cu codul ASCII 10 se numete LF
(Line Feed) iar caracterul cu codul ASCII 13 se numete CR (Carriage Return). Cele dou
caractere se pot introduce de la tastatur acionnd tasta Enter (numit i RETURN)
obinndu-se caracterul de rnd nou (newline). Acest caracter are i o notaie special n
limbajul C i anume el se noteaz prin: \n. Pentru tabulator se utilizeaz notaia: \t.
Caracterele: spaiu, newline, tabulator orizontal le numim caractere sau spaii albe (white
space).
6.2.1.1. Identificatori (nume)
Un identificator (nume), n limbajul C, este o succesiune de litere i eventual cifre,
care ncepe cu o liter. n calitate de litere se folosesc litere mici i litere mari ale alfabetului
englez, precum i caracterul de subliniere (_). Literele mici se consider distincte de cele
134
Limbajul de programare C
mari. Exemple corecte de identificatori: ecuatia_de_grad_doi, x1,x2, a, b, anul_I, _functie, A,
XB. Exemple greite de identificatori: 1_EM, 3_ut.
6.2.1.2. Cuvinte cheie
Un cuvnt cheie este un cuvnt mprumutat din limba englez, care are un neles
predefinit. Cuvinte cheie se scriu cu litere mici n limbajul C.
Un cuvnt cheie nu poate avea alt utilizare ntr-un program C dect cea care i-a fost
predefinit. Fiind o succesiune de litere, un cuvnt cheie este totodat i un nume.
Exemple: if while for break do
Lista cuvintelor cheie este dat n anexa I.
Sensul fiecrui cuvnt cheie va rezulta la definirea construciei n care se utilizeaz.
6.2.1.3. Tipurile de baz din limbajul C
Un program n limbajul C, ca de altfel n orice limbaj de programare, realizeaz o
succesiune de prelucrri asupra unor date. Datele sunt diferite funie de natura (tipul) lor.
n limbajul C distingem cteva tipuri predefinite de date, pe care le numim tipuri de
baz. n afara acestor tipuri utilizatorul i poate defini tipuri noi funcie de specificul datelor
problemei pe care o are de rezolvat.
n tabelul 6.1 de mai jos se indic tipurile de baz din limbajul TURBO C.
Tabelul 6.1.
Cuvnt
cheie
Lungime
n bii
Tip de reprezentare intern
int 16 ntreg binar reprezentat prin complement fa de 2 pe 2 octei
short 16 idem
long 32 ntreg binar reprezentat prin complement fa de 2 pe 4 octei
unsigned 16 ntreg binar fr semn
char 8 caracter reprezentat prin cod ASCII
float 32 numr reprezentat n virgul flotant n simpl precizie
double 64 numr reprezentat n virgul flotant n dubl precizie
long double 80 numr reprezentat n virgul flotant n dubl precizie
Datele de tip int (ntregi cu semn) aparin intervalului [-32768, 32767]. Datele de tip
long (ntregi cu semn n dubl precizie) aparin intervalului [-2
31
, 3
31
]. Datele de tip unsingned
(ntregi fr semn) aparin intervalului [0,65535]. Datele de tip char au valori n intervalul
[0,255] sau [-128,127]. Se pot utiliza ntregi fr semn n dubl lungime folosind succesiunea
de cuvinte cheie: unsigned long. O dat flotant n simpl precizie de tip float, difer de zero,
135
Limbajul de programare C
are valoarea absolut n intervalul [3.4*10
-38
, 3.4*10
38
]. O dat flotant n dubl precizie de tip
double, long double n intervalul [3.4*10
-4932
, 1.1*10
4932
].
6.2.1.4. Constante
O contant are o valoare i un tip. Att tipul ct i valoarea unei constante se definesc
prin caracterele care compun constanta respectiv.
a) Constante ntregi
O constant ntreag poate fi un ir de cifre, care eventual este precedat de un semn. O
constant ntreag se reprezint prin complement fa de 2 pe 16 bii sau chiar 32 de bii dac
nu ncape pe 16 bii. n cazul n care dorim ca o constant ntreag s fie reprezentat pe 32 de
bii, chiar dac ea se poate reprezenta pe 16 bii, vom termina constanta respectiv prin L sau l.
n felul acesta, se impune tipul long pentru constanta respectiv.
O constant ntreag, precedat de un zero nesemnificati, se consider scris n
sistemul de numeraie cu baza 8. De asemenea, o constant ntreag care ncepe cu 0x sau 0X
(zero urmat de litera x mic sau mare) se consider scris n sistemul de numeraie cu baza 16.
n rest se consider c baza de numeraie este 10.
Exemple de reprezentri sunt artate n tabelul 6.2.
Tabelul 6.2.
Repreze
ntare
Lungimea
reprezentrii
Lungime de reprezentare intern
31645 16 bii ntreg zecimal reprezentat n binar prin complement fa de 2
-12345 16 bii ntreg zecimal reprezentat n binar prin complement fa de 2
12345L 32 bii ntreg zecimal reprezentat n binar prin complement fa de 2
012345 16 bii
ntreg octal reprezentat n binar
(o cifr octal se reprezint pe 3 bii)
0xa12b 16 bii
ntreg hexazecimal reprezentat n binar
(o cifr hexazecimal se reprezint pe 4 bii)
923456 32 bii ntreg zecimal reprezentat n binar prin complement fa de 2
b) Constant flotant (real)
O constant flotant este un numr raional care se compune din:
- un semn care poate i lipsi n cazul unui numr nenegativ;
- o parte ntreag care poate fi i vid;
- o parte fracionar care poate fi i vid;
- un exponent care poate fi i vid;
136
Limbajul de programare C
Exemple:
7125.34 -49.0 85. 367e-2 .3789 -.128 4.3E20 -.29e+15
c) Constant caracter
O constant caracter reprezint un caracter i are ca valoare codul ASCII al
caracterului respectiv. O constant caracter grafic se poate scrie incluznd caracterul
respectiv ntre caractere apostrof.
Anumite caractere negrafice au notaii speciale la scrierea crora se utilizeaz
caracterul backslash (\). Aa, de exemplu, am vzut c pentru caracterul tabulator orizontal
folosim notaia: \t.
O astfel de constant caracter se va scrie incluznd notaia respectiv ntre caractere
apostrof. Deci constanta caracter tabulator orizontal se va scrie: \t
n mod analog, constanta caracter de rnd nou (newline) se va scrie: \n
Alte constante caracter care au notaii consacrate sunt prezentate n tabelul 6.3:
Tabelul 6.3
Caracter
Reprezentare prin constant
caracter
Valoare
revenire cu o poziie (backspace) \b 8
retur de car \r 13
salt de pagin la imprimant \f 12
Caracterul backslash poate fi folosit pentru a reprezenta nsui constanta caracter
apostrof sau chiar backslash. Astfel:
\ - reprezint constanta caracter apostrof;
\\ - reprezint constanta backslash.
d) Constant ir sau ir de caractere
O constant ir este o succesiune de zero sau mai multe caractere delimitate prin
ghilimele. Ghilimelele nu fac parte din irul de caractere. Dac dorim s folosim caractere
negrafice n compunerea unui ir de caractere, atunci putem folosi convenia de utilizare a
caracterului backslash indicat mai sus. n particular, dac ntr-un ir de caractere dorim s
reprezentm chiar caracterul ghilimele, atunci vom scrie: \. De asemenea, nsui caracterul
backslash se va scrie: \\.
6.2.1.5. Variabile simple
Prin variabil nelegem o dat a crei valoare se poate schimba n timpul executrii
programului care o conine.
137
Limbajul de programare C
Unei variabile i se ataeaz un nume prin intermediul cruia putem referi sau chiar
modifica valoarea variabilei respective. Valorile pe care le poate avea o variabil trebuiesc s
aparin unui acelai tip. De aceea, unei variabile i corespunde un tip.
Corespondena ntre numele i tipul unei variabile se realizeaz printr-o construcie
special numit declaraie.
Sintaxa unei declaraii n limbajul C este urmtoarea:
nume_tip lista_de_variabile;
Exemple:
int i,k,x; - i,k i x sunt variabile de tip int (ele pot avea ca valori ntregi binari
reprezentai prin complement fa de 2 pe 16 bii);
float a,b; - a i b sunt variabile de tip float (valorile lor sunt reprezentate n
format flotant simpl precizie pe 32 de bii);
char c; - c este o variabil de tip char (valorile ei sunt ntregi din intervalul
[0,255] sau [-128, 127]).
6.2.1.6. Comentarii
n limbajul C un comentariu ncepe prin succesiunea de caractere: /* i se termin
prin: */.
Se pot aduga comentarii pe o singur linie precedate de o succesiune de dou
caractere /.
6.2.2. Expresii
6.2.2.1. Structura expresiilor n limbajul C
O expresie n limbajul C se compune dintr-un operand sau mai muli operanzi legai
prin operatori.
O expresie are o valoare i un tip care se determin aplicnd operatorii prezeni
conform prioritilor i asociativitii acestora.
n limbajul C operatorii se asociaz de la stnga la dreapta, exceptnd operatorii unari,
condiionali i de atribuire, care se asociaz de la dreapta la stnga.
ntr-o expresie pot fi folosite parantezele rotunde pentru a impune o anumit ordine n
executarea operaiilor.
Operatorii limbajului C pot fi grupai n mai multe clase. Cu toate acestea ei pot fi
utilizai mpreun ntr-o aceeai expresie.
6.2.2.2. Operatorii aritmetici
Acetia sunt:
138
Limbajul de programare C
- (minus unar);
+ (plus unar);
* / % operatori binari multiplicativi;
+ - operatori binari aditivi.
Operatorii de pe aceeai linie au aceeai prioritate. Operatorii unari au prioritate mai
mare dect cei binari. Operatorii multiplicativi au o prioritate mai mare dect cei aditivi.
Operatorul * noteaz operaia de nmulire, operatorul / operaia de mprire, iar
operatorului % are ca rezultat restul mpririi dintre doi operanzi ntregi. Operatorii aditivi
noteaz aceleai operaii ca i n matematic.
6.2.2.3. Operatori relaionali
Acetia sunt:
< (mai mic);
<= (mai mic sau egal);
> (mai mare);
>= (mai mare sau egal).
Toi operatorii relaionali au aceeai prioritate. Ea este mai mic dect prioritatea
operatorilor aditivi.
Rezultatul aplicrii unui operator relaional este 1 sau 0, dup cum operanzii se afl n
relaia definit de operatorul respectiv sau nu.
Exemple:
Fie a = 3 i b = -8
Atunci:
a>0 are valoarea 1; a<=0 are valoarea 0;
a+b>0 are valoarea 0; a>=0 are valoarea 1;
-a<0 are valoarea 1; a+b>=b-a are valoarea 1;
a+b<=b-a are valoarea 0.
6.2.2.4. Operatori de egalitate
Exist doi operatori de egaliate:
= = (egal);
! = (diferit).
Operatorii de egalitate au aceeai prioritate. Prioritatea lor este imediat mai mic dect
a operatorilor relaionali.
6.2.2.5. Operatori logici
Operatorii logici sunt:
139
Limbajul de programare C
! (negaia logic operator unar);
&& (I logic);
(SAU logic).
Operatorul ! are aceeai prioritate cu operatorii unari + i -. De altfel toi
operatorii unari au aceeai prioritate n limbajul C.
Operatorul && (I logic) este mai prioritar dect operatorul (SAU logic), dar
are o prioritate mai mic dect operatorii de egalitate.
n limbajul C nu exist valori logice speciale. Valoarea fals se reprezint prin zero.
Orice valoare diferit de zero reprezint valoarea adevrat.
Operatorii logici se evalueaz de la stnga la dreapta. Dac la evaluarea unei expresii
se ajunge ntr-un punct n care se cunoate valoarea ntregii expresii, atunci restul expresiei nu
se mai evalueaz.
Exemplu:
an%4= = 0 && an% 100! = 0 || an% 400 = = 0.
Variabila an, din expresia de mai sus, are valoarea unui ntreg ce reprezint un an
calendaristic. Expresia are valoarea 1 dac anul reprezentat prin valoarea variabilei an este
bisect i zero n caz contrar.
ntr-adevr, un an este bisect dac are loc una din urmtoarele condiii:
a) anul este multiplu de 4 i nu este multiplu de 100; sau
b) este multiplu de 400.
Condiia a) se exprim prin: multiplu de 4: an%4 = = 0
i: &&
nu e multiplu de 100: an%100! = 0
Condiia b) se exprim prin: multiplu de 400: an%400 = = 0
n final cele dou condiii se leag prin operatorul || (SAU) i n felul acesta se obine
expresia cerut.
6.2.2.6. Operatori logici pe bii
Acetia, n ordine descresctoare a prioritii lor sunt:
~ (operator unar; complement fa de unu)
<< >> (deplasri)
& (I pe bii)
^ (SAU-EXCLUSIV pe bii)
| (SAU pe bii)
Operatorul ~ are ca effect inversarea tuturor biilor: biii care iniial au avut valoarea 1
vor deveni 0, iar cei care au avut valoarea 0 vor deveni 1.
Operatorul << este un operator binar pentru deplasare la stnga care se aplic astfel:
140
Limbajul de programare C
a << b va avea ca efect deplasarea la stnga a biilor lui a cu b poziii.
Operatorul >> este un operator binar pentru deplasare la dreapta care se aplic astfel:
a >> b va avea ca efect deplasarea la dreapta a biilor lui a cu b poziii.
Exemple de utilizare a operatorilor pe bii:
Considerm declaraia: int x=6, y=3;

unde reprezentrile interne ale celor dou numere ntregi sunt urmtoarele:
x
y
1. Aplicnd operatorul ~ numrului x se va obine:
~x
care are valoarea n hexazecimal fff9 iar n zecimal are valoarea 7.
2. Aplicnd operatorul & celor dou numere ntregi x, y se va obine expresia:
x & y
care n hexazecimal i n zecimal are valoarea 2.
3. Aplicnd operatorul | celor dou numere ntregi x i y se va obine valoarea:
x | y
care n hexazecimal i n zecimal are valoarea 7.
4. Aplicnd operatorul << celor dou numere ntregi x i y se obine valoarea:
x<<y
care n hexazecimal are valoarea 30 iar n zecimal are valoarea 48.
5. Aplicnd operatorul >> celor dou numere ntregi x i y se obine valoarea:
x>>y
care are valoarea 0.
Aplicarea operatorului de deplasare la stnga, << , este echivalent cu o nmulire a lui
x cu 2
y
, iar a operatorului de deplasare la dreapta, >>, cu o mprire a lui x cu 2
y
.
Observaie: Operatorii logici pe bii sunt diferii de operatorii logici. De exemplu
pentru valorile lui x i y anterioare, expresia x && y are ca valoare 1, spre deosebire de x&y
care are valoarea 2, conform exemplului anterior.
Operatorii pe bii sunt utilizai pentru construirea de diverse mti. De exemplu, ntr-o
palet de 16 culori un octet poate pstra dou culori. Extragerea culorilor se poate face cu
ajutorul operatorilor logici pe bii, ca n exemplul din figura urmtoare:
X=
141
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 1 0 1
C
1
C
2
Limbajul de programare C
C
1
= x >> 4, iar
C
2
= x & 0x0f
6.2.2.7. Operatorul de atribuire
Operatorul de atribuire n forma cea mai simpl se noteaz prin caracterul =. El se
utilizeaz n construcii de forma:
v = expresie.
Operatorul de atribuire are prioritatea cea mai mic fa de operatorii indicai pn n
momentul de fa.
Construcia v = expresie se numete expresie de atribuire. Ea este considerat ca fiind
un caz particular de expresie. Ca orice expresie, ea are o valoare i un tip. Tipul ei coincide cu
tipul lui v, iar valoarea ntregii expresii este chiar valoarea atribuit lui v. Rezult de aici c o
expresie de forma v1 = (v = expresie) este i ea legal.
Operatorii de atribuire se asociaz de la dreapta la stnga. n general, putem realiza
atribuiri multiple printr-o expresie de forma:
v
n
= = v
1
= v = expresie.
n evaluarea unei expresii se aplic regula conversiilor implicite. Prin aplicarea ei
rezult fiecare expresie de tip. n cazul unei expresii de atribuire, dac expresia din dreapta
semnului egal are un tip diferit de cel al variabilei v, atunci nti valoarea ei se convertete spre
tipul variabilei v i apoi se realizeaz atribuirea.
Pentru operaia de atribuire, n afara semnului egal se mai poate folosi i succesiunea:
op = unde prin op se nelege unul dintre operatorii binari aritmetici sau logici pe bii, adic
unul din urmtorii: % / * - + & ^ | << >>.
Aceti operatori se folosesc pentru a prescurta expresiile de atribuire. Astfel expresia:
v op= expresie este identic cu expresia de atribuire: v = v op (expresie).
Exemple:
Fie declaraiile: int i,j,k; double x,y;
x = 3.8 - se atribuie lui x valoarea 3,8;
i = 3.8 - se atribuie lui i valoarea 3;
i = x>0 - se atribuie lui i valoarea 1 dac x>0 i 0 n caz contrar;
x = (i*j+k)/(i*j-k)
x* = 3 - este echivalent cu x = x*3;
i<<=10 - este echivalent cu i = i<<10;
x*=y-10 - este echivalent cu x=x*(y-10)
i& = j>>k - este echivalent cu i=i&( j>>k).
142
Limbajul de programare C
Construciile de mai jos sunt eronate:
x%=y - operatorul % nu se poate aplica la operanzi care nu sunt
ntregi.
i=j - construcia op= implic un operator binar;
x=i+(j*(k-y)+2.3 - lipsete o parantez nchis;
y=3(x-10) - lipsete operatorul * dup 3.
6.2.2.8. Operatori de incrementare i decrementare
Aceti operatori sunt unari i deci au aceeai prioritate cu ceilali operatori unari din
limbajul C. Operatorul de incrementare se noteaz prin ++, iar cel de decrementare prin
--.
Operatorul de incrementare mrete valoarea operandului su cu unu, iar cel de
decrementare micoreaz valoarea operandului cu unu. Operatorii pot fi astfel :
prefixai: ++ operand -- operand
postfixai: operand ++ operand --
n cazul n care sunt folosii prefixai, ei se aplic nti i apoi se folosete valoarea
operandului la care s-a aplicat. Cnd sunt postfixai, se folosete valoarea operandului
nemodificat de operatorul respectiv, apoi se aplic.
Exemple:
Dac x are valoarea 3, atunci n atribuirea y= ++x lui y i se atribuie valoarea 4 deoarece
nti s-a incrementat x i apoi s-a folosit valoarea x. n schimb, dac folosim atribuirea y=x++,
pentru aceeai valoare a lui x, atunci lui y i se atribuie valoarea 3. n ambele cazuri x se
mrete cu 1. Diferena const numai n valoarea atribuit: n primul caz (operator prefixat),
se atribuie valoarea incrementat, iar n al doilea caz (operator postfixat), se atribuie valoarea
neincrementat.
Menionm c aceti operatori se pot aplica numai la urmtorii operanzi: variabil
simpl cu indici, referire la elementul unei structuri (structurile vor fi definite mai trziu).
Exemple:
int i,j; double x,y;
j=i++ - este echivalent cu atribuirile j=i; i=i+1;
y= -- x - este echivalent cu secvena de atribuire: x=x-1; y=x;
x=tab[3]-- - este echivalent cu: x= tab[3]; tab[3]=tab[3]-1;
i=++tab[j+1] - este echivalent cu: tab[j+1]=tab[j+1]+1; i=tab[j+1];
x=tab[++j] - este echivalent cu: j=j+1; x= tab[j];
x = tab[i--] - este echivalent cu: x=tab[i]; i=i-1;
y=++i-j - este echivalent cu: i=i+1;y=i-j;
y=i++-j - este echivalent cu: y=i-j; i=i+1;
Construciile urmtoare sunt eronate:
y=(i-j)++ - operatorul ++ nu se poate aplica expresiei i-j;
143
Limbajul de programare C
x= --(i-j) - operatorul nu se poate aplica expresiei i-j;
i=j- - - - - nu mai reprezint operatorul de decrementare.
6.2.2.9. Operatorul de conversie explicit (operatorul cast)
Adesea dorim s form tipul unui operand sau chiar al unei expresii. Acest lucru este
posibil folosind o construcie de forma: (tip) operand.
Prin aceasta, valoarea operandului se convertete spre tipul indicat n paranteze.
Exemplu:
int x,y; double z;
Fie x = 10 i y = 4, valorile lui x i respectiv y. Atunci n urma atribuirii: z = x/y, z
primete valoarea 2 deoarece 10/4 d ctul 2 (mprirea dintre operanzi ntregi are ca
rezultat ctul ntreg). n schimb, dac vom converti cei doi operanzi spre tipul double,
atunci mprirea nu mai este ntreag i rezultatul va fi 2,5. O astfel de conversie are
forma:
z = (double)x/y
n expresiile de mai sus, construcia (tip) este un operator unar prin care se expliciteaz
conversia dorit. El are aceeai prioritate ca restul operatorilor unari, iar expresia
(tip)operand se numete expresie cast.
6.2.2.10. Operatorul dimensiune (sizeof)
Lungimea n octei a unei date se poate determin folosind o construcie de forma:
sizeof(data) unde data este numele unei variabile simple, al unui tablou, al unei structuri, al
unui tip sau referirea la elementul unui tablou sau structur. Sizeof este operatorul unar
dimensiune i are aceeai prioritate ca ceilali operatori unari. n expresia de mai sus,
parantezele rotunde sunt obligatorii numai n cazul n care data este numele unui tip.
Exemple:
int i; float x; double d; char c; int tab[10]; double dtab[10];
sizeof(i) sau sizeof i - are valoarea 2;
sizeof(x) sau sizeof x - are valoarea 4;
sizeof(float) - are valoarea 4;
sizeof(d) sau sizeof d - are valoarea 8;
sizeof(double) - are valoarea 8;
sizeof(c) sau sizeof c - are valoarea 1;
sizeof(tab[i]) sau sizeof tab[i] - are valoarea 2;
sizeof(tab) sau sizeof tab - are valoarea 20;
sizeof(dtab) sau sizeof dtab - are valoarea 80.
6.2.2.11. Regula conversiilor implicite
144
Limbajul de programare C
O expresie poate conine operanzi de tipuri diferite. Nu exist restricii n acest sens.
Astfel, de exemplu ntr-o expresie se pot folosi operanzi de tip char. Acetia se convertesc n
mod automat spre tipul int, nainte de a face operaii cu ei.
Dac operanzii unui operator binar sunt de acelai tip, se aplic operatorul asupra
operanzilor respectivi, iar tipul rezultatului este acelai cu al operanzilor. n caz contrar
sunt necesare conversii care se execut n mod automat, conform regulii de mai jos
(regula conversiilor implicite):
1.Fiecare operand de tip char se convertete spre tipul int;
2.Dac unul dintre operanzi este de tip long double, atunci cellalt operand se
convertete spre tipul long double i rezultatul va avea tipul long double;
3.Dac unul dintre operanzi este de tip double, atunci cellalt operand se convertete
spre tipul double i rezultatul va avea tipul double;
4.Dac unul dintre operanzi este de tip float, atunci cellalt operand se convertete spre
tipul float i rezultatul va avea tipul float;
5.Dac unul dintre operanzi este de tip long, atunci cellalt operand se convertete spre
tipul long i rezultatul va avea tipul long;
6.Dac unul dintre operanzi este de tip unsigned, atunci cellalt operand se convertete
spre tipul unsigned i rezultatul va avea tipul unsigned;
7.Dac unul dintre operanzi este de tip int, atunci cellalt operand se convertete spre
tipul int i rezultatul va avea tipul int;
Exemple:
int i,j,k; float a,b; double x,y;
unsigned p; long r; char c;
Valorile diferitelor expresii sunt prezentate n tabelul 6.3.
Tabelul 6.3.
Expresii Conversii Tipul expresiei
i+j/k - int
a/b - float
x+y - double
i+a i spre float float
i-3.5 i spre double double
i+5 - int
i+32767 - int
i+x i spre double double
i-c c spre int int
x+100 100 spre double double
p-20 20 spre unsingned unsigned
r*5 5 spre long long
i*31 - int
(long)i*3 i spre long, 3 spre long long
(double)i/j i spre double, j spre double double
145
Limbajul de programare C
(double)(i/j)
se realizeaz mprirea ntreag
ntre i i j i rezultatul se
convertete spre double.
double
6.2.2.12. Operatori condiionali
Operatorii condiionali se utilizeaz n evaluri de expresii care prezint alternative.
O astfel de expresie are formatul: exp1? exp2:exp3 unde exp1, exp2 i exp3 sunt
expresii.
O astfel de expresie se evalueaz n felul urmtor:
1.Se evalueaz expresia exp1;
2.Dac exp1 este diferit de zero, atunci valoarea i tipul expresiei condiionale este
egal cu expresia exp2, altfel cu expresia exp3.
Operatorii condiionali sunt ? i :. Ei trebuie s fie folosii mpreun, adic operatorul
? Trebuie s aib un corespondent : i numai unul.
Ei au prioritatea imediat mai mic dect operatorul logic SAU (||) i imediat mai mare
dect operatorii de atribuire.
Exemple:
1. y?x/y:x*x
n acest exemplu, exp1 este y, exp2 este x/y iar exp3 este x*x.
Expresia se evalueaz astfel: dac y este diferit de zero atunci rezultatul este valoarea
i tipul expresiei x/y, altfel este valoarea i tipul expresiei x*x.
O expresie condiional este un caz particular de expresie i deci este legal i
urmtoarea expresie de atribuire: z=y?x/y:x*x.
Aceast expresie exprim un proces de calcul pe care l putem descrie n pseudocod,
astfel:
dac y 0 atunci z x/y
altfel z x*x
2. Determinarea maximului dintre dou numere a i b.
dac a>b atunci max a
altfel max b
Expresia corespunztoare din limbajul C va fi:
max = (a > b)?a : b
n acest exemplu: exp1 este a > b, exp2 este a, iar exp3 este b.
6.2.2.13. Operatorul virgul
146
Limbajul de programare C
Exist cazuri n care este util s grupm mai multe expresii ntr-una singur, expresii
care s se evalueze succesiv.
n acest scop se folosete operatorul virgul, care separ secvena de expresii, acestea
grupndu-se ntr-o singur expresie.
Operatorul virgul are cea mai mic prioritate dintre toi operatorii limbajului C.
Prioritatea lui este imediat mai mic dect a operatorilor de atribuire.
Cu ajutorul lui construim expresii de forma: exp
1
, exp
2
,,exp
n
.
Aceast expresie, ca oricare alta, are o valoare i un tip. Att valoarea, ct i tipul
ntregii expresii coincide cu valoarea i tipul lui exp
n
deci cu a ultimei expresii.
Exemple:
++i,--j - i se mrete cu o unitate, apoi j se micoreaz cu o
unitate; valoarea i tipul ntregii expresii coincid cu ale lui j;
k=(x=10,y=2*i-5,z=3*j,i+j) - se execut pe rnd cele 3 atribuiri, apoi se efectueaz
suma i+j care se atribuie lui k.
Amintim c toi operatorii se asociaz de la stnga la dreapta, exceptnd cei unari,
condiionali i de atribuire care se asociaz de la dreapta la stnga. n tabelul 6.4 sunt
prezentate prioritile operatorilor.
Tabelul 6.4
( ) [ ] .
-(unar) +(unar) *(unar) &(unar) ! ++ -- (tip) sizeof
* / %
+ -
<< >>
< <= >= >
= = ! =
&
^

&&

? :
= op= op poate fi: (binar) / % +(binar) -(binar) >> << & ^ !
,
147
Limbajul de programare C
6.3. Structura programelor C
6.3.1. Structura unui program n limbajul C
Un program n limbajul C se compune din una sau mai multe funcii. Dintre acestea
una este funcia principal. Funcia principal definete adresa de lansare a programului. Un
program n C se lanseaz cu prima instruciune a funciei principale.
Fiecare funcie are un nume. Funcia principal are numele main.
6.3.2. Structura unei funcii
n limbajul C exist dou feluri de funcii: funcii care returneaz o valoare i funcii
care nu returneaz o valoare la revenirea din ele. Structura unei funcii este urmtoarea:
tip nume (lista parametrilor formali)
{
declaraii de variabile locale
instruciuni
}
unde:
- tip definete tipul valorii returnate de funcie i este un cuvnt cheie pentru tipurile de
baz. Dac funcia nu returneaz nici o valoare, se poate utiliza cuvntul cheie void. Dac tip
este absent, se presupune c funcia sau nu returneaz nici o valoare sau returneaz o valoare
de tip int. Se recomand ca utilizatorul s indice totodat tipul deoarece absena lui constituie
o surs potenial de erori.
- lista_parametrilor_formali este fie vid, fie conine declaraiile parametrilor formali
separate prin virgul.
Menionm c parantezele rotunde sunt prezente chiar i atunci cnd lista
parametrilor formali este vid.
6.4. Preprocesare
Un program n limbajul C poate fi prelucrat nainte de a fi compilat. O astfel de
prelucrare se numete preprocesare.
Prin preprocesare se pot realiza:
- includeri de texte;
- definiii i apeluri de macrouri simple;
- compilare condiionat;
6.4.1. Includerea unui fiier surs
148
Limbajul de programare C
Fiierele surs pot fi incluse cu ajutorul construciei #include. Aceast construcie are
una din urmtoarele formate:
#include specificator_de_fiier sau
#include <specificator_de_fiier>
unde:
- specificator_de_fiier trebuie s fie un nume de fiier valid din punct de vedere al
sistemului de operare DOS, care poate avea o extensie (.h, .c etc.) i opional o
cale. Diferena dintre cele dou formate const n algoritmul de cutare al
fiierului care se include.
Astfel, varianta <...> specific includerea unui fiier standard, care este cutat de
obicei n directorul INCLUDE.
n cazul n care se utilizeaz caracterele ghilimele, utilizatorul furnizeaz fiierul care
se include. Acest fiier va fi cutat n directorul curent sau ntr-un director precizat.
Un fiier standard care trebuie inclus frecvent este fiierul stdio.h. Includerile de
fiiere, de obicei, se fac la nceputul fiierului surs. n felul acesta datele coninute n el se pot
utiliza n tot fiierul surs. De aceea, la nceputul fiierelor surs vom ntlni mai multe
includeri de fiiere, printre care de obicei se va afla i fiierul stdio.h: #include <stdio.h>.
Un exemplu de nchidere a unui fiier utilizator este: #include mouse.h.
6.4.2. Constante simbolice
O alt construcie tratat de procesor este construcia define. Un format simplu al
acestei construcii este urmtorul:
#define nume succesiune_de_caractere.
O constant simbolic este definit din punctul apariiei construciei define
corespunztoare ei i pn la sfritul fiierului surs respectiv sau pn la redefinirea sau
anihilarea ei prin intermediul construciei #undef.
Exemplu: #define A 100
6.5. Intrri/ieiri standard
Operaiile de intrare/ieire se realizeaz prin apeluri de funcii. Datele de
intrare/ieire se presupune c sunt organizate n fiiere.
Unui program BORLAND C i se ataeaz n mod automat urmtoarele fiiere:
- stdin - intrare standard;
- stdout - ieire standard;
- stderr - ieire standard pentru erori;
- stdprn - ieire standard pentru imprimant;
- stdaux - intrare/ieire serial.
149
Limbajul de programare C
6.5.1. Funcia standard printf
Funcia de bibliotec printf realizeaz ieiri cu format. Sintaxa funciei este:
printf(control, par
1
, par
2
,par
n
),
unde control este un ir de caractere care conine texte de scris, specificatori de format pentru
datele care se scriu, iar par
1
, par
2
,par
n
sunt expresii ale cror valori se scriu conform
specificatorilor de format prezentai n parametrul de control.
Un specificator de format ncepe cu un caracter procent (%). n continuare, ntr-un
specificator de format mai putem avea:
1.Un caracter - opional: implicit, datele se aliniaz n dreapta cmpului n care se
scriu. Atunci cnd caracterul - este prezent, data corespunztoare este ncadrat la stnga.
2.Un ir de cifre zecimale opionale, care definete dimensiunea minim a cmpului
afectat datei respective.
3.Un punct opional, urmat de un ir de cifre zecimal. irul de cifre zecimal aflat dup
punct definete precizia datei care se scrie sub controlul specificatorului respectiv.
4.Una sau dou litere, care definesc tipul de conversie aplicat datei care se scrie. n
cazul n care specificatorul de format conine dou litere, prima poate fi l.
n tabelul 6.5 sunt prezentate intrrile/ieirile standard utilizate n limbajul C.
Tabelul 6.5.
Litera Conversia realizat
d
data se convertete din tipul int i zecimal i se scriu la ieire caracterele zecimale
ale ei, eventual precedate de semnul -, dac este negativ.
o data se convertete din tipul int n octal i se scriu la ieire caracterele ei octale;
x
data se convertete din tipul int n hexazecimal i se scriu la ieire caracterele ei
hexazecimale; cifrele peste 9 se scriu cu literele mici (a-f);
X ca i n cazul literei x, dar se vor folosi literele mari (A-F);
u data se convertete din tipul usingned n zecimal ntreg fr semn;
c
valoarea parametrului care i corespunde se consider c reprezint codul ASCII al
unui caracter i se scrie caracterul respectiv;
s
parametrul care-i corespunde se scrie ca un ir de caractere; se consider c irul se
termin la ntlnirea caracterului NUL (\0);
f
valoarea parametrului care-i corespunde se convertete din tip float sau double n
formatul: dd ...d.ddd (d reprezint o cifr zecimal), unde numrul de cifre dup
punctul zecimal este fie cel indicat de precizie specificatorului de format, fie este
egal cu 6; partea ntreag este precedat de semnul minus dac numrul este negativ;
e
conversia se realizeaz de tipul float sau double n formatul:
d.dd..detddd unde numrul cifrelor de dup punctul zecimal este dat de precizia
specificatorului de format sau este egal cu 6, dac acesta este absent; partea ntreag
este precedat de minus dac numrul este negativ;
150
Limbajul de programare C
E
ca i n cazul literei e cu deosebirea c litera e se schimb cu litera E:
d.dd..dEtddd n ambele cazuri, la exponent se va scrie una, dou sau trei cifre, n
funcie de valoarea numrului;
g
se aplic una din conversiile definite de literele f sau e, alegndu-se aceea care se
reprezint pe un numr minim de caractere; de asemenea, zerourile de la sfritul
prii fracionare se omit;
G ca i g cu singura deosebire c se utilizeaz E n loc de e.
Funcia printf returneaz lungimea total n octei a datelor scrise la terminal sau
valoarea simbolic EOF n caz de eroare. EOF este o constant simbolic definit n fiierul
stdio.h. Ea este definit astfel: #define EOF 1.
Exemple:
Fie declaraiile: int i,j; float a; double x; int c; long k;
i = 558 a = 47.389 x = -123.5e20 c = x j = -123 k = 45678
Mai jos apelm funcia printf cu diferii specificatori de format care permit afiarea la
terminal a valorilor acestor variabile. n tabelul 6.6 sunt prezentate rezultatele afiate n cazul
apelrii funciei printf:
Tabelul 6.6.
Nr.crt. Apelul funciei printf Afiri
1. printf (i=%d\n,i); i = 558
2. printf (i=%2d\n,i); i = 558
3. printf (i=%5d\n,i); i = 558
4. printf (i=%o\n,i); i = 1056
5. printf (i=%x\n,i); i = 22e
6. printf (j=%d\n,j); j = -123
7. printf (a=%.3f\n,a); a = 47.389
8. printf (a=%.2f\n,a); a = 47.39
9. printf (x=%e\n,x); x = -1.235000e+22
10. printf (c=%c,cod = %d\n,c,c); c = x, cod = 120
11. printf (k=%ld\n,k); k =45678
6.5.2. Funcia standard scanf
Funcia bibliotec scanf realizeaz intrri cu format de la intrarea standard stdin
(intrare de la terminalul de la care s-a lansat programul). Ea poate fi apelat printr-o
instruciune de apel de forma:
scanf (control, par
1
, par
2
, .., par
n
);
Ca i n cazul funciei printf, parametrul control este delimitat de ghilimele i poate
conine texte i specificatori de format.
Caracterele albe din parametrul de control sunt neglijate.
- parametrii par
1
, par
2
, .., par
n
definesc zonele receptoare ale datelor citite prin
intermediul funciei scanf. Fiecare dintre ei reprezint adresa unei zone receptoare.
151
Limbajul de programare C
Acest lucru se indic, n limbajul C, printr-o construcie de forma: &nume care
determin adresa zonei de memorie rezervat variabilei num. Caracterul &, din
construcia de mai sus, reprezint un operator unar, numit operatorul adres. El
are aceeai prioritate ca i ceilali operatori unari din limbajul C.
Funcia scanf citete toate cmpurile care corespund specificatorilor de format i
eventual textelor scrise n parametrul de control. n cazul unei erori, citirea se ntrerupe n
locul n care s-a ntlnit eroarea. La revenirea din ea funcia scanf returneaz numrul
cmpurilor citite corect.
Exemplul 1:
void main () /* citete un intreg de 4 cifre i scrie cifrele
respective precedate fiecare de cte un spaiu */
{
int cif1, cif2, cif3, cif4;
scanf (%1d%1d%1d%1d, &cif1, &cif2, &cif3, &cif4);
printf(%2d%2d%2d%2d\n, cif1, cif2, cif3, cif4);
}
Exemplul 2:
Programul urmtor citete o dat calendaristic scris sub forma: zzllaaaa
zz - ziua cu 2 cifre;
ll - luna cu 2 cifre;
aaaa - anul cu 4 cifre.
apoi se scrie data respectiv permutnd anul cu ziua, astfel nct dac la intrare se citete, spre
exemplu, 01031992, la ieire se va obine 19920301.
void main () /* citete zzllaaaa i rescrie aaaallzz */
{
int ziua, luna, anul;
scanf (%2d%2d%4d, &ziua, &luna, &anul);
printf(%4d%02d%02d\n, anul, luna, ziua);
}
Exemplul 3:
#define MAX 50
void main () /* citete nume, prenume i data naterii i rescrie datele respective astfel:
linia 1: nume prenume
linia 2: zi luna an */
{ int ziua, luna, an;
char nume [MAX+1], prenume [MAX+1];
scanf (%50s%50s%d%d%d, nume, prenume, &zi, &luna, &an);
152
Limbajul de programare C
printf(%s%s\n, nume, prenume);
printf(%d %d %d\n, zi, luna, an);
}
Exemplu de linie de intrare: Popescu Ion 1 9 1991
6.5.3. Funcia standard putchar
Funcia standard putchar se poate utiliza pentru a scrie un caracter n fiierul standard
de ieire stdout, n poziia curent a cursorului.
Exemple:
1) putchar (A);
- se scrie caracterul A n fiierul de ieire n poziia curent a cursorului;
2) putchar (A+10);
- se scrie caracterul de cod A+10 = 65+10=75, adic litera K;
3) putchar (\n);
- se scrie caracterul de rnd nou (newline). Aceasta are ca efect deplasarea
cursorului n coloana 1 din linia urmtoare.
6.5.4. Funcia standard getchar
Aceast funcie citete de la intrarea standard (fiierul standard stdin) caracterul curent
i returneaz codul ASCII al caracterului citit. Tipul valorii returnate este int. La ntlnirea
sfritului de fiier (^Z) se returneaz valoarea EOF.
Funcia getchar poate fi apelat printr-o instruciune de forma:
getchar()
sau utiliznd-o ca operand ntr-o expresie. Folosind expresia de atribuire:
c =getchar ()
se citete caracterul curent de la intrare.
6.5.5. Funciile standard getch i getche
Funcia getche citete de la intrarea standard caracterul curent i returneaz codul
ASCII al caracterului citit. Spre deosebire de getchar, aceast funcie are acces direct la
caracter, de ndat ce acesta a fost tastat. Se apeleaz la fel ca i getchar, adic fie printr-o
instruciune de apel de forma: getche ();
Funcia getch este similar cu funcia getche cu singura deosebire c citirea se face
fr ecou (caracterul tastat nu se afieaz la terminal i nici cursorul nu se deplaseaz). Ea se
apeleaz n acelai mod ca i funcia getche.
6.5.6. Funciile standard gets i puts
153
Limbajul de programare C
Pentru a introduce de la terminal un ir de caractere se poate folosi funcia gets. Ea
permite citirea cu ecou a caracterelor codului ASCII.
Funcia gets are ca parametru adresa de nceput a zonei n care se pstreaz caracterele
citite. De obicei, aceast zon de memorie poate fi zona alocat unui tablou de tip char.
Funcia gets returneaz adresa de nceput a zonei n care s-au pstrat caracterele sau
zero n cazul n care s-a ntlnit sfritul (^Z).
Exemplu:
char t[255]; gets(t);
La acest apel se citesc caracterele tastate pe un rnd i se pstreaz n tabloul t.
Caracterul tastat nainte de a aciona tasta ENTER este urmat n tabloul t de caracterul NUL,
iar la revenirea din funcie se returneaz adresa zonei receptoare adic valoarea lui t.
Funcia puts realizeaz operaia invers fa de funcia gets. Ea afieaz la terminal
caracterele irului de caractere ASCII aflate ntr-o zon de memorie. Adresa de nceput a
acestei zone de memorie este parametrul funciei puts.
Exemplu:
char t[255]; gets(t); puts(t);
Se d funcia y(x) definit ca mai jos:
( )

'

+
< +

0 x pentru 2 x 5
0 x pentru 10 x 2 x 3
x y
2
S se scrie un program care citete valoarea variabilei x (virgul flotant dubl
precizie) i scrie valoarea lui y.
Programul este prezentat n continuare:
#include <stdio.h>
#include <conio.h>
void main ()
{ float x,y;
printf("\nIntroduceti valoarea lui y:");
scanf("%f",&x);
y=x<0?3*x*x+2*x-10:5*x+2;
printf("\nValoarea functiei este %f",y);
getch();
}
Pentru citirea i scrierea datelor de tip float s-a utilizat specificatorul de format %f.
6.6. Instruciunile limbajului C
Regula general este c toate instruciunile limbajului C se termin prin punct i
virgul, excepie fcnd instruciunile care se termin cu acolada nchis (instruciunea
compus i instruciunea switch) dup care nu se pune punct i virgul.
154
Limbajul de programare C
6.6.1. Instruciunea expresie
Instruciunea expresie se obine scriind punct i virgul dup o expresie.
6.6.2. Instruciunea compus
Instruciunea compus este o succesiune de declaraii urmate de instruciuni,
succesiune inclus ntre acolade.
Declaraiile sau instruciunile (eventual ambele) pot lipsi. Aceast instruciune are
formatul:
{ declaraii
instruciuni
}
Observaii:
1) Dup paranteza nchis a unei instruciuni compuse nu se pune punct i virgul.
2) Corpul unei funcii are aceeai structur ca i instruciunea compus, deci o funcie
are formatul:
antetul funciei
instruciune compus
6.6.3. Instruciunea IF
Instruciunea if permite s realizeze o ramificare a execuiei n funcie de valoarea unei
expresii. Ea are unul din urmtoarele formate:
Format 1:
if(expresie) instruciune 1
Format 2:
if(expresie) instruciune 1
else instruciune 2
Exemple:
1. Programul urmtor citete trei numere ntregi i afieaz maximul dintre ele.
void main () /* citete a,b,c i scrie maximul dintre ele */
{
int a,b,c, max;
scanf (%d%d%d, &a, &b, &c);
if (a > b) max = a;
else max = b;
if (c > max) max = c;
printf (max(%d, %d, %d) = %d\n, a, b, c, max);
}
2. Programul urmtor citete valoarea lui x i scrie valoarea funciei de mai jos:
155
Limbajul de programare C
( )

'

> +

< + +

0 x pentru 1 x 8 x 2
0 x pentru 100
0 x pentru 1 x 2 x 5 x 4
x y
2
2 3
void main () /* citete x, calculeaz y(x) i scrie valoarea lui y */
{
float x,y;
scanf(%f, &x);
if(x < 0) y = 4*x*x*x+5*x*x-2*x+1;
else if (x = = 0) y = 100.0;
else y = 2*x*x+8*x-1;
printf(y(x) = %f\n, y);
}
6.6.4. Instruciunea WHILE
Instruciunea while are urmtorul format:
while (expresie) instruciune
Exemple:
1. Programul urmtor citete un ir de numere ntregi separate prin caractere albe i
scrie suma lor.
void main () /* nsumeaz intregii citii din fiierul de intrare */
{
int s,i;
s = 0;
while (scanf(%d, &i)==1) s = s + i;
printf(suma = %d\n, s);
}
Observaie:
irul de la intrare se poate termina cu orice caracter nenumeric (diferit de caracterele
albe) sau chiar prin sfritul de fiier (^Z). Reamintim c att nainte de sfritul de fiier, ct
i dup el se aciona tasta ENTER (RETURN).
2. Programul citete un numr ntreg n i scrie n!.
void main () /* citete n si scrie n! */
{
int n,i; double f;
f = 1.0; i = 2;
scanf(%d, &n);
while (i<=n) {
156
Limbajul de programare C
f = f*i;
i++;}
printf(n = %d,n! = %g\n, n,f);
}
Observaie:
Instruciunile din corpul ciclului while:
f = f*i;
i++;
pot fi nlocuite cu una singur: f*=i++; Deci ciclul while se poate scrie mai compact astfel:
while(i<=n) f*=i++;
3. Programul urmtor citete un ir de numere ntregi i scrie maximul dintre ele. Se
presupune c irul conine cel puin un numr.
void main () /* citete un ir de numere ntregi i scrie maximul dintre ele */
{
int max,i;
scanf(%d, &max);
while (scanf(%d, &i) = =1) if(i > max) max = i;
printf(max = %d\n, max);
}
6.6.5. Instruciunea FOR
Instruciunea for, ca i instruciunea while, realizeaz o structur repetitiv
condiionat anterior. Este folosit pentru repetarea operaiilor n care se cunoate numrul de
pai i dup o condiie. Formatul instruciunii este:
for(exp
1
; exp
2
; exp
3
) instruciune
unde exp
1
, exp
2
, exp
3
sunt expresii.
Antetul ciclului este definit de: for(exp
1
; exp
2
; exp
3
)
Instruciunea care se execut repetat formeaz corpul ciclului, exp
1
constituie partea de
iniializare a ciclului, exp
2
este partea de reiniializare a lui, iar exp
3
reprezint condiia de
continuare a ciclului. De obicei, exp
1
i exp
3
reprezint atribuiri.
Instruciunea for se execut conform urmtorilor pai:
1.Se execut secvena de iniializare definit de expresia exp
1
.
2.Se evalueaz exp
2
. Dac exp
2
are valoarea zero, atunci se iese din ciclu. Altfel se
execut instruciunea din corpul ciclului.
3.Se execut secvena de reiniializare definit de exp
3
, apoi se reia de la pasul 2.
Echivalena dintre instruciunile while i for este sintetizat astfel:
exp
1
;
while (exp
2
)
157
Limbajul de programare C
{
instruciune
exp
3
;
}
Un exemplu foarte simplu de ciclu cu pas este nsumarea elementelor unui tablou.
s = 0;
for (i=0; i<n; i++) s = s + tab[i];
sau mai compact:
for (s=0, i=0; i<n; i++) s+=tab[i];
n secvena compact s-a introdus expresia s=0 n pasul de iniializare a ciclului
folosind operatorul virgul.
Aceeai secven se poate scrie folosind instruciunea while:
s=0; i=0;
while (i<n)
{
s+=tab[i];i++;
}
sau mai compact:
s = 0; i = 0;
while (i<n) s += tab [i++];
Exemple:
1. Se consider problema simpl de afiare a numrului de caractere citite de la intrarea
stdin. Vom prezenta dou variante de rezolvare, una folosind instruciunea while, iar cealalt
folosind instruciunea for.
Varianta cu instruciunea while: Varianta cu instruciunea for:
#include <stdio.h>
main()
/* numara caracterele citite */
{
long n;
n = 0;
while (getchar() ! =EOF) n++
printf(numarul caracterelor citite= %ld\n, n);
}
#include <stdio.h>
main()
/* numara caracterele citite */
{
long n;
for (n = 0;getchar() ! =EOF) n++);
printf(numarul caracterelor citite= %ld\n, n);
}
2. Programul urmtor citete componentele vectorului v scriindu-le cte 10 pe rnd,
separate printr-un spaiu. De asemenea, dup ultimul element se scrie caracterul newline.
158
Limbajul de programare C
Deoarece elementele se scriu cte 10 pe rnd nseamn c se va scrie caracterul de
rnd nou (newline ) dup al 10-lea element, adic dup v[9], v[19], v[29],, precum i dup
ultimul element care este v[n-1]. n rest, se scrie cte un spaiu dup fiecare element.
#define max 100
void main () /*citete componentele unui vector i rescrie cte 10 pe un rnd */
{
int n,i; float v[max];
if (scanf (%d, &n) !=1 || n <=0 || n>max)
printf(numar componente eronate \ n);
else
{ /*se citesc componentele lui v */
for (i=0; i<n; i++); scanf(%f, &v[i]);
for (i = 0; i < n; i++) /*se listeaz componentele lui v*/
printf (%f%c, v[i], i%10 = = 9 || i = = n-1? \n: );
} /* sfrit else */
}
3. Programul urmtor afieaz numerele lui Fibonacci mai mici sau egale cu n. Dac f(0)
= 0 i f(1) = 1 sunt primele dou numere ale lui Fibonacci, atunci urmtoarele numere ale
lui Fibonacci se obin folosind relaia de recuren:
f(i) = f(i-2) + f(i-1)
#define MAXFIB 32767
void main () /* genereaz numerele lui Fibonacci mai mici sau egale cu n*/
{
long n; unsingned f0, f1, fi;
if (scanf (%1d, &n) ! = 1 n<0 n > MAXFIB)
printf (n este eronat sau se depaseste limita MAXFIB \n);
else
{
printf (%d\n, 0);
for (f0=0, f1=1; f1<=n; f0 =f1, f1 =fi)
{
fi = f0 + f1;
printf (%u\n, f1);
} /* sfrit for */
} /* sfrit else */
}
6.6.6. Instruciunea DO-WHILE
159
Limbajul de programare C
Aceast instruciune realizeaz structura repetitiv condiionat posterior. Aceast
structur nu este obligatorie pentru a scrie programe, ea putndu-se realiza prin celelalte
structuri. Are urmtorul format:
do
instruciune
while (expresie);
Aceasta, la rndul ei, poate fi scris printr-o secven n care se folosete instruciunea
while:
instruciune
while (expresie) instruciune
Exemple:
1. Programul urmtor utilizeaz o metod iterativ simpl pentru extragerea rdcinii
ptrate dintr-un numr. Fie irul:
x
0
, x
1
, ... x
n
,
unde x
n
=1/2(x
n-1
+a/x
n-1
)
Se poate demonstra c acest ir converge, pentru a>0, ctre rdcina ptrat din a.
Convergena este rapid pentru 0<a<=1. n acest caz se poate lua x
0
= 1. Pentru a obine
rdcina ptrat cu o precizie dat, este suficient ca diferena absolut dintre doi termeni
consecutivi ai irului s fie mai mic dect eroarea admis EPS. Programul de fa consider
EPS = 10
-10
.
Pentru realizarea procesului iterativ descris de mai sus, sunt suficiente dou variabile
x1 i x2 care pstreaz doi termeni consecutivi. Programul n limbajul C este:
#define EPS 1e-10
void main () /* calculeaza rdcina ptrat dintr-un numr subunitar */
{
double x1, x2, y, a;
if (scanf (%1f, &a) !=1 || a<0 || a > 1.0)
printf (numarul citit nu este subunitar si pozitiv\n);
else {
x2 = 1.0;
do {
x1 = x2; x2 = 0.5*(x1+a/x1);
if ((y = x2-x1)<0) y=-y;
}
while (y >= EPS);
printf (radacina patrata din: %g este: %.11f\n, a, x2);
} /* sfrit else */
}
160
Limbajul de programare C
6.6.7. Instruciunea SWITCH
Instruciunea switch permite realizarea structurii selective multiple, fiind folosit
pentru ramificarea programului pe mai multe ramuri.
switch (expresie)
{
case c1: ir
1
break;
case c2: ir
2
break;

case c
n
: ir
n
break;
default: ir
}
unde: c
1
, c
2
, , c
n
sunt constante sau constante simbolice;
ir
1
, ir
2
, , ir
n
sunt iruri de instruciuni;
Exemple:
1. Programul urmtor citete o dat calendaristic de forma: zzllaaaa i o scrie sub
forma: aaaa, luna zi unde:
zz este ziua pe 2 cifre; aaaa este anul pe 4 cifre;
ll este luna pe 2 cifre; zi este ziua pe 1-2cifre;
luna este denumirea lunii calendaristice;
void main () /* citete zzllaaaa i scrie aaaa, luna zz */
{
int zz,ll,an;
scanf (%2d%2d%4d, &zz, &ll, &an);
printf(%d ,an);
switch (ll) {
case 1: printf (Ianuarie); break;
case 2: printf (Februarie); break;
case 3: printf (Martie); break;
case 4: printf (Aprilie); break;
case 5: printf (Mai); break;
case 6: printf (Iunie); break;
case 7: printf (Iulie); break;
case 8: printf (August); break;
case 9: printf (Septembrie); break;
case 10: printf (Octombrie); break;
case 11: printf (Decembrie); break;
case 12: printf (Noiembrie); break;
161
Limbajul de programare C
default: printf (luna eronata);
}
printf (\t%d\n, zz);
}
6.6.8. Instruciunea BREAK
Formatul instruciunii este urmtorul:
break;
Aa cum s-a vzut n paragraful 6.6.7, se poate folosi instruciunea break pentru a iei
dintr-o instruciune switch.
Ea mai poate fi utilizat pentru a iei dintr-o instruciune ciclic.
6.6.9. Instruciunea CONTINUE
Instruciunea are urmtorul format: continue;
Efect:
1.n ciclurile while i do-while realizeaz saltul la evaluarea expresiei care decide
asupra continuitii ciclului;
2.n ciclul for realizeaz saltul la pasul de reiniializare.
Un exemplu de program n care se poate utiliza instruciunea continue este urmtorul:
Se citesc de la tastatur temperaturile nregistrate pe durata a n zile. Se cere
programul C care afieaz pe ecran media temperaturilor pozitive. Programul va citi toate
temperaturile indiferent dac sunt pozitive sau negative i calculeaz media celor pozitive. n
cazul n care se ntlnete o temperatur negativ se va ignora.
#include <stdio.h>
#include<conio.h>
void main()
{
int n,i;
float suma_temp,temperatura,temp_medie,nr_temp_pozitive;
puts("\nIntroduceti numarul de zile:");
scanf("%d",&n);
nr_temp_pozitive=suma_temp=0;
for(i=1;i<=n;i++)
{ printf("\nTemperatura pentru ziua %d ",i);
scanf("%f",&temperatura);
if (temperatura<0) continue;
nr_temp_pozitive=nr_temp_pozitive+1;
suma_temp=suma_temp+temperatura;
162
Limbajul de programare C
}
temp_medie=suma_temp/nr_temp_pozitive;
printf("\nTemperatura medie pozitiva este %.2f",temp_medie);
getch();
}
6.6.10. Instruciunea GOTO
Instruciunea goto are urmtorul format:
goto nume_de_etichet;
Ea realizeaz saltul la instruciunea prefixat de eticheta al crui nume se afl scris
dup cuvntul cheie goto.
Eticheta trebuie s fie urmat de caracterul :.
Un exemplu de program n care se folosete instruciunea goto este urmtorul:
Se citesc de la tastatur temperaturile nregistrate pe durata a n zile. Se cere
programul C care afieaz numrul zilei n care s-a ntlnit prima temperatur negativ sau un
mesaj n cazul n care nu s-a ntlnit o temperatur negativ.
#include <stdio.h>
#include<conio.h>
void main()
{ int n,i;
float temperatura;
puts("\nIntroduceti numarul de zile:");
scanf("%d",&n);
for(i=1;i<=n;i++)
{ printf("\nTemperatura pentru ziua %d ",i);
scanf("%f",&temperatura);
if (temperatura<0)
{ printf("\nPrima temperatura negativa s-a inregistrat in ziua
%d",i);
goto end; }
}
printf("\nNu s-a inregistrat nici o temperatura negativa ");
end: getch();
}
Observaie: Se recomand evitarea folosirii instruciunii goto ntruct contravine
regulilor programrii structurate; se pot utiliza, n schimb, variabile logice care simuleaz
funcionarea instruciunii goto.
6.7. Declaraia de tablou
163
Limbajul de programare C
Un tablou este reprezentat printr-o mulime finit i ordonat de elemente de acelai tip
care are asociat un nume.
Tablourile se folosesc n cazul aplicaiilor care lucreaz cu mai multe date i pentru
care declararea unor variabile simple nu mai este suficient. Un astfel de exemplu ar fi un
program care citete temperaturile nregistrate ntr-o perioad oarecare de timp i apoi le
prelucreaz (determin temperatura minim, maxim, le ordoneaz cresctor sau descresctor,
etc.).
n acest caz declararea unei variabile simple cum este variabila temperatura din
exemplele anterioare nu mai este suficient, deoarece ea poate memora la un moment dat doar
temperatura curent, nu i pe celelalte temperaturi.
n astfel de situaii se utilizeaz date structurate. Datele structurate sunt date care
conin mai multe date de acelai tip sau de tipuri diferite. Tablourile sunt structuri de date care
conin date de acelai tip. Referirea la o dat din cadrul tabloului se face utiliznd un indice.
Indicele reprezint poziia datei n tablou.
Un tablou, ca orice variabil simpl, trebuie declarat nainte de a fi utilizat. Declaraia
de tablou, n forma cea mai simpl, conine tipul comun, al elementelor sale, numele tabloului
i limitele superioare pentru fiecare indice, incluse ntre paranteze drepte:
tip lista_de_elemente;
unde lista_de_elemente se compune dintr-un element sau mai multe, separate prin virgul. Un
element dintr-o astfel de list are formatul: nume[lim
1
][lim
2
][lim
n
] n care lim
1
, lim
2
, ,
lim
n
sunt expresii constante care au valori ntregi. Prin expresie constant nelegem o
expresie care poate fi evaluat la ntlnirea ei de ctre compilator.
Exemple:
1.Fie v un vector cu 10 componente de tip ntreg. El se declar astfel: int v[10];
2.Fie a o matrice cu 100 de linii i 4 coloane. Elementele ei sunt reprezentate n virgul
flotant simpl precizie. Ea se declar astfel: float a[100] [4];
La elementele unui tablou ne referim prin variabile cu indici. O astfel de variabil se
compune din numele tabloului urmat de unul sau mai muli indici, fiecare indice inclus n
paranteze drepte. Numrul indicilor definete dimensiunea tabloului. Indicii sunt expresii care
au valori ntregi. Limita inferioar a indicilor este zero.
Astfel, dac v este tabloul declarat n exemplul 1, atunci elementele lui sunt: v[0], v[1],
v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9].
n cazul matricei a declarate n exemplu 2, elementele ei vor fi
pe prima linie: a[0] [0], a[0] [1], a[0] [2], a[0] [3]
pe a doua linie: a[1] [0], a[1] [1], a[1] [2], a[1] [3]
. . .
pe ultima linie: a[99] [0], a[99] [1], a[99] [2], a[99] [3]
164
Limbajul de programare C
Numele unui tablou poate fi utilizat n diferite construcii, el avnd ca valoare adresa
primului su element.
n figura 6.1 se indic repartizarea memoriei pentru tabloul tab, declarat mai sus:
Exemple de programe care utilizeaz tipul de date tablou.
1. Se presupune c de-a lungul unei perioade de timp de n zile se nregistreaz
temperatura mediului ambiant. Se cere un program C care citete de la tastatur aceste
temperaturi i determin:
a) Temperatura minim i maxim precum i zilele n care acestea s-au nregistrat;
b) Numrul de temperaturi negative;
c) Temperatura medie de-a lungul celor n zile.
#include <stdio.h>
#include<conio.h>
void main()
{ int temperatura[100];
int n,t_max,t_min,nr_t_neg,i;
printf("\nIntroduceti numarul de zile:");
scanf("%d",&n);
for (i=0;i<n;i++)
{ printf("\nTemperatura din ziua %d : ",i+1);
scanf("%d",&temperatura[i]); }
t_max=t_min=temperatura[0];
for (i=0;i<n;i++)
{ if (temperatura[i]<t_min) t_min=temperatura[i];
if (temperatura[i]>t_max) t_max=temperatura[i]; }
printf("\nTemperatura maxima este %d si s-a inregistrat in zilele ",t_max);
for (i=0;i<n;i++)
if (temperatura[i]==t_max)
printf("%d ",i+1);
printf("\nTemperatura minima este %d si s-a inregistrat in zilele ",t_min);
for (i=0;i<n;i++)
if (temperatura[i]==t_min)
printf("%d ",i+1);
nr_t_neg=0;
165
Adresa lui
tab[0] tab[0] tab[1] tab[2] tab [3]
Fig. 6.1. Repartizarea memoriei pentru tabloul tab.
Limbajul de programare C
for(i=0;i<n;i++)
if (temperatura[i]<0)nr_t_neg++;
printf("\nS-au inregistrat %d temperaturi negative",nr_t_neg);
getch();
int s=0;
for (i=0;i<n;i++)
s+=temperatura[i];
printf("\nTemperatura medie este %.2f",(float)s/n); }
2. Se consider o matrice cu n linii i m coloane care are elemente de tip real. Se
cere programul C care determin toate elementele din matrice, mpreun cu poziiile lor, care
au proprietate de a fi maxime pe linia n care se afl i minime pe coloana n care se afl.
Programul va conine i secvena de afiare a matricei date.
Spre exemplu, pentru matricea urmtoare:
1 11 1 10 6
4 7 3 1 2
1 3 0 8
11 9 1 14 2

6
un astfel de element este 6 aflat la intersecia liniei 2 cu coloana 4.
Problema poate fi rezolvat n felul urmtor: Se determin pentru fiecare linie
elementul maxim i pentru fiecare coloan elementul minim. Aceste valori se pstreaz n cte
un vector cu numele max, respectiv min. Dup determinarea elementelor acestor doi
vectori, ele se vor compara ntre ele pentru determinarea elementului cutat. Pentru matricea
din exemplul anterior cei doi vectori vor fi:
( ) 1 6 1 1 8
11
7
6
14
1 11 1 10 6
4 7 3 1 2
1 3 0 8
11 9 1 14 2

,
_

,
_

6
Programul este prezentat n continuare:
#include <stdio.h>
#include<conio.h>
void main()
{float a[10][10],min[10],max[10];
int i,j,m,n;
printf("\nIntroduceti nr. de linii:");
scanf("%d",&n);
166
Limbajul de programare C
printf("\nIntroduceti nr. de coloane:");
scanf("%d",&m);
printf("\nIntroduceti elementele matricei\n");
for (i=0;i<n;i++)
for(j=0;j<m;j++)
{ printf("\na[%d][%d]=",i,j);
scanf("%f",&a[i][j]); }
printf("\nMatricea data este:\n");
for (i=0;i<n;i++) //secvena de afiare elemente
matrice
{ for (j=0;j<m;j++)
printf("%.2f ",a[i][j]);
printf("\n"); }
for(i=0;i<n;i++) //secvena de determinare a maximelor pe
linii
{ max[i]=a[i][0];
for (j=0;j<m;j++)
if (max[i]<a[i][j]) max[i]=a[i][j]; }
for(j=0;j<m;j++) //secvena de determinare a maximelor pe
linii
{ min[j]=a[0][j];
for (i=0;i<n;i++)
if (min[j]>a[i][j]) min[j]=a[i][j]; }
int gasit=0; //Secvena de comparare element cu element a celor doi vectori
for (i=0;i<n;i++)
for (j=0;j<m;j++)
if (max[i]==min[j])
{ gasit=1;
printf ("\nElementul %.2f de pe linia %d, coloana %d",a[i][j],i,j); }
if (gasit==0) printf("\nNu exista un astfel de element!")
getch();
}
3. Se cere un program care citete de la tastatur elementele ntregi ale unui vector de
dimensiune n i afieaz aceste elemente n ordine cresctoare.
Problema are o importan destul de mare n programare, deoarece problema sortrii
cresctoare sau descresctoare a unui ir de valori este destul de frecvent ntlnit n practic.
S-au descoperit un numr mare de algoritmi de sortare, dintre care se va utiliza un algoritm
167
Limbajul de programare C
relativ simplu, algoritm cunoscut sub denumirea de bubblesort sau sortare prin metoda
bulelor.
Algoritmul const din parcurgerea vectorului de mai multe ori, n fiecare parcurgere
comparndu-se elementele aflate pe poziii alturate. Ori de cte ori cele dou elemente
comparate nu se afl n ordinea dorit, ele se vor interschimba. Pentru interschimbare se va
folosi o variabil auxiliar aux. Algoritmul se consider ncheiat n urma unei parcurgeri a
vectorului n care nu se mai efectueaz nici o interchimbare. Pentru a se marca efectuarea unei
interschimbri se va utiliza o variabil de tip ntreg, gata, cu rol de adevrat/fals.
Aceast variabil va primi valoarea 1 nainte de fiecare parcurgere a vectorului i valoarea
0 n momentul n care se efectueaz o interschimbare. Algoritmul se va considera ncheiat
cnd variabila gata va rmne 1 la sfritul parcurgerii vectorului.
Algoritmul este explicat pentru vectorul:
A = [9, 2, 6, 1, 3 ].
a) Prima parcurgere:
gata = 1
i=0 i=1 i=2 i=3
9 2 2 2 2 gata=0
2 9 6 6 6 gata=0
6 6 9 1 1 gata=0
1 1 1 9 3 gata=0
3 3 3 3 9
b) A doua parcurgere:
gata = 1
i=0 i=1 i=2 i=3
2 2 2 2 2
6 6 1 1 1 gata=0
1 1 6 3 3 gata=0
3 3 3 6 6
9 9 9 9 9
168
Limbajul de programare C
c) A doua parcurgere:
gata = 1
i=0 i=1 i=2 i=3
2 1 1 1 1 gata=0
1 2 2 2 2
3 3 3 3 3
6 6 6 6 6
9 9 9 9 9
n cea de-a treia parcurgere, care n acest caz este i ultima, nu se mai efectueaz nici o
interschimbare, prin urmare vectorul este ordonat i algoritmul se ncheie.
Denumirea algoritmului Sortare prin metoda bulelor sau Bubblesort provine
din analogia care se poate face ntre elementele vectorului i nite bule cu densiti
proporionale cu valoarea elementului respectiv. Se observ c elementele de valori mari (deci
bulele cu densiti mai mari) coboar, cum este i cazul elementului 9 care la nceput se afla
pe poziia 0 n vector i n urma primei parcurgeri a vectorului a cobort pe ultima poziie.
Programul C care implementeaz acest algoritm este prezentat n continuare.
#include <stdio.h>
#include<conio.h>
void main()
{ int a[100],n,i,gata,aux;
printf("\nNumar elemente vector:");
scanf("%d",&n);
for (i=0;i<n;i++)
{ printf("\na[%d]=",i);
scanf("%d",&a[i]); }
do
{ gata=1;
for (i=0;i<n-1;i++) //parcurgere vector
if (a[i]>a[i+1])
{ aux=a[i]; //efectuare interschimbare
a[i]=a[i+1];
a[i+1]=aux;
gata=0; }
}
while(gata==0); //test daca s-a terminat sortarea
printf("\nVectorul sortat este:\n");
for (i=0;i<n;i++)
printf("%d ",a[i]);
169
Limbajul de programare C
getch();
}
6.8. Apelul i revenirea dintr-o funcie
6.8.1 Apelul unei funcii
Am amintit n capitolul 1 c funciile sunt de dou feluri:
- funcii care returneaz o valoare la revenirea din ea;
- funcii care nu returneaz o valoare la revenirea din ea.
O funcie care nu returneaz o valoare la revenirea din ea se apeleaz printr-o
instruciune de apel. Ea are urmtorul format:
nume(lista_parametrilor_efectivi);
n cazul n care o funcie returneaz o valoare, ea poate fi apelat fie printr-o
instruciune de apel, fie sub forma unui operand al unei expresii. Apelul se face printr-o
instruciune de apel atunci cnd nu dorim s utilizm valoarea returnat de funcia respectiv.
Definiia unei funcii are structura urmtoare:
Antet
instruciune compus
Antetul are formatul:
tip nume(lista_parametrilor_formali)
Funciile care nu returneaz o valoare la revenirea din ele au ca tip cuvntul cheie void.
De asemenea, dac lista_parametrilor_formali este vid, ea poate fi definit prin cuvntul
cheie void. Lista parametrilor formali conine declaraiile acestora separate prin virgul.
Parametrii formali ai unei funcii sunt reprezentai de datele prin care funcia respectiv
comunic cu alte funcii din cadrul programului respectiv. Ea se pune n coresponden cu lista
parametrilor efectivi din instruciunea de apel. Cele dou liste de parametrii trebuie s coincid
ca numr, ordine i tip.

6.8.2 Prototipul unei funcii
O funcie poate fi apelat dac ea este definit n fiierul surs nainte de a fi apelat.
Acest lucru nu este totdeauna posibil i n astfel de cazuri apelul funciei trebuie s fie
precedat de prototipul ei. Prototipul poate fi scris la nceputul fiierului surs i n acest caz el
va fi valabil n tot fiierul surs.
Prototipul unei funcii este de fapt antetul funciei i conine toate informaiile
referitoare la funcia respectiv: numele funciei, tipul returnat de funcie, numrul i tipul
parametrilor funciei.
170
Limbajul de programare C
Exemple:
1) double val(double x, int a); Acest prototip indic faptul c funcia val returneaz o
valoare flotant n dubl precizie i are doi parametri, primul de tip double i al doilea de tip
int.
Exemple de instruciuni de apel:
y= val(z,b); s=val(12.6, 5);
n aceste exemple s,z i y trebuie s fie variabile de tipul double iar b o variabil de
tipul int.
Se observ c o funcie poate fi apelat fie cu parametrii constani, fie cu parametrii
variabili.
2) void citire(void);
Conform acestui prototip funcia citire nu returneaz nici o valoare i nu are
parametrii. Funcia poate fi apelat printr-o instruciune de apel de forma:
citire( );
3) double sum (double x[], int n);
Funcia sum returneaz o valoare flotant n dubl precizie. Ea are doi parametrii: un
tablou unidimensional de tip double i o variabil de tip int. Aceast funcie se poate apela
printr-o instruciune de apel de forma:
s=sum(a,m);
unde a trebuie s fie un tablou cu elemente de tip double, m o variabil de tipul int, iar s o
variabil de tipul double.
6.8.3. Apel prin valoare i apel prin referin
La apelul unei funcii, fiecrui parametru formal i se atribuie valoarea parametrului
efectiv care-i corespunde. Deci, la apelul unei funcii, se transfer valorile parametrilor
efectivi. Din aceast cauz, se spune c apelul este prin valoare.
n anumite limbaje de programare, la apel se transfer nu valorile parametrilor efectivi
ci adresele acestor valori. n acest caz se spune c apelul este prin referin.
ntre cele dou tipuri de apeluri exist o diferen esenial i anume: n cazul apelului
prin valoare, funcia apelat nu poate modifica parametrii efectivi din funcia care a fcut
apelul, neavnd acces la ei. De fapt funcia primete o copie a valorilor de apel.
n cazul apelului prin referin, funcia apelat dispunnd de adresa parametrilor
efectivi, i poate modifica pe acetia.
n limbajul C, apelul prin referin se realizeaz n cazul n care parametrul efectiv este
numele unui tablou. n acest caz, se transfer valoarea numelui tabloului, adic adresa
primului su element. n felul acesta, funcia apelat dispune de adresa de nceput a tabloului
utilizat ca parametru i n consecin ea poate modifica elementele tabloului respectiv.
171
Limbajul de programare C
n concluzie, n limbajul C, transferul parametrilor se face prin valoare. Acest
transfer devine prin referin n cazul n care parametrul efectiv este numele unui tablou sau se
cere explicit acest lucru adugnd caracterul & n faa parametrului.
6.8.4. Revenirea dintr-o funcie
Revenirea dintr-o funcie se poate face n dou moduri:
-la ntlnirea instruciunii return;
-dup execuia ultimei sale instruciuni, adic a instruciunii care precede acolada
nchis ce termin corpul funciei respective.
Instruciunea return are dou formate: return;
sau return expresie;
Primul format se utilizeaz cnd funcia nu returneaz o valoare, iar cel de al doilea
format se utilizeaz atunci cnd funcia returneaz o valoare.
Un exemplu de program care utilizeaz funcii definite de utilizatori este urmtorul:
Se citesc de la tastatur cele n elemente de tip ntreg ale unui vector. Se cere
programul C care determin:
a) Suma cifrelor fiecrui element din vector;
b) Inversul fircrui element din vector;
c) Cel mai mare divizor comun al elementelor din vector.
Pentru ca programul s realizeze aceste cerine vom defini trei funcii utilizator:
a) O funcie suma_cifre care va calcula suma cifrelor unui numr ntreg transmis
ca parametru;
b) O funcie invers care va returna inversul unui numr ntreg transmis ca
parametru;
c) O funcie cmmdc care va determina cel mai mare divizor comun a dou numere
ntregi transmise ca parametri.
Programul este prezentat n continuare:
#include<stdio.h>
#include<conio.h>
int suma_cifre(int n)
{ int s;
s = 0;
while (n!=0)
{ s = s + n % 10;
n = n / 10; }
return s; }
int invers(int n)
{ int inv;
172
Limbajul de programare C
inv = 0;
while (n!=0)
{ inv = inv * 10 + n % 10;
n = n / 10; }
return inv; }
int cmmdc(int a, int b)
{ while (a!=b)
if (a>b) a = a - b;
else b = b - a;
return a; }
void main()
{ int a[100],n,i,c;
float p;
printf("\nNumar elemente vector:");
scanf("%d",&n);
printf("\nElementele vectorului:\n");
for (i=0;i<n;i++)
{ printf("a[%d]=",i);
scanf("%d",&a[i]); }

printf("\nSuma cifrelor elementelor vectorului:\n");
for (i=0;i<n;i++)
printf("%d ",suma_cifre(a[i]));
printf("\nNumerele inverse elementelor vectorului:\n");
for (i=0;i<n;i++)
printf("%d ",invers(a[i]));
c = a[0];
for (i=1;i<n;i++)
c=cmmdc(c,a[i]);
printf("\nCel mai mare divizor comun este %d",c);
getch(); }
6.9. Clase de memorie
Compilatorul C aloc memorie variabilelor din program de dimensiune
corespunztoare tipului fiecreia. Memoria poate fi alocat static sau dinamic. Memoria
alocat dinamic se repartizeaz pe stiv, spre deosebire de cea static, repartizat ntr-o zon
special afectat programului. n funcie de modul n care se aloc memoria, vom distinge mai
multe clase de memorie.
173
Limbajul de programare C
O prim clas de memorie este aceea alocat variabilelor globale. Lor li se aloc
memorie pe toat perioada execuiei programului i ele pot fi utilizate n tot programul.
O alt clas de memorie este alocat variabilelor locale. O astfel de variabil nu este
valabil n tot programul. Ea are o utilizare local.
6.9.1. Variabile globale
O variabil global are o definiie i attea declaraii de variabil extern cte sunt
necesare.
n mod implicit, definiia unei variabile globale determin ca variabila respectiv s fie
definit ncepnd din punctul scrierii ei i pn la sfritul fiierului surs respectiv. De aceea
se recomand ca definiiile variabilelor globale s fie scrise la nceputul fiierului surs.
Exemplu:
int i; double x;
void main ()
{
i = 100;

x = i*x;
}
Variabilele i i x, fiind definite n afara funciilor programului, sunt globale.
Construciile int i i respectiv double x sunt definiiile lor, iar din punct de vedere
sintactic ele au acelai format ca declaraii obinuite.
Ele pot fi folosite n toate funciile din fiierul surs care conine definiiile lor fr alte
declaraii suplimentare.
n schimb, pentru a le utiliza n funcii ale programului situate n alte fiiere surs dect
n cel n care sunt definite, ele trebuie declarate ca externe n funciile respective.
O declaraie de variabil extern este la fel ca i o declaraie obinuit, doar c ea
ncepe prin cuvntul extern.
De exemplu, dac dorim s folosim variabile globale i i x definite mai sus ntr-o
funcie f, aflat ntr-un alt fiier surs dect cel n care au fost ele definite, atunci n corpul
funciei f le vom declara ca externe:
f ()
{ extern int i;
extern double x;

x = i*x;
}
174
Limbajul de programare C
Compilatorul aloc memorie pentru variabilele globale n momentul ntlnirii
definiiilor lor. Aceast memorie se afl ntr-o zon special destinat acestui scop i ea
rmne alocat pe ntreaga durat a execuiei programului.
6.9.2. Variabile locale
Variabilele locale nu sunt valabile n tot programul. Exemple de variabile locale sunt
variabilele declarate n corpul unei funcii i care nu sunt declarate ca externe. Ele pot fi
folosite numai n corpul funciei respective, adic ncepnd din momentul apelrii funciei
respective i pn la revenirea din ea.
Astfel de variabile pot fi alocate pe stiv. n acest caz, lor li se aloc memorie pe stiv
la intrarea n funcia n care sunt declarate i i pierd alocarea respectiv la ieirea din funcie,
cnd se reface stiva la forma dinaintea apelului.
Stiva este o zon de memorie cu o destinaie special. Ea are o structur de tip LIFO
(Last Input First Output). ntr-o astfel de zon de memorie se aloc pe rnd memorie
variabilelor locale declarate n corpul funciilor. Eliberarea memoriei astfel alocate se face n
ordine invers alocrii ei, la terminarea execuiei funciei respective.
Variabilele alocate n acest fel se numesc automatice. Ele se declar n corpul
funciilor i au sintaxa obinuit.
Se poate ca variabilele locale s nu fie alocate pe stiv, ci ntr-o zon de memorie
destinat special n acest scop. O astfel de variabil se spune c este static.
O variabil static declarat n corpul unei funcii are domeniul de valabilitate numai
corpul funciei respective, ca i variabilele automatice. Spre deosebire de ele, variabila static
nu se aloc pe stiv, ci ntr-o zon de memorie destinat acestui scop.
n cazul n care o variabil static este declarat n afara funciilor, ea este definit din
punctul n care a fost declarat i pn la sfritul fiierului surs care conine declaraia ei.
Spre deosebire de variabilele globale, o variabil static declarat n acest fel nu poate fi
declarat ca extern.
Menionm c tablourile de dimensiuni mari se recomand a fi declarate statice,
deoarece dac ele sunt automatice, pot s conduc la depirea stivei alocate programului.
Amintim c, implicit, dimensiunea stivei alocate unui program este de 4K octei.
Exemple:
1. Fiierul fis1.cpp este un fiier surs care conine dou variabile globale i i x o
variabil static y i dou funcii main i f, care i ele conin la rndul lor variabilele statice a
i b.
/* variabile globale
int i; /* definitia variabilei i */
175
Limbajul de programare C
double x; /*definitia variabilelor x
*/
/* variabile statice */
static int y;
void main ()
{
static char a; /* variabila statica
*/
int c; /* variabila automatica
*/
.
/* se pot folosi variabilele i, x, y, a si c*/
.
}
f()
{ int p; /* variabila automatica
*/
static float b; /* variabila statica */
..
/* se pot folosi variabilele i, x, y, p si b */
.
}
Variabilele a i c sunt locale funciei main i ele nu pot fi folosite n funcia f. La fel,
variabilele p i b sunt locale n funcia f i nu pot fi folosite n funcia main.
2. Funcia fis2.cpp conine funciile f1 i f2 care intr n componena aceluiai
program ca i funciile main i f din fiierul fis2.cpp.
/* variabile statice */
static usingned t;
f1 ()
{
extern int i; /* declaratie externa pentru i */
extern double x; /* declaratie externa pentru x */
static int k;
.
/* se pot folosi variabilele i, x, k si t */
}
176
Limbajul de programare C
f2()
{ extern int i; /* declaratie externa pentru i */
static double s; /* variabila statica */

/* se pot folosi variabilele i, s, si t */
}
Se observ c variabila static y, definit n fiierul fis1.cpp, nu poate fi utilizat n
fiierul fis2.cpp. De asemenea, variabila static t nu poate fi folosit n fiierul fis1.cpp.
Variabila global x nu poate fi folosit n funcia f2, ea nefiind declarat ca i extern.
Observaii:
1. Variabilele globale constituie un mijloc simplu de interfa ntre funciile unui
program.
2. Funciile, ca i variabile globale, pot fi utilizate n tot programul.
6.9.3. Variabile registru
Limbajul C ofer posibilitatea de a aloca variabilele n regitri. Prin aceasta se poate
economisi att timp de execuie, ct i memorie.
Se pot aloca n regitri numai parametrii i variabilele automatice de tip int, char i
pointer.
O variabil registru se declar n mod obinuit, precednd declaraia ei prin cuvntul
register.
Exemplu:
f(register int x)
{ register char a;
}
nti se aloc parametrul x ntr-un registru i apoi se aloc a ntr-un alt registru.
Observaie: Se recomand alocarea n regitri a variabilelor care au o utilizare mare,
innd seama ns, c numrul acestora este relativ mic.
6.9.4. Iniializarea
Variabilelor li se pot atribui valori iniiale. O variabil simpl se poate iniializa
printr-o declaraie de forma:
tip nume = expresie;
sau static tip nume = expresie;
dac variabila este static.
Variabilelor globale i statice li se atribuie valorile iniiale la lansarea programului.
Expresia utilizat n cazul acestor variabile trebuie s fie expresie constant care s poat fi
177
Limbajul de programare C
evaluat de compilator la ntlnirea ei, deoarece variabilele globale i statice se iniializeaz
prin valori definite la compilare.
Variabilele automatice se iniializeaz la execuie, de fiecare dat cnd se activeaz
funcia n care sunt declarate. Din aceast cauz, nu mai este necesar ca expresia s fie o
expresie constant. Totui, la ntlnirea ei, trebuie s fie definii operanzii expresie de
iniializare.
Exemplu:
f (int n) ;
{ int x = 10;
int y = x*n;
}
La ntlnirea expresiei x*n sunt deja definii ambii operanzi:
- x a fost n prealabil iniializat;
- n are o valoare care a fost deja transferat la apel.
Variabilele globale i statice neiniializate au implicit valoarea egal cu zero.
Variabilele automatice neiniializate au o valoare iniial imprevizibil.
Tablourile se pot iniializa printr-o list de expresii incluse ntre acolade. Astfel, un
tablou unidimensional se poate iniializa folosind urmtorul format:
tip nume [n] = {exp
1
, exp
2
, , exp
n
}; sau
static tip nume [n] = {exp
1
, exp
2
, , exp
n
}; dac tabloul este static
Pentru un tablou bidimensional vom folosi urmtorul format:
tip nume [n] = {
{exp
11
, exp
12
, , exp
1m
},
{exp
21
, exp
22
, , exp
2m
},
...............
{exp
n1
, exp
n2
, , exp
nm
},
};
Exemple:
int it[ ] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int matrice [4] [3] = {{-1, 0, 1}, {-1}, {0, 1}, {0, 0, 1}}.
Observaii:
1. n cazul vectorului it nu s-a specificat la declarare dimensiunea acestuia deoarece
ea rezult n urma iniializrii, fiind egal cu 9;
2. La iniializarea elementelor tabloului matrice o parte din elemente pot rmne
neiniializate.
6.10. Pointeri n limbajul C
Un pointer este o variabil care are ca valori adrese.
178
Limbajul de programare C
Pointerii se utilizeaz pentru a face referire la date cunoscute prin adresele lor. Astfel,
dac p este o variabil de tip care are ca valoare adresa zonei de memorie alocat pentru
variabila ntreag x, atunci: p reprezint valoarea variabilei x, ca n figura 6.2:
n figura 6.2 valoarea 2500 reprezint adresa de memorie a valorii 5, adic este un
pointer.
Operatorul (unar) are aceeai prioritate ca i ceilali operatori unari din limbajul C.
Dac p conine adresa zonei de memorie alocat variabilei x, vom spune c p
pointeaz spre x sau c p conine adresa lui x.
Pentru a atribui unui pointer adresa unei variabile, putem folosi operatorul unar &.
Astfel, dac dorim ca p s pointeze spre x, putem utiliza atribuirea: p = &x;
6.10.1. Declaraia de pointer
n general, un pointer se declar prin:
tip nume;
ceea ce nseamn c nume este un pointer care pointeaz spre o zon de memorie ce conine o
dat de tipul tip.
Comparnd declaraia anterioar de pointer cu una obinuit:
tip nume;
putem considera c tip dintr-o declaraie pointer reprezint tip dintr-o declaraie obinuit.
De aceea, construcia tip se spune c reprezint un tip nou, tipul pointer.
Exist cazuri n care dorim ca un pointer s fie utilizat cu mai multe tipuri de date. n
acest caz, la declararea lui nu dorim s specificm un tip anume. Aceasta se realizeaz folosind
cuvntul cheie void astfel:
void nume;
Exemple:
179

Memoria p (adresa de memorie


a variabilei x)

Memoria
2500
Fig. 6.2. Referirea la o variabil utiliznd pointeri.
Limbajul de programare C
Fie declaraiile:
int x,y;
int p;
1. y = x+100; este echivalent cu secvena:
p = &x;
y = p+100;
n care lui y i se atribuie suma dintre coninutul zonei a crei adres se afl n p i 100.
2. x = y; este echivalent cu secvena:
p = &x;
p = y;
n care coninutul zonei a crei adres se afl n p devine egal cu valoarea lui y.
3. x++; este echivalent cu secvena:
p = &x;
(p)++;
n care coninutul zonei a crei adres se afl n p se mrete cu 1.
4. Fie funcia perm de mai jos:
void perm(int x,int y)
{ int temp;
temp=x;
x=y;
y=temp; }
Un apel de forma:
perm (a,b);
nu are nici un efect, deoarece la acest apel valoarea lui a se transfer parametrului x, iar a lui b
parametrului y. Apoi funcia perm permut valorile parametrilor x i y, ns prin aceasta
valorile parametrilor efectivi a i b rmn nemodificate. Deci la revenirea din funcia perm,
variabilele a i b au aceleai valori nemodificate de apel.
Pentru a realiza o permutare a valorilor acestor variabile, este necesar s se transfere nu
valorile, ci adresele variabilelor.
Acest lucru este posibil dac modificm apelul n felul urmtor:
perm (&a, &b);
n acest caz parametrii formali ai funciei perm au ca valori adrese, deci ei sunt
pointeri. Avnd n vedere acest fapt, funcia perm sufer urmtoarele modificri:
void perm(int x,int y); /*x si y sunt pointeri spre
intregi*/
{ int temp;
180
Limbajul de programare C
temp=x; /* lui temp i se atribuie continutul zonei a carei
adresa
se afla in x, adica se face
temp=a*/
*x = *y; /* in zona de la adresa x se transfera continutul
zonei a carei adresa se afla in y, deci realizeaza a = b
*/
*y = temp; /* in zona de la adresa y se transfera valoarea lui
temp,deci b = temp */
}
6.10.2. Pointeri i tablouri
Numele unui tablou este un pointer deoarece el are ca valoare adresa primului su
element. Totui exist o diferen ntre numele unui tablou i o variabil de tip pointer,
deoarece dac unei variabile de tip pointer i se poate atribui o adres, acest lucru nu se poate
realiza pentru numele unui tablou, el fiind ntotdeauna un pointer spre primul element al
tabloului. Cu alte cuvinte, numele unui tablou trebuie considerat ca fiind un pointer constant.
Deci, dac p este un pointer spre ntregi i a este un tablou de tip ntreg, o atribuire de
forma
p = a;
este corect. Prin aceast atribuire pointerul p va pointa i el spre primul element al tabloului,
ca n figura 6.3:
n schimb o atribuire de forma: a = p; este interzis, p fiind un pointer constant.
Legtura dintre tablorui i pointeri ne ofer o alt soluie de prelucrare a unor mulimi
de valori, nu prin intermediul unui tablou clasic, ci prin intermediul unui pointer. Varianta cu
pointeri ofer avantajul alocrii stricte a unui numr de componenete.
S considerm situaia n care dorim s pstrm mediile a n studeni. Pentru c nu se
cunoate exact ci studeni vor fi, vom declara un tablou avnd un numr maxim estimat de
componente, ca n urmtoarea declaraie :
181
Adresa lui a(0)
Adresa lui a(0)
a(0) a(1) a(2) ... a(n)
a
p
p = a
Fig. 6.3. Atribuirea numelui unui tablou unei variabile de tip pointer.
Limbajul de programare C
float medii[30];
Aceast declaraie va avea ca efect alocarea unei zone contigue de 30 de locaii
(fiecare avnd dimensiunea de 4 octei), chiar dac programatorul va stoca doar 25 de medii.
Varianta cu pointeri va consta n declararea unui pointer spre float, la care se va aloca exact
attea locaii ct studeni sunt.
Un alt exemplu este cel n care lucrm cu tablouri de caractere. De exemplu, pentru a
pstra numele unei persoane, se putea folosi un tablou declarat astfel:
char Nume[20];
Varianta cu pointeri va consta n declaraia urmtoare:
char *Nume;
Aceast variant este mult mai flexibil oferind posibilitatea de a aloca exact attea
poziii ct este necesar.
6.10.3. Operaii cu pointeri
Asupra pointerilor se pot face diferite operaii, indicate n cele ce urmeaz.
a) Operaii de incrementare i decrementare
Operatorii de incrementare i decrementare se pot aplica variabilelor de tip pointer.
Ei se execut altfel dect asupra datelor de alte tipuri.
Efect:
- operatorul de incrementare aplicat asupra unui operand de tip pointer spre tipul t
mrete adresa coninut de operand cu numrul de octei necesari pentru a pstra o dat de
tipul t;
- operatorul de decrementare se execut n mod analog.
Observaie:
Aceast proprietate este foarte util cnd se au n vedere prelucrri de tablou. Fie
declaraiile de mai jos:
int tab[n];
int *p;
int i;
unde n este o constant ntreag, fie p = &tab[i]; unde 0<i<n-1.
n acest caz, instruciunea p++; mrete valoarea lui p cu 2 i deci va reprezenta adresa
elementului tab[i+1].
b) Adunarea i scderea unui ntreg dintr-un pointer
Dac p este un pointer sunt corecte expresiile de forma: p+n i p-n unde n este de tip
ntreg.
182
Limbajul de programare C
Efect:
- expresia p+n mrete valoarea lui p cu nt, unde t este numrul de octei necesari
pentru a memora o dat de un tip spre care pointeaz p;
- n mod analog, expresia p-n micoreaz valoarea lui p cu nt.
Dac a este un tablou cu elemente de tipul t, atunci a este un pointer, deci expresia
a+n este corect i va reprezenta un pointer spre elementul a[n] (adresa acestuia). Valoarea
acestui element se poate obine din expresia (a+n).
n acest fel se pot nlocui indicii de tablou prin expresii cu pointeri.
c) Compararea a doi pointeri
Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi comparai folosind
operatorii de relaie i de egalitate.
Astfel, dac p i q sunt doi pointeri care pointeaz spre elementele t[i] respectiv t[j] ale
tabloului t, expresia p<q are sens i ea este adevrat dac i<j. De asemenea p! = q are
valoarea adevrat dac i j, etc.
Este permis compararea pointerilor i cu constanta simbolic NULL care se poate
interpreta ca nici o adres. Astfel dac expresia: p==NULL este adevrat nseamn c p
nu conine nici o adres.
d) Diferena a doi pointeri
Doi pointeri care pointeaz spre elementele aceluiai tablou pot fi sczui. Rezultatul
diferenei a doi pointeri este definit astfel: fie a un tablou de un tip oarecare i p, q doi
pointeri, p conine adresa elementului a[i], iar q conine adresa elementului a[i+n]. Atunci
diferena: q-p are valoarea egal cu n.
6.10.4. Alocarea dinamic a memoriei
Alocarea de zone de memorie i eliberarea lor n timpul execuiei programelor permite
gestionarea optim a memoriei de ctre programe. Un astfel de mijloc de gestionare a
memoriei l vom numi alocare dinamic a memoriei.
n acest paragraf indicm trei funcii din biblioteca limbajului C, utilizate frecvent n
alocarea dinamic a memoriei.
Prototipurile lor se afl n fiierele standard alloc.h i stdlib.h.
Funcia malloc permite alocarea unui bloc de memorie a crui dimensiune se specific
n octei. Funcia returneaz un pointer spre nceputul zonei alocate. ntruct acest pointer
trebuie s permit memorarea oricrui tip de dat n zona alocat, el este de tip void*.
Prototipul funciei este:
183
Limbajul de programare C
void *malloc (usingned n);
unde n este numrul de octei ai zonei de memorie care se aloc.
n cazul n care n este prea mare, funcia returneaz pointerul NULL.
Funcia calloc are prototipul:
void *calloc (usingned nrelem, usingned dimelem);
unde: - dimelem este dimensiunea n octei a unui element de dat;
- nrelem este numrul elementelor pentru are se aloc memorie.
Prin aceast funcie se aloc nrelem*dimelem octei.
Ea returneaz pointerul spre nceputul zonei rezervate sau pointerul NULL n cazul n
care numrul octeilor este prea mare (depete zona de memorie liber afectat alocrilor
dinamice).
Elementele din zona de memorie alocat prin calloc au valoarea zero.
Funcia free elibereaz o zon de memorie care n prealabil a fost alocat prin malloc
sau calloc. Prototipul ei este:
void free(void *p);
unde p este pointerul returnat de malloc sau calloc la alocare, deci este pointerul spre
nceputul zonei care se elibereaz.
Exemplu:
Funcia memoreaz un ir de caracatere ntr-o zon de memorie alocat prin funcia
malloc. Ea returneaz adresa de nceput a zonei n care s-a salvat irul de caractere, deci
returneaz un pointer spre tipul char.
#include <stdio.h>
#include <alloc.h>
#include <string.h>
char *memoreaza (char *s)
/* memoreaza sirul de caractere spre care pointeaza s in zona
de memorie furnizata de malloc*/
{
if ((p = (char *) malloc (strlen(s)+1)) ! = NULL)
{ strcpy(p,s); return p;
}
else return NULL;
}
n exemplul anterior s-au utilizat dou funcii specifice irurilor de caractere, strlen,
care returneaz lungimea unui ir de caractere i strcpy, care copiaz un ir de caractere n
altul, funcii definite n fiierul string.h.
184
Limbajul de programare C
6.10.5. Prelucrarea tablourilor unidimensionale folosind pointeri
S-a artat c numele unui tablou este pointer constant care are ca valoare adresa
primului element al su. Pentru declaraia:
tip a[];
identificatorul a are ca valoare adresa elementului a[0] , adic &a[0].
Fie tip*p; atunci atribuirea de forma p = a; este corect i n urma acestei atribuiri, p
are valoare tot adresa lui a[0].
Dac utilizm ca parametru efectiv numele unui tablou unidimensional, atunci
parametrul formal corespunztor poate fi declarat fie ca un tablou unidimensional (cu
paranteze ptrate vide), fie printr-un pointer:
tip a [100]

f(a);

Antetul lui f se definete prin unul din formatele:


f(tip t[]) sau f(tip*t).
Indiferent care dintre antete se alege, n corpul funciei f se pot folosi att variabilele cu
indici: t[exp] ct i expresii cu pointeri: *(t+exp) pentru a face acces la elementele tabloului
tab.
S-a artat c n general, dac tab este un tablou unidimensional de tipul tip, atunci a+n
este chiar adresa elementului a[n]. Deci:
x = a[n] sau x = *(a+n)
sunt atribuiri care au acelai effect (atribuire lui x valoarea elementului a[n]). Expresiile cu
pointeri pot fi folosite i n stnga operatorului de atribuire, adic:
a[n] = x
i *(a+n) = x
au acelai efect.
Din cele de mai sus rezult c variabilele cu indici se pot echivala prin expresii cu
pointeri. Este posibil s procedm i invers, adic s nlocuim expresiile cu pointeri prin
variabile cu indici, dei acest lucru nu este recomandabil.
Fie, de exemplu, declaraia:
tip*p = (tip*)malloc(...);
Expresia *(p+n) este echivalent cu expresia p[n].
Exemplu:
Programul urmtor calculeaz i afieaz suma elementelor ntregi ale unui vector de
dimensiune n .
#include <stdio.h>
185
Limbajul de programare C
#include <conio.h>
void main()
{ int a[100],i,n;
printf("\nIntroduceti numarul de elemente al vectorului");
scanf("%d%",&n);
printf("\nIntroduceti elementele vectorului:");
for (i=0;i<n;i++)
{ printf("\na[%d]=",i);
scanf("%d",a+i); }
int s=0;
for (i=0;i<n;i++)
s=s+*(a+i);
printf("\nSuma elementelor vectorului a este: %d",s);
getch(); }
Se observ c att la citirea elementelor ct i la calculul sumei s-au utilizat expresii cu
pointeri n locul expresiilor cu indici .
Legtura dintre tablouri i pointeri ne ofer o alt soluie de prelucrare a unor mulimi
de valori, nu prin intermediul unui tablou clasic, ci prin intermediul unui pointer. Varianta cu
pointer ofer avantajul alocrii stricte a unui numr de componenete, exact cte are nevoie
programatorul.
Vom considera un exemplu n care vom renuna la tablouri statice i vom folosi o
variant dinamic, bazat pe pointeri. Fie notele la laborator ale studenilor unei grupe, note
stocate ntr-un vector declarat ca pointer. Se dorete afiarea notelor studenilor, determinarea
mediei grupei la laborator i numrarea a cte note peste 8 exist.
Datele se pot stoca ntr-un vector static, de genul int Note[30], unde s-a estimat
existena a maxim 30 de studeni. Varianta dinamic const n folosirea unui pointer cruia i se
rezerv o zon exact de memorie, n care s se stocheze acele valori, pointer declarat astfel:
int *Note. Folosind operaiile cu pointeri prezentate anterior, se pot accesa valorile din zona de
memorie alocat.
Prezentm, mai jos, programul respectiv :
#include <conio.h>
#include <stdio.h>
#include <alloc.h>
int N, *Note; //se declar pointerul folosit la stocarea datelor
void Citire (void)
{ int I;
printf("\n Dati numarul de studenti");
scanf("%d",&N);
186
Limbajul de programare C
Note = (int *) malloc (N * sizeof(int)); //se aloca memorie pentru cele n
note
for (I=0; I<N ; I++)
{ printf("\n Dati nota studentului %d",I+1);
scanf("%d", Note+I); }
}
void Afisare(int *Note, int N)
{ int I;
for (I=0; I<N ; I++)
printf("\t%d",*(Note+I) );
printf("\n"); }
float Media(int *Note, int N)
{ float Med=0;
int I;
for (I=0; I<N ; I++)
Med += *(Note+I);
return Med/N; }
int Numar (int *Note, int N)
{ int I, Nr;
Nr=0;
for (I=0; I<N ; I++)
if (*(Note+I)>=8) Nr++;
return Nr; }
void main()
{ Citire();
Afisare(Note, N);
printf("\n Media notelor este %5.2f", Media(Note, N));
printf("\n Numarul notelor peste 8 este %d", Numar(Note, N));
getch();
}
6.10.6. Prelucrarea tablourilor bidimensionale folosind pointeri.Tablouri de pointeri
Numele unui tablou bidimensional, la fel ca numele unui tablou unidimensional, are ca
valoare adresa primului element al tabloului.
Fie, de exemplu, declaraia:
tip tab[...] [...];
atunci tab are ca valoare adresa lui tab[0][0].
187
Limbajul de programare C
Dac n cazul tablourilor unidimensionale, tab+n este adresa elementului tab[n], adic
&tab[n], n cazul tablourilor bidimensionale, expresia tab+n este adresa elementului
tab[n][0].
Aceast interpretare rezult din faptul c un tablou bidimensional trebuie considerat ca
fiind un tablou de tablouri unidimensionale. n general, un tablou k dimensional este un
tablou de tablouri k-1 dimensionale.
Fie, de exemplu, declaraia:
tip tab[m][n];
Tabloul tab poate fi privit ca m tablouri unidimensionale. Astfel, tab[0], tab[1], ,
tab[m-1] sunt cele m elemente ale lui tab i fiecare este un pointer spre cte un tablou
unidimensional de n elemente. De aici rezult c tab[0] are ca valoare adresa lui tab[0][0],
tab[1] are ca valoare adresa lui tab[1][0] i n general tab[i] are ca valoare adresa lui tab[i][0].
n figura 6.4 este prezentat interpretarea tablourilor bidimensionale descris anterior.
Cum *(tab+i) are ca valoare chiar tab[i], rezult c tab[i][0] are aceeai valoare cu
*(*(tab+i)).
De asemenea, tab[i]+j are ca valoare adresa lui tab[i][j]. Deci tab[i][j] are aceeai
valoare ca i expresia *(tab[i]+j), iar aceasta din urm are aceeai valoare cu expresia
*(*(tab+i)+j).
nseamn c atribuirile *(*(tab+i)+j) = x i tab[i][j] = x sunt echivalente.
Dac tab este un tablou unidimensional declarat prin:
tip tab[...];
atunci tab are tipul tip* adic pointer spre tip.
Dac tab este un tablou bidimensional declarat prin:
tip tab[m][n];
atunci tab are tipul tip (*)[n] adic pointer spre tip.
Menionm c n declaraia de mai sus m i n sunt expresii constante.
S presupunem c tab este parametru la apelul funciei f:
... f(tab).
n acest caz, parametrul formal al funciei f poate fi declarat n urmtoarele moduri:
... f (tip t[ ][n]) sau ... f(tip(*p)[n]).
188
tab[0][0] tab[0][1] tab[0][n-1]
tab[1][0] tab[1][1] tab[1][n-1]
... ... ...
tab[m-1][0] tab[m-1][1] tab[m-1][n-1]
tab[0]
tab[1]
...
tab[m-1]
Fig. 6.4. Interpretarea tabloului bidimensional ca o mulime de
tablouri unidimensionale.
Limbajul de programare C
Fie declaraia:
tip *tab1[n];
n acest caz tab1 este un tablou unidimensional de pointeri spre tipul tip, adic
fiecare element din cele n ale tabloului tab1 este un pointer spre tip. Tablourile tab i tab1 nu
trebuie confundate. Tabloului tab i se aloc o memorie de m*n*sizeof(tip) octei. Tabloului
tab1 i se aloc n*sizeof(tip*) octei.
Dac o funcie g are la apel ca parametru pe tabloul tab1: ... g(tab1) ... atunci
parametrul formal al funciei g poate fi declarat n urmtoarele moduri:
... g(tip*p[]) sau ...g(tip**p).
Exemple:
1. Fie declaraiile:
double tab[2][3] = {{0, 1, 2}, {3, 4, 5}};
double *p;
n tabelul 6.7 sunt prezentate rezultate ale execuiei unor instruciuni:
Tabelul 6.7.
Instruciuni
Eleme
ntul
afiat
Valoarea
elementului
p = tab[0];
printf (%g, *p);
tab[0] [0]; 0
p = tab[1];
printf (%g, *p);
tab[1] [0]; 3
printf (%g, *(*(tab))); tab[0] [0]; 0
printf (%g, *(*(tab+1)+2)); tab[1] [2]; 5
2. Considerm funcia f definit astfel:
void f(double (*p)[3])
{ int i, j;
for (i=0; i<2; i++)
for (j = 0; j < 3; j ++) printf (%g , p[i][j]).
}
La apelul f(tab); unde tab este tabloul definit n exerciiul 1, se afieaz valorile
elementelor lui tab separate de cte un spaiu:
0 1 2 3 4 5
Expresia p[i][j] poate fi nlocuit cu *(*(p+i)+j).
3. Fie declaraiile:
double *t[2];
189
Limbajul de programare C
double t0[3] = {10, 11, 12};
double t1[3] = {13, 14, 15};
i atribuirile t[0] = t0; t[1] = t1;
Definim funcia f1 astfel:
void f1(double *p[ ])
{

}
unde corpul funciei f1 coincide cu al funciei f.
La apelurile f1(t); se listeaz valorile elementelor tablourilor t0 i t1, separate prin cte
un spaiu, adic:
10 11 12 13 14 15
Acelai rezultat se obine dac antetul lui f1 se schimb cu:
void f1 (double **p)
n ambele situaii, expresia p[i][j] poate fi schimbat cu *(*(p+i)+j).
Ca o consecin a celor prezentate anterior, putem renuna la tablourile bidimensionale
statice i putem folosi tablouri de pointeri. Acest lucru este posibil pentru c pointerii, fiind
variabile, pot forma alte tipuri de date structurate, de exemplu tablouri. Dac T este un tip de
date oarecare, tipul pointerilor spre acel tip va fi T*.
Un tablou de pointeri spre tipul T se declar prin :
T *Nume[dim];
unde Nume va fi numele tabloului de pointeri, iar dim numrul de pointeri din tablou.
Cu ajutorul unui tablou de pointeri, se pot pstra datele unei matrici, dar cu avantajul c
fiecare linie are o lungime variabil.
S considerm un exemplu n care se dau notele a n candidai la cele m probe date la
un concurs pentru obinerea unui post. Putem folosi un tablou de pointeri pentru afiarea
notelor fiecrui candidat i pentru determinarea mediei obinute de fiecare candidat. Notele
candidailor vor putea fi stocate i accesate cu ajutorul unui tablou de pointeri, declarat astfel:
Note[I]
Note[0]
Note[1]
.
Note[19]
Prezentm programul surs n varianta cu tablouri de pointeri:
#include <conio.h>
#include <stdio.h>
#include <alloc.h>
190
4 5 8 9 6 7 5
9 9 6 7 9 8 8
8 7 4 5 6 7 8
Fig. 6.5. Memorarea notelor studenilor
utiliznd pointerii.
int *Note[20];
adic ca un tablou de 20 de pointeri, cte un
pointer pentru fiecare candidat. De fapt,
fiecare pointer va conine adresa unei zone
de memorie unde sunt stocate notele acelui
candidat (zona ce trebuie rezervat ). Acest
lucru se observ n figura 6.5:
Limbajul de programare C
int N, M, *Note[20];
float *Media;
void Citire (void)
{ int I, J;
printf("\n Dati numarul de candidati");
scanf("%d",&N);
printf("\n Dati numarul de probe");
scanf("%d",&M);
for(I=0; I<N; I++)
Note[I] = (int *) malloc (N * sizeof(int)); //se aloca memorie pentru cele n
note
Media = (float *)malloc(N*4);
for (I=0; I<N ; I++)
{ *(Media+I)=0;
for (J=0; J<M ; J++)
{ printf("\n Dati nota studentului %d la proba %d",I+1, J+1);
scanf("%d", Note[I]+J);
*(Media+I) += *(Note[I]+J); }
*(Media+I)=*(Media+I)/M; }
}
void Afisare(int *Note[20], int N, int M)
{ int I, J;
for (I=0; I<N ; I++)
{ printf("\n Notele candidatului %d sunt", I+1);
for (J=0; J<M ; J++)
printf(" %d",*(Note[I]+J) );
printf("\tsi media %5.2f", *(Media +I));}
printf("\n"); }
void main()
{ int I;
clrscr();
Citire();
Afisare(Note, N, M);
for (I=0; I<N; I++) free(Note[I]); // se elibereaz memoria rezervat
getch(); }
Un alt exemplu, n care tablourile de pointeri ofer o soluie mai bun din punct de
vedere al spaiului ocupat, este aceea n care se lucreaz cu liste de denumiri de obiecte. S
considerm situaia n care se pstreaz numele a n persoane. Fiecare nume de persoan are o
anumit lungime. Prin urmare, cea mai bun soluie const n folosirea unui tablou de pointeri
191
POPESCU ION
AVRAMESCU DORU
ION ION
Fig. 6.6. Memorarea datelor personale utiliznd
pointerii.
Vom prezenta, mai departe,
un exemplu de cum se prelucreaz o
astfel de list de nume de persoane,
folosind un tablou de pointeri.
Pentru exemplificare, vom afia
numele acestora n ordine
alfabetic, convertite n mari i vom
afia cel mai lung nume. Vom
declara lista cu numele celor n
persoane astfel:
Limbajul de programare C
n care s se pstreze adresele spre zonele n care sunt stocate numele de persoane. Varianta
cu tablouri de pointeri va permite folosirea judicioas a spaiului de memorie, exact att ct
este nevoie. Acest lucru se observ i n figura 6.6.
Nume[I]
Nume[0]
Nume[1]
.
Nume[99]
char *Nume[99];
Se observ faptul c se declar 100 de adrese i nu 100 de iruri de caractere, soluia
aceasta oferind o economie de memorie. Sursa programului este prezentat mai jos:
#include <conio.h>
#include <stdio.h>
#include <alloc.h>
#include <string.h>
int N;
char *Nume[99]; // lista cu adresele celor n nume, maxim 100
void Citire (void)
{ int I, J;
printf("\n Dati numarul de persoane");
scanf("%d",&N);
for(I=0; I<N; I++)
Nume[I] = (char *) malloc (N * sizeof(char));//alocarea memoriei fiecrui
nume
for (I=0; I<N ; I++)
{ printf("\n Dati numele persoanei %d ",I+1);
scanf("%s", Nume[I]);
for (J=0; J<=strlen(Nume[I]); J++)
if ((*(Nume[I]+J)>='a')&&(*(Nume[I]+J)<='z'))
*(Nume[I]+J) -= 32; //fiecare litera mica se transforma in litera mare
}}
void Ordonare(char *Nume[20], int N)
{ int I, Ok; char *Aux;
Aux = (char *) malloc(15);
do
{ Ok=0;
192
Limbajul de programare C
for (I=0; I<=N; I++)
if ( strcmp(Nume[I], Nume[I+1])>0)
{ strcpy (Aux, Nume[I]);
strcpy (Nume[I], Nume[I+1]);
strcpy (Nume[I+1], Aux);
Ok=1; }
}
while (Ok); }
void Afisare(char *Nume[20], int N)
{ int I;
Ordonare(Nume, N);
for (I=0; I<N ; I++)
printf("\n%s",Nume[I]);
printf("\n"); }
char *Cel_mai_lung(char *Nume[20], int N)
{ int I, Lmax;
char *Aux;
Aux = (char *)malloc(15);
Lmax = strlen(Nume[0]);
strcpy(Aux, Nume[0]);
for (I=0; I<N ; I++)
if (Lmax < strlen (Nume[I]))
{ Lmax = strlen(Nume[I]);
strcpy(Aux, Nume[I]); }
return Aux; }
void main()
{ int I;
clrscr();
Citire();
printf("\nLista persoanelor ordonata alfabetic");
Afisare(Nume, N );
printf("\n Cel mai lung nume este %s", Cel_mai_lung(Nume, N));
for (I=0; I<N; I++) free(Nume[I]);
getch(); }
6.10.7. Pointeri spre funcii
Orice funcie are asociat o adres fix de memorie, anume adresa de nceput a
funciei. Aceast adres de nceput este stocat n numele funciei. Prin urmare, numele unei
funcii este un pointer spre funcia respectiv. El poate fi folosit ca parametru efectiv la
193
Limbajul de programare C
apeluri de funcii. n felul acesta, o funcie poate transfera funciei apelate un pointer spre o
funcie. Aceasta, la rndul ei, poate apela funcia care i-a fost transferat n acest fel.
Pointerii spre funcii sunt pointeri speciali ce permit declarea parametrilor de tip
funcie. Cu aceti parametrii de tip pointer spre funcie, putem transmite o funcie ca
parametru la alt funcie.
Forma general a declaraiei unui pointer la un tip de funcie este :
tip (*pf) (list parametrii formali) ;
unde tip este tipul valorii returnate de funcie, pf va fi numele pointerului. Se remarc prezena
parantezelor, fr ele declaraia ar spune c pf este o o funcie ce returneaz un pointer. De
exemplu, declaraia int (*p)(int x, int x) precizeaz c p este un pointer spre o funcie de tip
int, care are doi parametrii.
Apelul unei funcii prin intermediul unui pointer se face cu construcia:
(*pf)(list parametrii actuali);
sau var= (*pf)(list parametrii actuali);
S considerm cazul n care dorii s scriei o funcie general care s poat calcula
valoarea unei integrale dintr-o funcie oarecare (integrabil). De fapt, dorii s rulai aceast
funcie de calculul integralei cu diverse funcii transmise de utilizator. Din pcate, n
momentul construirii funciei de calcul nu cunoatei care este funcia. Totui, acest lucru va fi
posibil folosind pointeri spre funcii.
n primul rnd, funciile pentru care se dorete calculul integralei se vor putea declara
obinuit, sub forma :
double f1(double x)
{ return x*x 1; }
sau
double f2(double x)
{ return sin (x *x); }
sau
double f3(double x)
{ return exp(x); }
Funcia general de calcul ( bazat pe o anumit metod de integrare) va trebui s aib
prototipul urmtor :
double Integrala( double A, double B, int N, double (*p)(double) );
unde A , B reprezint capetele de integrare iar n numrul de puncte pentru diviziunea aleas
(vom folosi o metod de integrare bazat pe analiz numeric, folosind metoda trapezelor).
Pointerul spre funcie este declarat astfel :
double (*p)(double) .
S ncercm s interpretm aceast construcie :
*p - este un pointer
194
Limbajul de programare C
(*p)(double) - nseamn c p este un pointer spre funcii cu argumente de
tip double.
double (*p)(double) - nseamn c p este un pointer spre funcii care
returneaz o valoare de tip double i are ca parametru un double.
Pointerul p va putea fi folosit pentru a accesa valorile funciei. Apelarea funciei se
face cu expresia (*p)(x). Mai mult, funcia general construit anterior, se va putea apela
( pentru exemplele de funcii prezentate anterior) astfel :
ValInt= Integrala(10, 20, 100, f1);
sau
A=-3.14; B=3.14 ; // adic A i B sunt egale cu Pi i Pi
ValInt= Integrala(A, B, 100, f2);
Trebuie s atragem atenia asupra faptului c construcia double *p(double) este
corect, dar are alt semnificaie: reprezint prototipul unei funcii ce returneaz un pointer
spre double (!!). Acest lucru este datorat faptului c parantezele () sunt mai prioritare dect
operatorul *.
Prezentm programul care calculeaz valoarea integralei.
#include <graphics.h>
#include <math.h>
#include <stdio.h>
#include <conio.h>
double f(double x)
{ return x*x-1; }
double f1(double x)
{ return sin(x*x); }
double f2(double x)
{ return exp(x); }
double Integrala(double A, double B, int n, double (*p)(double))
{ double pas,S;int i;
pas=(B-A)/n;
S=0;
for(i=0;i<n;i++)
S=S + (*p)(A+i*pas);
S=S + (*p)(A)/2 + (*p)(B)/2;
S=S*pas;
return S; }
void main()
{ double ValInt,A,B;
ValInt=Integrala(1,2,100,f);
printf("\n valoarea integralei din f=x*x-1 este %lf",ValInt);
195
Limbajul de programare C
A=-3.14;
B=3.14;
ValInt=Integrala(A,B,100,f1);
printf("\n valoarea integralei din f=sin(x*x) este %lf",ValInt);
ValInt=Integrala(10,100,100,f2);
printf("\n valoarea integralei din f=exp(x) este %lf",ValInt);
getch(); }
196
Limbajul de programare C
6.10.8. Tratarea parametrilor din linia de comand
n linia de comand folosit la apelul execuiei unui program se pot utiliza diferii
parametri. n cazul utilizrii mediului de dezvoltare integrat Borland C, aceti parametri se pot
defini utiliznd submeniul Arguments al meniului Options. Se selecteaz meniul Options
folosind sgeile sau tastnd <ALT>O. Apoi se selecteaz submeniul Arguments cu ajutorul
sgeilor sau tastnd A. n acest moment se afieaz o fereastr i se vor tasta parametrii care
s fie prezeni la lansarea programului. Parametrii se tasteaz unul dup altul, separai prin
blancuri. Dup ultimul parametru se va aciona tasta ENTER.
Aceti parametri pot fi utilizai parametrii argc i argv ai funciei principale.
Parametrul argc este de tip ntreg i indic numrul de parametri din linia de comand
(sau diferii cu ajutorul submeniului Arguments). Parametrul argv este un tablou de pointeri
spre zonele n care sunt pstrai parametrii liniei de comand. Acetia se consider iruri de
caractere. n felul acesta, antetul funciei principale va fi:
main (int argc, char *argv[])
Exemplu:
Considernd c la lansarea programului prog s-au furnizat parametrii:
15 SEPTEMBRIE 2001
n acest caz argc = 4, iar tabloul argv conine pointerii:
- argv[0] - pointeri spre numele programului (calea, numele i extensia .EXE);
- argv[1] - pointeri spre 15;
- argv[2] - pointeri spre SEPTEMBRIE;
- argv[3] - pointeri spre 2001.
Observaii:
1) Lansarea unui program se face cu prima instruciune a funciei principale. Parametrii
argc i argv au deja n acest moment valorile indicate mai sus. Deci ei pot fi analizai chiar
ncepnd cu prima instruciune a funciei principale.
2) n mod frecvent aceti parametri reprezint diferite opiuni ale programului, date
calendaristice, nume de fiiere etc.
3) argv[0] este ntotdeauna pointerul spre numele fiierului cu imaginea executabil a
programului.
Exemple:
1. Programul urmtor afieaz parametrii din linia de comand.
void main(int argc, char *argv[])
/*afiseaza parametrii din linia de comanda */
{ int i;
for (i = 0; i<argc; i++)
printf(%s\n, argv[i]);
197
Limbajul de programare C
}
2. Programul urmtor preia din linia de comand o dat calendaristic compus din trei
parametri: zi, denumire_luna, an, testeaz dac este valid i n caz afirmativ o rescrie sub
forma:
zz/ll/aaaa
unde:
- zz este ziua pe doua cifre;
- ll este numrul lunii pe dou cifre;
- aaaa este anul pe patru cifre.
Aa cum s-a indicat mai sus, parametrii liniei de comand sunt pstrai sub form de
iruri de caractere. De exemplu, data calendaristic 15 septembrie 2001 se pstreaz prin trei
iruri de caractere:
15 septembrie 2001
Ziua i anul vor trebui convertite din iruri de caractere n ntregi binari. n acest scop,
se poate folosi funcia de bibliotec atoi, care are ca parametru pointerul spre irul de caractere
ce reprezint numrul de convertit, iar la revenire returneaz ntregul binar rezultat din
conversie. Prototipul acestei funcii se afl n fiierul stdlib.h.
Denumirea lunii se caut cu ajutorul funciei strcmp, comparndu-se cu elementele
tabloului de pointeri tpdl, iniializat cu denumirile lunilor calendaristice. Funcia strcmp
are prototipul n fiierul string.h. n acest exemplu, argc trebuie s aib valoarea 4, iar
argv are elementele definite n felul urmtor:
- argv[0] - pointeri spre numele programului (calea, numele i extensia
.EXE);
- argv[1] - pointeri spre zi;
- argv[2] - pointeri spre denumirea lunii;
- argv[3] - pointeri spre an.
Programul care realizeaz cele escrise anterior este urmtorul:
#include <stdlib.h>
#include <string.h>
void main(int argc, char*argv [])
{ static int tabzi [] = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
static char *tpdl[] = {
luna ilegala, ianuarie, februarie, martie, aprilie, mai, iunie,
iulie, august, septembrie, octombrie, noiembrie, decembrie };
int zz, ll, aa, i;
if (argc! = 4) printf (numar parametric eronat\n);
else
if(strlen(argv[1])>2) printf(peste 2 cifre la zi\n) ;
198
Limbajul de programare C
else
if(strlen(argv[10])>10)
printf(peste 10 caractere la denumire luna\n);
else
if(strlen(argv[3])>4) printf(peste 4 cifre la an\n);
else { /*1 */
zz = atoi (argv[1]); /* se cauta luna in tpd1 */
for (ll = 1; ll<13; ll++)
if(strcmp (argv[2], tpd1[11])==0) break;
if(ll= = 13) /* nu s/a gasit luna */
printf(denumirea lunii eronata\n);
else { /* 2 */ /* se converteste anul */
aa = atoi (argv[3]);
if(aa<1600) printf(anul eronat\n);
else { /* 3 */
i = tabzi [11] + (ll = = 2&&(aa%4 = = 0&& aa%100!=0 aa%400= =0));
if (zz<1 zz>i) printf(ziua eronata\n);
else /* data calendaristica corecta */
printf(%02d/%02d/%d\n, zz, ll, aa);
} /* sfarsit else 3*/
} /* sfarsit else 2*/
} /* sfarsit else 1*/
} /* sfarsit main*/
n acest program s-a utilizat funcia atoi care realizeaz o conversie a unui ir de
caractere ntr-un ntreg. Funcia se gsete n fiierul antet stdlib.h.
6.10.9. Modificatorul const
Am vzut c o constant se definete prin caracterele care intr n compunerea ei. De
asemenea s-a artat c putem atribui un nume unei constante printr-o construcie #define.
Un astfel de nume se spune c este o constant simbolic i el se substituie prin irul
de caractere care i corespunde n zona de preprocesare.
Un alt mod de a defini o constant este acela de a folosi modificatorul const ntr-o
declaraie. Printr-o astfel de declaraie, unui nume i se poate atribui o valoare constant. n
acest caz, numele respectiv nu mai este tratat de preprocesor i el poate fi folosit n program n
mod analog cu numele variabilelor.
Observaie:
Unui nume declarat cu ajutorul modificatorului const nu i se poate schimba valoarea
printr-o expresiei de atribuire, ca i unei variabile obinuite.
199
Limbajul de programare C
Formatele declaraiei cu modificatorul const sunt urmtoarele:
tip const nume = valoare;
const tip nume = valoare;
tip const nume;
const tip nume;
const nume= valoare;
const nume;
Exemple:
1. const i = 10
n urma creia i devine egal cu 10. O expresie de atribuire de forma i = 10 este eronat.
2. const double pi = 3.14159265;
la care ulterior nu este posibil s schimbm valoarea lui pi, scriind de exemplu pi = 3.14159;
3. char*const s=sir;
n care s este un pointer constant spre zona n care este pstrat irul de caractere. Valoarea lui
s nu poate fi schimbat. Cu toate acestea, coninutul zonei spre care pointeaz s poate fi
schimbat.
Atribuirea s = t; unde t este un pointer spre caractere, nu este acceptat de compilator.
n schimb, *s =1; sau *(s+1) = a; sunt instruciuni corecte i vor modifica primul, respectiv
al doilea caracter al zonei spre care pointeaz s.
4. char const *s=sir;
n care s este un pointer spre o zon constant. n acest caz valoarea lui s poate fi schimbat,
de exemplu s = t;. n schimb, o instruciune de forma s =1; este o eroare, deoarece prin
declaraia de mai sus s-a dat compilatorului indicaia c zona spre care pointeaz s este
constant. Zona respectiv poate fi modificat numai prin intermediul unui alt pointer. De
exemplu, fie char*p; Atunci p = s; i *p=1; sunt corecte.
5. const char *s=sir;
este identic cu declaraia de la exemplul 4.
Programul ilustreaz cteva cazuri de utilizare a modificatorului const n declaraii,
precum i modurile indirecte de modificare a constantelor declarate n acest fel.
#include <string.h>
void main()
{
int const i=10; int const j;
double const x = 1.23456789; double const pi;
const n=12; int const k; const 1;
char *t=xyz; char *const s=sir; char const *s1 = abc;
const s2 = ABCDE; char *p; int *q;
q = &j; *q = 1234;
200
Limbajul de programare C
*(int *) &k=3;
*(int *) &1 = -3;
printf(dimensiunile lui I,j,n,1\n);
printf(%d %d %d %d\n, sizeof(i), sizeof(j), sizeof(n), sizeof(1));
printf(dimensiunile lui x,s,s1,s2\n);
printf(%d %d %d %d\n, sizeof(x), strlen(s), strlen(s1), strlen(s1),
strlen(s2));
printf(valorile lui i,n,s,s1,s2\n);
printf(i = %d n=%d s=%s s1=%s s2=s%\n, i,n,s,s1,s2);
printf(valorile lui j,k,1\n);
printf(j=%d k=%d l=%d\n, j,k,l);
*(double *) &pi=3.14159;
printf(valorile lui x,pi\n);
printf(x=%.10f pi=%.10f\n, x,pi);
/* modificari folosind pointeri */
*(int *)&i = 10000;
q=&l; *q=7;
printf(valorile modificate ale lui i si l\n);
printf(i=%d l=%d\n, i, l);
*(double *) &pi = 3.14159265;
printf(valorea modificata a lui pi\n);
printf(%.10f\n, pi);
*s = 1; *(s+1) = 2;
printf(valorea modificata a zonei spre care pointeaza s\n);
printf(s=%s\n, s);
p = s1; *p=1;
printf(valorea modificata a zonei spre care pointeaza s1\n);
printf(s=%s\n, s1);
}
Rezultatele programului:
dimensiunile lui i,j,n,1
2 2 2 2
dimensiunile lui x,s,s1,s2
8 3 3 5
valorile lui I,n,s,s1,s2
i = 10 n = 12 s = sir s1 = abc s2 = ABCDE
Valorile lui j,k,l
j = 1234 k=3 l=3
201
Limbajul de programare C
Valorile lui x, pi
x = 1.2345678900 pi=3.1415900000
Valorile modificate ale lui I si l
i = 10000 l=7
valoarea modificata a lui pi
3.1415926000
valoarea modificata a zonei spre care pointeaza s
s = 12r
valoarea modificata a zonei spre care pointeaza s1
s1 = 1bc
Observaii:
1) Declaraia const permite protejarea valorii atribuite unui nume fa de eventualele
ncercri ulterioare de a modifica accidental aceste valori prin simple atribuiri.
2) n cazul n care programatorul dorete s fac o astfel de modificare, ea se poate
realiza, dar nu direct, ci doar indirect, folosind pointerii.
De exemplu, dac dorim s modificm valoarea constantei i din exemplu 1 de la 10 la
100, putem realiza acest lucru prin instruciunea urmtoare:
*(int*)&i = 100
Aceasta este posibil deoarece unui nume declarat prin const i se aloc memorie.
Expresia de mai sus este echivalent cu secvena:
int *p;
................
p = &i;
*p = 100;
ntr-adevr, &i reprezint adresa zonei alocate numelui i. Atunci (int *)&i convertete
aceast adres spre un pointer spre ntregi, deci ea are aceeai valoare ca i p dup
instruciunea p = &i; Prin urmare, instruciunea *p = 100; devine echivalent cu
*(int*)&i = 100;
Modificatorul const se folosete frecvent la declaraia parametrilor formali de tip
pointer. O astfel de declaraie are formatul:
Un parametru formal declarat prin construcia:
tip*nume_parametru_formal
corespunde unui parametru efectiv a crui valoarea este o adres.
La apel, valoarea parametrului formal devine egal cu aceast adres. Datorit acestui
fapt funcia apelat poate s modifice data aflat la adresa respectiv.
Modificatorul const utilizat la declararea unui astfel de parametru formal interzice
funciei apelate s modifice data de la adresa recepionat la apel de ctre parametrul formal
corespunztor.
202
Limbajul de programare C
Acest mecanism este utilizat frecvent n cazul funciilor de tratare a irurilor de
caractere.
Exemple:
1. Funcia strlen din biblioteca standard a limbajului C are prototipul:
unsigned strlen(const char*s);
Ea se apeleaz prin expresii de atribuire de forma:
n = strlen(x);
unde x este un pointer spre o zon de memorie n care se afl un ir de caractere.
Funcia strlen determin lungimea irului aflat la adresa recepionat de ctre
parametrul s. Ea nu are voie s modifice irul respectiv i din aceast cauz parametrul s se
declar folosind modificatorul const.
2. Funcia strcpy din biblioteca standard a limbajului C are prototipul:
Char * strcpy (char*dest, const char * sursa);
Aceast funcie copiaz irul aflat la adresa recepionat de parametrul surs n zona
de memorie a crei adres este atribuit parametrului dest. Deoarece funcia strcpy nu are voie
s modifice irul de la adresa surs, la destinaia acestui parametru s-a utilizat modificatorul
const. n schimb, funcia schimb coninutul de la adresa dest, unde se copiaz irul i deci
parametrul dest nu se mai declar prin modificatorul const.
Funcia strcpy returneaz adresa din dest, adic adresa zonei n care s-a copiat irul.
3. Funcia strcat din biblioteca standard a limbajului C are prototipul:
char*strcat(char*a, const char*b);
irul spre care pointeaz b se concateneaz la sfritul irului spre care pointeaz a.
irul spre care pointeaz nu poate fi modificat de ctre funcia strcat i din aceast cauz
parametrul b este declarat cu modificatorul const.
Funcia returneaz pointerul spre irul rezultat, deci chiar valoarea parametrului a.
4. Funcia strcmp din biblioteca standard a limbajului C are prototipul:
int strcmp (const char *a, const char *b);
Funcia compar irurile spre care pointeaz a i b. Ea nu poate modifica cele dou
iruri i de aceea cei doi parametri au fost declarai prin modificatorul const.
6.10.10. Stiva, exemplu de structur ce folosete pointeri
Prin stiv se nelege o mulime ordonat de elemente la care accesul se realizeaz
conform principiului LIFO (Last In First Out).
Cel mai simplu procedeu de implementare a unei stive este pstrarea elementelor ei
ntr-un tablou unidimensional.
n zona de memorie afectat stivei se pot pstra elementele ei unul dup altul. De
asemenea ele se pot scoate din aceast zon n ordine invers pstrrii lor. Astfel, la un
moment dat, se scoate ultimul element pus n stiv i numai acesta.
203
Limbajul de programare C
Despre ultimul element pus pe stiv se spune c este n vrful stivei, despre primul c
este la baza stivei.
Vrful stivei se modific de fiecare dat cnd punem un element pe stiv n aa fel
nct lungimea prii ocupate a stivei s creasc.
Elementul care se scoate din stiv este elementul aflat n vrful stivei. Prin aceasta,
partea ocupat din stiv se micoreaz. Cu alte cuvinte accesul este permis doar la vrful
stivei:
- un element se poate pune pe stiv numai dup elementul aflat n vrful stivei i dup
aceast operaie el ajunge n vrful stivei;
- se poate scoate de pe stiv numai elementul aflat n vrful stivei i dup aceast
operaie n vrful stivei rmne elementul care a fost pus pe stiv naintea lui.
n felul acesta, se observ c elementele unei stive sunt gestionate dup principiul
numit FIFO: ultimul element pus pe stiv este primul element care se scoate din stiv.
O astfel de gestiune se obine simplu dac reorganizm zona de memorie afectat stivei
ca un tablou unidimensional i n plus se definesc dou funcii: una care pune un element pe
stiv i alta care scoate un element din stiv.
Denumirile utilizate mai jos pentru aceste funcii sunt denumiri deja consacrate n
literatura de specialitate. Astfel, vom numi:
- push funcia care pune un element pe stiv;
- pop funcia care scoate un element din stiv.
De asemenea, vom denumi stack tabloul utilizat n implementarea stivei i next
variabila care indic prima poziie liber din stiv.
Deci stack va fi tabloul unidimensional, pe care l vom aloca static n momentul de fa
i l vom considera de tip ntreg. Aceasta nseamn c elementele pstrate n stiv sunt numere
ntregi (de tip int). Evident, se pot considera stive care s pstreze i date de alte tipuri.
Rezult c stack[0] este elementul de la baza stivei. Dac ultimul element pus pe stiv
este stack[n], atunci acesta se afl n vrful stivei. Variabila next definete prima poziie liber
din stiv, deci ea indic elementul stack[n+1].
Principalele funcii de lucru cu stiva implementate n limbajul C sunt:
- clear funcia de iniializare a stivei. Dup apelul ei stiva devine vid;
- top permite acces la elementul din vrful stivei, la fel ca pop, fr a
elimina ns acest element;
- empty returneaz o valoare diferit de zero (adevrat) dac stiva
este
vid i zero n caz contrar;
- full returneaz o valoare diferit de zero dac stiva este plin i zero
n caz contrar.
204
Limbajul de programare C
Exemplul 1: Se vor defini funciile push, pop i clear pentru o stiv de tip nreg de
maxim 1000 de elemente.
# define MAX 1000
static int stack[MAX]
static next = 0; /* indicele pentru baza
stivei */
void push(x) /* pune pe stiva valoarea
lui x */
int x;
{ if (next<MAX) stack [next++] = x;
else printf(stiva este depasita\n); }
int pop() /*scoate elementul din varful stivei si returneaza valoarea
lui */
{ if (next>0) return stack [--next];
else { printf (stiva este vida\n); return 0; }
}
void clear() /* videaza
stiva */
{ next = 0;
}
Exemplul 2: S se rescrise funciile push, pop i clear folosind pointeri n locul
variabilelor cu indici, considernd un numr maxim de 1000 de elemente.
# define MAX 1000
static int stack[MAX]
static int *next = stack;
/* pointerul next se initializeaza cu adresa de indice a zonei afectate stivei */
void push(int x) /* pune pe stiva valoarea
lui x */
{ if (next<stack+MAX) *next++ = x;
else printf(stiva este depasita\n); }
int pop() /*scoate elementul din varful stivei si returneaza valoarea
lui */
{ if (next>stack) return (*--next);
else { printf (stiva este vida\n); return 0;}
}
void clear() /* videaza
stiva */
{ next = stack; }
205
Limbajul de programare C
6.11. Structuri i tipuri definite de utilizator
n anumite situaii practice se lucreaz cu seturi mari de date. Aceste date pot s fie
toate de acelai tip sau s fie de tipuri diferite. Dac datele sunt de acelai tip ele se pot grupa
n structuri de tip tablou. Dac sunt de tip diferit, spunem despre grupa respectiv de date c
formeaz o structur.
6.11.1. Declaraia de structur
O structur se poate declara n felul urmtor:
struct nume { list de_declaratii } nume
1
, nume
2
, , nume
n
;
unde nume
1
, nume
2
, , nume
n
sunt nume care pot i lipsi, dar nu toate deodat.
Exemple:
1. Structurile de tip dat calendaristic se pot declara n felul urmtor:
struct data_calend { int zi;
char luna [11];
int an; }
data_nasterii, data_angajarii;
Prin aceast declaraie s-a introdus tipul struturat data_calend, iar data_nasterii i
data_angajarii sunt structuri de tipul data_calend.
O declaraie de forma:
struct nume { lista_de_declaratii};
introduce un tip nou de date (un tip structurat). Acest tip are numele nume i poate fi folosit
n continuare pentru a declara date, singura diferen fa de tipurile predefinite fiind aceea c
este precedat de cuvntul rezervat struct.
Prin declaraia
struct nume
1
, nume
2
, , nume
n
;
se declar nume
1
, nume
2
, , nume
n
ca structuri de tipul nume.
Observaii:
1. Printr-o declaraie de forma:
struct nume { lista_de_declaratii
};
lui nume nu i se aloc memorie. O astfel de declaraie o vom numi n continuare declaraia de
tip.
2. O declaraie de forma:
struct nume nume
1
, nume
2
, , nume
n
;
este tratat de compilator ca i declaraiile obinuite. Compilatorul aloc memorie structurilor
nume
1
, nume
2
, , nume
n
conform tipului nume.
Considerm un model simplificat ce pstreaz datele personale:
206
Limbajul de programare C
- nume_prenume;
- adresa;
- data_nasterii;
- data_angajarii.
Prin declaraia de mai jos, introducem tipul data_pers:
struct data_pers { char nume_prenume [50];
char adresa [50];
struct data_calend
data_nasterii, data_angajarii;
};
6.11.2. Accesul la elementele unei structuri
Accesul la elementele unei structuri se realizeaz printr-o construcie de forma:
nume.nume_cmp
unde: - nume este numele datei structurate;
- nume_cmp este numele componentei pe care vrem s o referim.
Punctul, utilizat n construcia de mai sus este un operator de prioritate maxim. El se
afl n tabela de prioriti n aceeai linie cu parantezele.
Fie declaraiile:
struct data_calend tdc[100], data;
n acest caz, tdc este un tablou ale crui element sunt structuri de tipul data_calend. Ne
putem referi la ziua corespunztoare elementului de indice i prin construcia tdc[i].zi. n
schimb, pentru variabila data referirea se face cu construcia data.zi.
6.11.3. Atribuirea unor nume pentru tipuri de date
Tipurile de baz ale limbajului C, numite i tipuri predefinite, se identific printr-un
cuvnt cheie (int, char, float etc.). Tipurile structurate se definesc printr-o declaraie de forma:
struct nume {};
Programatorul poate s atribuie un nume unui tip, indiferent de faptul c acesta este un
tip predefinit sau definit de utilizator. Aceasta se realizeaz prin intermediul construciei
typedef. Ea are urmtorul format:
typedef tip nume_tip;
unde:
- tip este fie numele unui tip predefinit, fie o declaraie de tip structurat;
- nume_tip este numele atribuit tipului respectiv.
Dup ce s-a atribuit un nume unui tip, numele respectiv poate fi utilizat pentru a
declara date de acel tip, exact la fel cum se utilizeaz n declaraii cuvintele cheie int, char,
float etc.
207
Limbajul de programare C
Observaie:
De obicei, numele asignat unui tip se scrie cu litere mari. Un exemplu de astfel de
nume exist n fiierul stdio.h, pentru tipul fiier, cruia i s-a atribuit numele FILE.
Exemple:
1. Fie declaraiile:
typedef int INTREG;
typedef float REAL;
n continuare, denumirile INTREG i REAL se pot folosi la fel ca i cuvintele cheie
int i float. Cu alte cuvinte, declaraia:
INTREG x,y,a [100];
este identic cu declaraia: int x,y,a [100];
n mod analog:
REAL p,q,r;
este identic cu declaraia: float p,q,r;
2. typedef struct dat_calend { int zi;
char luna [11];
int an;
} DC;
Prin aceast declaraie se atribuie denumirea DC tipului structurat data_calend. n
continuare putem declara date de tip DC:
DC data_angajarii, data_nasterii;
DC data_crt = {20, septembrie, 1991};
3. Programul urmtor citete numere complexe aflate n fiierul standard stdin i le
rescrie mpreun cu modulul lor. Un numr complex se introduce printr-o pereche de
numere flotante, primul reprezentnd partea real, iar cel de al doilea partea imaginar.
typedef struct { double real;
double imaginar;
} COMPLEX;
double modul (COMPLEX *);
void main ()
/*citeste numere complexe si le rescrie impreuna cu modulul lor*/
{ COMPLEX z;
while (scanf(%lf %lf, &z.real, &z.imaginar)= = 2)
printf (%lf %lf, modul = %lf\n,z.real, z.imaginar, modul(&z)); }
#include <math.h>
double modul (COMPLEX *x)
/*returneaza modulul numarului complex spre care pointeaza x*/
{ return sqrt (x->real* x->real+x->imaginar* x->imaginar); }
208
Limbajul de programare C
Pentru a exemplifica cele prezentate anterior, vom prezenta un program ce permite
operarea cu datele personale ale unui student: numr matricol, nume, data naterii, media sa
general din anul anterior, adresa sa. Vom dori s scriem un program care determin ce vrst
are studentul i care afieaz datele personale. Pentru aceasta vom defini un tip nou de date
numit Student, care s pstreze datele unui singur student. Data naterii, va fi declarat i ea
ca o alt structur existent n structura iniial, lucru care este permis. Pentru a determina
vrsta, vom afla data sistemului (folosind struct date, o structur a sistemului de tip dat
calendaristic i funcia getdate ). Programul este prezentat n continuare:
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <dos.h>
typedef struct stud{
unsigned nr_mat;
char nume[20], adr[30];
struct { unsigned zi,luna,an; } dn;
float med_gen;
} Student;
void main()
{ int Varsta;
Student S;
struct date d;
clrscr();
printf("\n Dati numarul matricol ");
scanf("%u", &S.nr_mat);
printf("\n Dati numele ");
scanf("%s", S.nume);
printf("\n Dati adresa ");
scanf("%s", S.adr);
printf("\n Dati data nasterii in forma zz:ll:aaaa: ");
scanf("%2d:%2d:%4d",&S.dn.zi,&S.dn.luna,&S.dn.an);
printf("\n Dati media generala ");
scanf("%f", &S.med_gen);
clrscr();
printf("Studentul %s este nascut in %2d:%2d:%4d, locuieste in %s si are
media %5.2f", S.nume,S.dn.zi,S.dn.luna,S.dn.an,S.adr,S.med_gen);
getdate(&d); //se afla data sistemului
Varsta=d.da_year-S.dn.an;
209
Limbajul de programare C

if (d.da_mon <S.dn.luna) //daca nu s-a implinit o luna
Varsta--;
if ((d.da_mon ==S.dn.luna) && (d.da_day<S.dn.zi)) //daca nu s-a implinit
ziua
Varsta--;
printf("\n Astazi suntem in %2d:%2d:%4d iar varsta este %d\n", d.da_day,
d.da_mon,d.da_year,Varsta);
getch();
}
Un alt exemplu l constituie programul urmtor care permite citirea de la tastatur a
datelor despre cei n candidai la examenul de admitere la facultate. Se presupune c admiterea
n facultate se face doar dup media obinut de candidat la bacalaureat i c la facultatea
respectiv exist trei profile la care candidatul se poate nscrie. Un candidat are dreptul s
specifice o ordine de preferin a celor trei profile (notate n program cu a, b i c).
Cunoscndu-se numrul de candidai, n, numrul de locuri disponibil la profilele a na, b
nb i la profilul c nc , precum i datele despre cei n candidai (nume, prenume i media la
bacalaureat) se cere programul C care s efectueze repartiia candidailor pe opiuni i
afiarea candidailor admii i a celor respini.
#include<stdio.h>
#include<conio.h>
typedef struct
{ char nume[20],pren[20];
float med;
char opt[3];
char repartizat;
} Candidat;
Candidat a[100];
int n;
int na,nb,nc;
void citire_date()
{ printf("\nNumar candidati:");
scanf("%d",&n);
printf("\nNumar locuri la profilul a:");
scanf("%d",&na);
printf("\nNumar locuri la profilul b");
scanf("%d",&nb);
printf("\nNumar locuri la profilul c");
210
Limbajul de programare C
scanf("%d",&nc);

float x;
int i,j;
for (i=0;i<n;i++)
{ printf("\nNumele:");
scanf("%s",a[i].nume);
printf("\nPrenumele:");
scanf("%s",a[i].pren);
printf("\nMedia :");
scanf("%f",&x);
a[i].med=x;
for (j=0;j<3;j++)
{ printf("\nOptiunea %d ",j+1);
fflush(stdin);
scanf("%c",&a[i].opt[j]); }
a[i].repartizat=' ';
}
}
void ordonare_medii()
{ int i,gata;
Candidat temp;
do
{ gata=1;
for (i=0;i<n-1;i++)
if (a[i].med<a[i+1].med)
{ temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
gata=0; }
}
while (gata==0);
}
void afisare_candidati()
{ int i;
printf("\nLista candidatilor este\n");
printf("\n*************************************");
for (i=0;i<n;i++)
211
Limbajul de programare C
printf("\n%20s %20s %.2f",a[i].nume,a[i].pren,a[i].med);
}
void repartizare_optiuni()
{ int i,j;
for (i=0;i<n;i++)
for (j=0;j<3;j++)
if (a[i].repartizat==' ')
switch (a[i].opt[j])
{ case 'a':if (na>0)
{ a[i].repartizat='a';
na--; }
break;
case 'b':if (nb>0)
{ a[i].repartizat='b';
nb--; }
break;
case 'c':if (nc>0)
{ a[i].repartizat='c';
nc--; }
break
}
}

void afisare_rezultate()
{ int i,j;
printf("\nLista cu candidatii admisi este ");
printf("\n**********************************");
for (i=0;i<n;i++)
if (a[i].repartizat!=' ')
printf("\n%20s %20s media %.2f profilul %c",a[i].nume,a[i].pren,a[i].med,
a[i].repartizat);
printf("\nLista cu candidatii respinsi este ");
printf("\n**********************************");
for (i=0;i<n;i++)
if (a[i].repartizat==' ')
printf("\n%20s %20s media %.2f ",a[i].nume,a[i].pren,a[i].med);
}

void main()
212
Limbajul de programare C
{ citire_date();
afisare_candidati();

ordonare_medii();
repartizare_optiuni();
afisare_rezultate();
getch();
}
6.11.4. Uniuni
Un caz special de structuri l constituie uniunile. Acestea sunt structuri alternative
pentru care dimensiunea spaiului necesar memorrii lor este egal cu cea mai mare
dimensiune necesar memorrii unei componente a acelei structuri. Astfel, toate componentele
uniunii ocup aceeai zon n cadrul memoriei. Tocmai de aceea, la un moment dat, spre
deosebire de structuri, este accesibil o singur component a unei uniuni. Uniunile sunt utile
pentru situaii n care se folosesc simultan variabilele, obinnd o economie de memorie.
Uniunile se definesc n aceeasi manier ca i structurile, cuvntul cheie utilizat fiind union.
union un_tip {
int uint;
float ufloat;
char uchar;
} o_variabila;
Dac, la un moment dat, variabila o_variabila are ncrcat elemental uint, presupunem
cu valoarea 15897, atunci accesul ctre celelalte dou componente este dac nu ilegal, cel
puin lipsit de sens. Aceasta deoarece n spaiul rezervat n memorie variabilei respective se
afl un numr ntreg i deci nu are sens s cutm un caracter sau un numr real.
Exemplu:
Programul urmtor calculeaz ariile pentru urmtoarele figuri geometrice: cerc, ptrat,
dreptunghi i triunghi. Datele programului, introduse de la tastatur vor fi de forma:
C , P, D, T un caracter care indic tipul figurii, Raza pentru cerc, latura pentru
patrat, cele doua laturi pentru dreptunghi, respectiv cele trei laturi pentru triunghi.
n cazul triunghiului se utilizeaz formula lui Heron pentru calculul ariei:
( ) ( ) ( ) c p b p a p p aria
unde:
a,b,c sunt laturile triunghiului;
2
c b a
p
+ +
, este semiperimetrul triunghiului,
condiia de existen a triunghiului fiind: p>a, p>b i p>c.
Programul este prezentat n continuare:
213
Limbajul de programare C
#include<stdio.h>
#include<math.h>
#include<conio.h>
#define Pi 3.1415927
#define Cerc 1
#define Patrat 2
#define Dreptunghi 3
#define Triunghi 4
typedef struct { int tip_figura;
union{ float raza;
float latura;
float lat_drept[2];
float lat_tr[3]; } figura;
} FIGURA;
void main()
{ float aria,p;
char tip;
int i;
FIGURA fig; //se citeste caracterul care defineste tipul
figurii
printf("\nIntroduceti tipul figurii:");
scanf("%c",&tip);
switch (tip) { case 'C': //cerc
printf("\nIntroduceti raza cercului:");
scanf("%f",&fig.figura.raza);
fig.tip_figura=Cerc;
break;
case 'P': //patrat
printf("\nIntroduceti latura patratului");
scanf("%f",&fig.figura.latura);
fig.tip_figura=Patrat;
break;
case 'D': //dreptunghi
printf("\nIntroduceti laturile dreptunghiului");
scanf("%f%f",&fig.figura.lat_drept[0],
&fig.figura.lat_drept[1]);
fig.tip_figura=Dreptunghi;
break;
214
Limbajul de programare C
case 'T':
//triunghi
printf("\nIntroduceti laturile triunghiului");
for (i=0;i<3;i++)
scanf("%f",&fig.figura.lat_tr[i]);
fig.tip_figura=Triunghi;
break;
default: printf("\nEroare");
break;
}
switch (fig.tip_figura)
{ case Cerc: printf("\nAria cercului este %f",fig.figura.raza*fig.figura.raza*Pi );
break;
case Patrat:printf("\nAria patratului este %f"
,fig.figura.latura*fig.figura.latura);
break;
case Dreptunghi: printf("\nAria dreptunghiului este %f",
fig.figura.lat_drept[0]*fig.figura.lat_drept[1]);
break;
case Triunghi: for(p=0,i=0;i<3;i++)p+=fig.figura.lat_tr[i];
p=p/2;
for(i=0;i<3;i++)
if (p<=fig.figura.lat_tr[i])
{printf("\nValorile nu pot fi laturile unui triunghi");
return; }
for (aria=1,i=0;i<3;i++) aria*=(p-
fig.figura.lat_tr[i]);
aria=sqrt(p*aria);
printf("\nAria triunghiului este %f",aria);
break;
}
}
6.11.5. Cmpuri de bii
Limbajul C permite utilizatorului definirea i prelucrarea datelor pe bii. Utilizarea
datelor pe bii are legtur cu folosirea indicatorilor. Prin indicator se nelege o dat care
poate avea doar dou valori, 0 sau 1. O astfel de valoare se poate pstra n memorie i pe un
215
Limbajul de programare C
singur bit. n acest scop limbajul C ofer posibilitatea de a declara date care s se aloce n
memorie pe bii.
Se nelege prin cmp un ir de bii adiaceni coninui ntr-un cuvnt de memorie (un
cuvnt de memorie este de obicei, o locaie de memorie de doi octei). Un cmp se declar ca
i componentele unei structuri avnd unul dintre tipurile: unsigned, int, unsigned char sau
signed char, indicndu-se n cadrul declaraiei i dimensiunea lui n numr de bii.
O structur care are n componena sa cmpuri are forma:
struct {cmp1;
cmp2;
. . .
cmpn;
} nume;
unde cmp1, cmp2, ..., cmpn are unul din formatele:
unsigned nume: lungime_n_bii;
sau :lungime_n_bii;
Exemplu:
struct { unsigned x:1;
unsigned y: 2;
unsigned z: 3;
} indicatori;
Cmpurile din cadrul unei structuri pot fi referite ca i celelalte componente ale
structurilor, ca n exemplul urmtor:
indicatori.x
indicatori.y
indicatori.z
Alocarea biilor n memorie depinde de sistemul de calcul. n cazul limbajului
Borland C, biii se aloc de la dreapta spre stnga (de la cei mai puini semnificativi spre cei
mai semnificativi), ca n figura 6.7.
Observaii:
216
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
x
y
z
Fig. 6.7. Modul de alocare n memorie a cmpurilor de bii.
Limbajul de programare C
1. Dac un cmp nu se poate aloca ntr-un cuvnt de memorie, el se aloc n cuvntul
urmtor n ntregime;
2. Un cmp nu poate avea dimensiunea mai mare de 16 bii;
3. Cel de-al doilea format de declarare a unui cmp (n care nu se specific numele
acestuia) se utilizeaz pentru cadraje, atunci cnd sunt zone de bii neutilizate n cadrul unui
cuvnt;
4. O structur care are n componena sa cmpuri poate avea i componente obinuite;
5. Nu se pot defini tablouri de cmpuri;
6. Unui cmp nu i se poate aplica operatorul adres (& ca operator unar) ;
7. Utilizarea cmpurilor poate avea ca efect obinerea unui program cu o portabilitate
redus.
6.11.6. Enumerri
Tipurile enumerare sunt introduse prin sintaxa:
enum nume {membrul,membru2,...} varl,var2,...;
De exemplu:
enum CULORI {ROSU,VERDE,ALBASTRU}
culoare_punct,culoare_linie;
enum CULORI culoare_cerc,culoare_fond;
definete tipul de date CULORI i declar variabilele culoare_punct i culoare_linie, urmate
de declarrile a nc dou variabile, culoare_cerc i culoare_fond.
Membrii unui tip enumerat sunt numai de tip ntreg. Valoarea fiecruia este obinut
prin incrementarea cu 1 a valorii membrului anterior, primul membru avnd, implicit, valoarea
0. Este permis iniializarea unui membru cu o valoare oarecare, avndu-se n vedere c doi
membri ai aceluiai tip nu pot avea aceeai valoare. Valorile membrilor urmtori se vor stabili
conform regulilor menionate.
enum ANOTIMP {IARNA=1, PRIMAVARA, VARA, TOAMNA};
enum BOOLEAN (fals,adevarat} conditie;
enum DIRECTIE {UP,DOWN,RIGHT,LEFT,NONE=0}; // ilegal
n ceea ce privete utilizarea variabilelor de tip enumerat limbajul C permite atribuiri
de tipul:
conditie=0;
dar acest tip de atribuiri genereaz avertismente din partea compilatorului. De aceea este bine
ca astfel de atribuiri s fie nsoite de conversia de tip corespunztoare.
conditie=fals;
conditie=(enum BOOLEAN) 0;
6.11.7. Pointeri spre structuri
217
Limbajul de programare C
Pointerii sunt adrese de variabile de orice tip. Prin urmare, un pointer poate fi declarat
ca un pointer spre tipul structur.
Un pointer ctre o structur se declar similar cu pointerii ctre variabile simple.
Dac TipStruct este un tip de date de tip structur definit explicit cu ajutorul construciei
typedef, declararea unui pointer spre structura aceea se face astfel:
TipStruct *P;
De asemenea, dac tipul de date structur nu se definete explicit cu typedef, se poate
folosi definiia cu struct :
struct Tipt*p;
S considerm un exemplu de structur:
typedef struct {
unsigned nr_mat;
char nume[20], adr[30];
struct { unsigned zi,luna,an; } dn;
float med_gen;
} Student;
Declararea unui pointer spre tipul Student se va face astfel :
Student *p;
n general, dac p este un pointer spre o structur atunci *p este structura respectiv,
iar (*p).nume_mebru este un membru obinuit al structurii. Pentru exemplul anterior avem
(*p).nume sau (*p).med_gen sau (*p).dn.zi .
Limbajul C permite o notaie echivalent pentru aceste construcii: dac p este un
pointer ctre o structur, iar m este un membru al acestei structuri, atunci (*p).m este
echivalent cu notaia p ->m (simbolul ->este format din caracterul - urmat de semnul >) .
Toate problemele referitoare la pointeri ctre variabile simple rmn valabile i la
pointeri ctre structuri. De exemplu, va trebui s rezervm loc pentru structura ce va fi
referit cu ajutorul unui pointer spre structuri.
Vom prezenta, mai departe, un exemplu de lucru cu pointeri spre structuri. Vom
considera urmtoarele date despre cele n cri ale bibliotecii facultii: numrul de inventar,
titlul, autorul i anul intrrii n bibliotec. Vom folosi tablou de pointeri spre structuri (adic
mai muli pointeri spre structuri) pentru stocarea datelor. Se vor afia datele despre cri,
sortate alfabetic dup titlu, se vor numra crilor unui autor dat, se vor afia crile intrate n
bibliotec ntr-un an dat sau un mesaj corespunztor dac nu sunt cri intrate n anul respectiv.
Sursa programului este prezentat n continuare.
#include <conio.h>
#include <stdio.h>
#include <alloc.h>
#include <string.h>
218
Limbajul de programare C
typedef struct carte{ unsigned long nr_inv;
char titlu[30], autor[20];
unsigned int an;
}Carte;
int N;
Carte *C[20]; // se declar 20 de pointeri spre structura carte
void Citire (void)
{ int I;
printf("\n Dati numarul de carti");
scanf("%d",&N);
for (I=0; I<N; I++)
C[I]=(Carte *)malloc(N * sizeof(Carte));//se aloca memorie pentru datele celor N carti
for (I=0; I<N ; I++)
{ printf("\n Dati numarul de inventar pentru cartea %d",I+1);
scanf("%d", &C[I]->nr_inv);
printf("\n Dati titlul pentru cartea %d",I+1);
scanf("%s", C[I]->titlu);
printf("\n Dati autorul pentru cartea %d",I+1);
scanf("%s", C[I]->autor);
printf("\n Dati anul intrarii in biblioteca pentru cartea %d",I+1);
scanf("%d", &C[I]->an); }
}
void Afisare_ordonate(Carte *C[20], int N)
{ int I, Ok;
Carte *Aux;
Aux=(Carte *)malloc(sizeof(Carte));
do
{ Ok=0;
for (I=0; I<N-1; I++)
if (strcmp(C[I]->titlu,C[I+1]->titlu)>0)
{ Aux=C[I];
C[I]=C[I+1];
C[I+1]=Aux;
Ok=1; }
}
while (Ok);
printf("\n Datele celor N carti sunt:");
printf("\n Nr_inv Titlu Autor An intrare");
219
Limbajul de programare C
for (I=0; I<N ; I++)
{ printf("\n%5d", C[I]->nr_inv);
printf(" %30s", C[I]->titlu);
printf(" %20s", C[I]->autor);
printf("%5d", C[I]->an); }
printf("\n"); }
int Numar (Carte *C[20],int N,char *Autor)
{ int I, Nr;
Nr=0;
for (I=0; I<N ; I++)
if (strcmp(Autor,C[I]->autor)==0) Nr++;
return Nr; }
void Afisare(Carte *C[20],int N,int An)
{ int I,k=0;
for (I=0; I<N ; I++)
if (C[I]->an==An)
{ printf("\n %s %s", C[I]->autor, C[I]->titlu);
k=1; }
if (k==0) printf("\n Nu exista carti intrate in anul %d", An); }
void main()
{ int I, optiune, An;
char *Autor;
Autor=(char *)malloc(20);
Citire();
do
{ printf("\n Dati 1- afisare carti ordonate dupa titlu");
printf("\n dati 2- numararea cartilor unui autor dat");
printf("\n dati 3- afisarea cartilor intrate intr-un an dat");
printf("\n dati 4- revenire in program");
scanf("%d", &optiune);
switch (optiune)
{ case 1: Afisare_ordonate(C , N);break;
case 2:{ printf("\n Dati autorul ");
scanf("%s", Autor);
printf("\n Numarul cartilor autorului %s este %d",Autor, Numar(C,N, Autor));
break; }
case 3: { printf("\n Dati anul ");
scanf("%d", &An);
220
Limbajul de programare C
Afisare(C,N, An);
break; }
}
}
while (optiune != 4);
for (I=0;I<N;I++) free(C[I]); // se elibereaza memoria folosit la stocarea crilor
}
6.12. Fiiere
Limbajul C nu are instruciuni de intrare/ieire. Aceste operaii se realizeaz prin
intermediul unor funcii din biblioteca standard a limbajului C. Aceste funcii pot fi aplicate n
mod eficient la o gam de aplicaii datorit multiplelor faciliti pe care ele le ofer. Ele
asigur o portabilitate bun a programelor, fiind implementate ntr-o form compatibil sub
toate sistemele de operare. Aceasta nu nseamn c ele nu au faciliti specifice pe anumite
sisteme, cum este de exemplu cazul limbajului Borland C.
n acest capitol se vor face referiri la funciile utilizate mai frecvent n operaiile de
intrare/ieire. Aceste operaii presupun c datele sunt organizate n fiiere.
n general, prin fiier nelegem o colecie ordonat de elemente numite nregistrri,
care sunt pstrate pe diferite suporturi externe (Dintre cele mai utilizate amintim
suporturile magnetice). Acestea, de obicei, sunt discuri i benzi magnetice. Ele se numesc
suporturi reutilizabile, deoarece zona utilizat pentru a pstra nregistrrile unui fiier
poate fi ulterior reutilizat pentru a pstra nregistrrile altui fiier.
Datele introduse de la un terminal se consider c formeaz un fiier de intrare.
nregistrarea n acest caz, de obicei este format din datele tastate la terminal pe un rnd, deci
caracterul de rnd nou (newline) este terminator de nregistrare. n mod analog, datele care se
afieaz pe terminal formeaz un fiier de ieire. nregistrarea, i n acest caz, poate fi format
din caracterele unui rnd.
Un fiier are o nregistrare care marcheaz sfritul de fiier. n cazul fiierelor de
intrare de la tastatur sfritul de fiier se genereaz prin Ctrl+Z. El poate fi pus n eviden
folosind constanta simbolic EOF definit n fiierul stdio.h.
Prelucrarea fiierelor implic un numr de operaii specifice acestora. Orice fiier,
nainte de a fi prelucrat, trebuie s fie deschis. De asemenea, la terminarea prelucrrii unui
fiier, acesta trebuie nchis.
Operaiile de deschidere i nchidere a unui fiier se pot realiza prin intermediul unor
funcii speciale din biblioteca standard a limbajului C. Alte operaii frecvente n prelucrarea
fiierelor sunt:
- crearea unui fiier; -consultarea unui fiier;
- actualizarea unui fiier; - adugarea de nregistrri ntr-un
fiier;
221
Limbajul de programare C
- poziionarea ntr-un fiier; - tergerea unui fiier.
Ca i operaiile de deschidere i nchidere de fiiere, operaiile indicate mai sus pot fi
realizate printr-un set de funcii aflate n biblioteca standard a limbajului C.
Prelucrarea fiierelor se poate face la dou niveluri. Primul nivel face apel direct la
sistemul de operare. Acesta este nivelul inferior de prelucrare a fiierelor. Cellalt nivel se
realizeaz prin utilizarea unor proceduri specializate n prelucrarea fiierelor care, printre
altele, pot rezerva i gestiona automat zone tampon necesare realizrii operaiilor de
intrare/ieire. Acesta este nivelul superior de prelucrare a fiierelor.
6.12.1. Nivelul inferior de prelucrare a fiierelor
6.12.1.1. Deschiderea unui fiier
Orice fiier nainte de a fi prelucrat trebuie deschis. Deschiderea unui fiier existent se
realizeaz prin intermediul funciei open. La revenirea din ea se returneaz aa numitul
descriptor de fiier. Aceasta este un numr ntreg. El identific n continuare fiierul respectiv
n toate operaiile realizate asupra lui.
n forma cea mai simpl, funcia open se apeleaz printr-o expresie de atribuire.
Prototipul acestei funcii este:
int open (const char*cale, int acces);
unde cale este pointer spre un ir de caractere care definete calea spre fiierul care se
deschide iar acces o variabil de tip ntreg care poate lua una din valorile urmtoare:
O_RDNLY fiierul se deschide numai n citire (consultare);
O_WRONLY fiierul se deschide numai n scriere (creare);
O_RDWR fiierul se deschide n citire/scriere;
O_APPEND fiierul se deschide la sfrit pentru adugare de nregistrri;
O_BINARY fiierul se prelucreaz binary;
O_TEXT fiierul este de tip text.
Aceste valori se pot combina cu ajutorul caracterului |, de exemplu:
O_RDW|O_BINARY fiierul este deschis n citire/scriere binar.
n mod implicit se consider c fiierul este de tip text.
Utilizarea funciei open presupune c n prealabil s-au inclus fiierele io.h i fcnt1.h
astfel:
#include <io.h>
#include <fcnt1.h>
Calea spre un fiier trebuie s respecte conveniile sistemului de operare MS-DOS. n
cea mai simpl form ea este un ir de caractere care definesc numele fiierului, urmat de
punct i extensia fiierului. Aceasta presupune c fiierul respectiv se afl n directorul curent.
222
Limbajul de programare C
n cazul n care fiierul nu este n directorul curent, numele este precedat de o
construcie de forma litera: \nume_1\\nume_k\ unde litera definete discul (n general A, B
pentru disc flexibil i C, D pentru disc fix) iar nume_i este nume de subdirector (i=1,2,,k).
Observaie:
Deoarece calea se include ntre ghilimele, caracterul \ trebuie dublat.
Deschiderea unui fiier nu reuete n cazul n care unul din parametric este eronat. n
acest caz funcia open returneaz valoarea 1.
De exemplu, dac se ncearc nchiderea unui fiier inexistent, funcia open va returna
valoarea -1.
Exemple:
1) char nfis[ ] = fis1.dat;
int df;

df = open(nfis, O_RDONLY);

Prin apelul de mai sus se deschide n citire fiierul fis1.dat din directorul curent.
Descriptorul de fiier returnat de funcia open se atribuie variabilei df.
2) char fis = A:\\JOC\\BIO.C;
int d;
...
d = open (fis, O_RDWR);
...
Prin acest apel se deschide n citire/scriere fiierul BIO.C din directorul JOC aflat pe
discul flexibil ncrcat pe unitatea A.
3) int d;
d = open (c:\\tc\\include\\text.h, O_APPEND)
Se deschide n adugare fiierul text.h din subdirectorul include al directorului tc de pe
discul C.
Observaie:
La compilare irul de caractere utilizat ca parametru se va pstra ntr-o zon de date i
el este nlocuit prin adresa zonei n care s-a memorat irul respectiv.
Pentru a crea un fiier nou se va folosi funcia creat n locul funciei open. Ea are
prototipul:
int crea (const char*cale, int mod);
unde cale este un parametru care se definete ca i n cazul funciei open iar mod este un
ntreg care poate fi definit prin constantele simbolice de mai jos:
S_IREAD proprietarul poate citi fiierul;
S_IWRITE proprietarul poate scrie n fiier;
223
Limbajul de programare C
S_IEXEC proprietarul poate executa programul coninut n fiier.
Aceti indicatori pot fi combinai folosind caracterul |. De exemplu, pentru
citire/scriere se va folosi:
S_IREAD|S_IWRITE proprietarul poate citi/scrie fiierul.
Observaii:
1) Funcia creat returneaz descriptorul de fiier sau 1 n caz de eroare.
2) Utilizarea acestei funcii implic includerea fiierelor io.h i stat.h.
3) Funcia creat poate fi utilizat i pentru a deschide un fiier existent. n acest caz
vechiul fiier se terge i n locul lui se va crea unul nou cu acelai nume.
6.12.1.2. Citirea dintr-un fiier (consultare)
Operaia de citire a unei nregistrri dintr-un fiier deschis n prealabil cu funcia open
se realizeaz cu ajutorul funciei read. Ea returneaz numrul octeilor citii din fiier.
Prototipul acestei funcii este
int read(int df, void*buf, unsigned lung);
unde:
- df este descriptorul de fiier a crui valoare a fost definit la apelul funciei open
pentru fiierul respectiv;
- buf este pointerul spre zona de memorie n care se recepioneaz nregistrarea care se
citete;
- lung este lungimea n octei a nregistrrii citite.
La eroare, funcia read returneaz valoarea 1. La fiecare apel al funciei read se
citete nregistrarea curent. Astfel, la primul apel se citete prima nregistrare din fiier, la al
doilea apel a doua nregistrare i aa mai departe. Ordinea nregistrrilor este cea definit la
crearea fiierului i eventual la adugarea de nregistrri dup crearea lui. La un apel al
funciei read se citesc cel mult lung octei, nregistrarea avnd lungimea definit la scrierea ei
n fiier. La sfrit de fiier nu se citete nimic i deci funcia read returneaz valoarea zero.
Dac lung = 1, atunci se citete un singur octet. De obicei nu este eficient s se
citeasc cte un octet dintr-un fiier, deoarece apelurile multiple ale funciei read pot conduce
la un consum de timp apreciabil. De aceea se folosesc frecvent nregistrri de 512 octei sau
mai mari.
Funcia read poate fi folosit pentru a citi de la intrarea standard (de la terminalul de
la care s-a lansat programul). n acest caz, descriptorul de fiier este 0. Programatorul nu
trebuie s deschid acest fiier deoarece el este deschis automat la lansarea n execuie a
programului.
Utilizarea funciei read presupune includerea fiierului io.h.
6.12.1.3. Scrierea ntr-un fiier (creare, punere la zi, adugare)
224
Limbajul de programare C
Pentru a scrie o nregistrare ntr-un fiier vom folosi funcia write. Se presupune c
fiierul este deschis n prealabil prin funcia creat sau open.
Ea este asmntoare cu funcia read, doar c realizeaz transferul de date n sens
invers, adic din memorie n fiier. Ea are prototipul:
int write(int df, void*buf, unsigned lung);
Funcia returneaz numrul octeilor scrii n fiier. Acesta este egal cu lung i
definete lungimea nregistrrii scrise n fiier. n cazul n care numrul returnat de funcia
write difer de parametrul lung scrierea a fost eronat.
Funcia write poate fi utilizat pentru a scrie la ieirile standard stdout i stderr.
Astfel, pentru a scrie la ieirea stdout se utilizeaz descriptorul_de_fiier = 1, iar
pentru stderr descriptorul_de_fiier = 2. Aceste fiiere nu trebuie deschise de programator
deoarece ele se deschid automat la lansarea programului.
Utilizarea funciei write implic includerea fiierului io.h.
6.12.1.4. Poziionarea ntr-un fiier
Aa cum s-a artat mai sus, operaiile de citire sau scriere ntr-un fiier se execut
secvenial, adic la fiecare apel al funciei read sau write se citete nregistrarea curent,
respectiv se scrie nregistrarea n poziia curent de pe suportul fiierului. Acest mod de acces
la fiier se numete secvenial i el este util cnd dorim s prelucrm fiecare nregistrare a
fiierului, una dup alta. n practic apar ns i restricii n care dorim s scriem i s citim
nregistrri ntr-o ordine aleatoare. n acest caz se spune c accesul la fiier este aleator. Pentru
a se realiza un acces aleator este nevoie s ne putem poziiona oriunde n fiierul respectiv. O
astfel de poziionare este posibil pe suporturile magnetice de tip disc i se realizeaz folosind
funcia lseek. Ea are prototipul:
long lseek(int df, long deplasament, int origine);
unde:
- df este descriptorul de fiier;
- deplasament definete numrul de octei peste care se va deplasa capul de
citire/scriere al discului;
- origine are una din valorile:
0 deplasamentul se consider de la nceputul fiierului;
1 deplasamentul se consider din poziia curent a capului de citire/scriere;
2 deplasamentul se consider de la sfritul fiierului.
Observaii:
1) Prin apelul lui lseek nu se realizeaz nici un fel de transfer de informaie, ci numai
poziionarea n fiier. Operaia urmtoare realizat prin apelul funciei read sau write se va
realiza din aceast poziie a capului de citire/scriere.
225
Limbajul de programare C
2) Funcia returneaz poziia capului de citire/scriere fa de nceputul fiierului, n
numr de octei. n caz de eroare se returneaz 1L.
3) Utilizarea funciei lseek presupune includerea fiierului io.h.
4) Apelul lseek(df, 01, 2) permite o poziionare la sfritul fiierului al crui descriptor
este df. n mod analog, apelul lseek(df,01,0) permite o poziionare la nceput de fiier.
6.12.1.5. nchiderea unui fiier
Dup terminarea prelucrrii unui fiier acesta trebuie nchis. Acest lucru se realizeaz
automat dac programul se termin prin apelul funciei exit. Programatorul poate nchide un
fiier folosind funcia close. Se recomand nchiderea unui fiier de ndat ce s-a terminat
prelucrarea lui. Aceasta din cauz c numrul fiierelor ce pot fi deschise simultan este
limitat. Aceast limit este dependent de sistemul de operare i ea variaz, de obicei, n
intervalul 15-25.
Observaie:
Fiierele corespunztoare intrrilor i ieirilor standard nu se nchid de ctre
programator.
Funcia close are prototipul:
int close(int df);
unde df este descriptorul fiierului care se nchide. La o nchidere normal, funcia returneaz
valoarea 0. n caz de incident se returneaz valoarea 1. Utilizarea funciei close implic
includerea fiierului io.h.
Exemple:
1. Programul urmtor copiaz intrarea standard la ieirea standard folosind o zon
tampon de 70 de caractere.
#define LZT 70
#include <io.h>
void main() /* copiaza intrarea standard la
ieirea standard */
{
char zt[LZT];
int 1;
while(1 = read (0, zt, LZT)) > 0) write(1, zt, 1);
}
2. Programul urmtor citete un ir de numere flotante de la intrarea standard i
creaz dou fiiere fis1.dat i fis2.dat. Primul conine numerele de ordin impar citite de la
intrarea standard, iar cel de-al doilea conine numerele de ordin par. Apoi se listeaz la
226
Limbajul de programare C
ieirea standard stdout cele dou fiiere n ordinea fis1.dat, fis2.dat, cte un numr pe un rnd
n formatul numr de ordine numr.
n acest program se utilizeaz funcia standard exit, care ntrerupe execuia unui
program. Ea are ca parametru un numr ntreg, care este egal cu zero la o terminare normal a
programului i cu o valoare diferit de zero n caz de eroare, care reprezint codul de eroare.
#include <stdio.h>
#include <stat.h>
#include <fcnt1.h>
#include <io.h>
void main () /* citeste un sir de numere flotante si creeaz
fis1.dat cu numerele de ordin impar si fis2.dat cu
numerele de ordin par; n final se listeaza fisierele */
{ union unr
{ float nr;
char tnr [sizeof(float)]; };
union unr nrcit;
int df1, df2;
int i, j;
/*se deschid fisierele in creare cu acces in citire/scriere*/
if ((df1 = creat(fis1.dat, S_IWRITE|S_IREAD)) == -1)
{ printf(nu se poate deschide fisierul fis1.dat in creare \n);
exit(1); }
if ((df2 = creat(fis2.dat, S_IWRITE|S_IREAD)) == -1)
{ printf(nu se poate deschide fisierul fis2.dat in creare \n);
exit(1); }
/* se citeste sirul de numere si se pastreaza in cele doua fisiere */
j = 1;
while ((i = scanf(%f, &nrcit.nr)) == 1)
{ if (j%2 == 1)
{ /*se scrie in fisierul fis1.dat deoarece j este impar*/
if (( write (df1, nrcit.tnr, sizeof(float)))!= sizeof (float))
{ printf(eroare la scriere in fisierul fis1.dat\n);
exit(1); }
}
else /* se scrie in fisierul fis2.dat fiindca j este par */
if ((write(df2, nrcit.tnr, sizeof(float)))!= sizeof(float))
{ printf(eroare la scriere in fisierul fis2.dat\n);
exit(1); }
227
Limbajul de programare C
j++;
} /* sfarsit while */
if (close(df1) < 0) /* se inchid fisierele */
{ printf(eroare la inchiderea fisierului fis1.dat\n);
exit(1); }
if ((df1 = open (fis1.dat, O_RDONLY)) == -1)
{ printf(nu se poate deschide fisierul fis1.dat in citire\n);
exit(1); } /* se deschide fisierul fis1.dat in citire */
j = 1; /* se listeaza fisierul fis1.dat */
while((i = read(df1, nrcit.tnr, sizeof(float))) > 0)
{ printf(%6d %g\n, j, nrcit.nr);
j + = 2; }
if (i < 0)
{ printf(eroare la citire din fis1.dat\n);
exit(1); }
close(df1);
if ((df2 = open(fis2.dat, O_RDONLY) == -1)
{ printf(nu se poate deschide fis2.dat in citire\n);
exit(2); } /* se deschide fisierul fis2.dat */
printf(n\n\n); /* se listeaza fisierul fis2.dat */
j = 2;
while((i = read (df2, nrcit.tnr, sizeif (float))) > 0)
{ printf(%6d %g\n, j, nrcit.nr);
j + = 2; }
if (i < 0)
{ printf(eroare la citire din fisierul fis2.dat\n);
exit(1); }
close(df2);
}
Observaie:
nrcit este o reuniune creia i se aloc 4 octei, adic o dimensiune capabil s pstreze
un numr flotant n simpl precizie. Zona respectiv este organizat i ca un tablou de tip
char, deoarece funciile read i write necesit o zon tampon de acest tip la majoritatea
implementrilor limbajului C.
6.12.2. Nivelul superior de prelucrare a fiierelor
6.12.2.1. Deschiderea unui fiier
228
Limbajul de programare C
La acest nivel se utilizeaz funcia fopen pentru deschiderea unui fiier. Ea returneaz un
pointer spre tipul FILE (tipul fiier), tip definit n fiierul stdio.h. Tipul FILE este un tip
structurat i el depinde de sistemul de operare. n caz de eroare funcia fopen returneaz
pointerul NULL.
Prototipul funciei fopen este:
FILE *fopen(const char cale, const charmod);
unde:
cale are aceeai semnificaie ca i n cazul funciilor open i creat.
mod este un pointer spre un ir de caractere care definete modul de prelucrare al
fiierului dup deschidere.
Acest ir se definete n felul urmtor:
r deschidere n citire(read);
w deschidere n scriere(write);
a deschidere pentru adugare (append);
r+ deschidere pentru modificare(citire sau scriere);
rb citire binar;
wb scriere binar;
r+b citire/scriere binar.
Dac se deschide un fiier inexistent f.cpp modul w sau a, atunci el este deschis n
creare. Dac se deschide un fiier existent cu modul w, atunci coninutul vechi al fiierului
se pierde i se creaz unul nou cu acelai nume.
Menionm c stdin, stdout, stderr, stdaux i stdprn sunt pointeri spre tipul FILE i
permit ca funciile de nivel superior de prelucrare a fiierelor s poat trata intrarea standard
i ieirile standard pe terminal i imprimant la fel ca i fiierele pe celelalte suporturi.
Singura deosebire const n aceea c aceste fiiere nu se deschid i nici nu se nchid de ctre
programator. Ele sunt deschise automat la lansarea n execuie a programului i se nchid la
apelul funciei exit.
6.12.2.2. Prelucrarea pe caractere a unui fiier
Fiierele pot fi scrise i citite caracter cu caracter, folosind dou funcii simple i
anume putc pentru scriere i getc pentru citire. Funcia putc are prototipul:
int putc(int c, FILE *pf)
unde:
- c - este codul ASCII al caracterului care se scrie n fiier;
- pf - este pointerul spre tipul FILE a crui valoare a fost returnat de funcia
fopen la deschiderea fiierului n care se scrie. n particular, pf poate fi unul din
pointerii:
229
Limbajul de programare C
stdout ieire standard;
stderr ieire pe terminal n caz de eroare;
stdaux comunicaie serial;
stdprn ieire paralel la imprimant.
Funcia putc returneaz valoarea lui c respectiv 1 n caz de eroare.
Funcia getc are prototipul:
int getc(FILE *pf);
unde pf este pointerul spre tipul FILE a crui valoare a fost returnat de funcia fopen la
deschiderea fiierului. Ea returneaz codul ASCII a caracterului citit sau EOF la sfrit de
fiier sau eroare. n particular, pf poate fi pointerul stdin (intrare de la tastatur).
6.12.2.3. nchiderea unui fiier
Dup terminarea prelucrrii unui fiier, aesta urmeaz a fi nchis. n acest caz se
utilizeaz funcia fclose. Ea are prototipul:
int fclose(FILE *pf);
unde pf este pointerul spre tipul FILE a crui valoare a fost definit la deschiderea fiierului
prin intermediul funciei fopen. Funcia fclose returneaz:
0 la nchiderea normal a fiierului;
1 n caz de eroare.
Exemple:
1. Programul copiaz intrarea standard la ieirea standard stdout, folosind funciile
getc i putc.
#include <stdio.h>
void main( ) /*copiaza intrarea stdin la ieirea stdout */
{ int c;
while((c = getc(stdin)) ! = EOF) putc(c, stdout);
}
Observaie:
Macrourile getchar i putchar sunt definite n fiierul stdio.h astfel:
#define getchar ( ) .getc(stdin)
#define putchar(c) putc(c, stdout)
2. Programul urmtor copiaz intrarea standard la imprimant.
#include <stdio.h>
void main ( ) /*copiaz caracterele din stdin la imprimanta */
{ int c;
while ((c = getc(stdin)) != EOF) putc(c, stdprn);
}
230
Limbajul de programare C
3. Programul urmtor scrie la ieirea stdout caracterele unui fiier a crui cale este
argumentul din linia de comand. Dac nu exist un argument n linia de comand, atunci
se citete de la intrarea standard.
#include <stdio.h>
void main(argc, argv) /* listeaza la iesirea stdout un fisier a carui cale
este argumentul din linia de comanda */
int argc;
char *argv [ ];
{ FILE *pf;
int c;
if(argc == 1) /* nu exista argument in linia de comanda */
pf = stdin;
else /* se deschide fisierul a carui cale se afla in argv [1] */
if((pf = fopen (* ++ argv, r)) == NULL)
{ printf(nu se poate deschide fisierul %s\n, *argv);
exit(1); }
while((c = getc(pf)) != EOF) putchar (c);
exit(0); /* se inched automat fisierele deschise */
6.12.2.4. Operaiile de intrare/ieire cu format
Biblioteca standard a limbajului C conine funcii care permit realizarea operaiilor de
intrare/ieire cu format. Se pot utiliza funciile fscanf i fprintf, prima pentru citire cu
format dintr-un fiier, iar a doua pentru scriere cu format ntr-un fiier.
Funcia fscanf este asemntoare cu funcia scanf. Ea are un parametru n plus fa de
scanf, un pointer spre tipul FILE care definete fiierul din care se face citirea. Acest
pointer este primul parametru al funciei fscanf. Ceilali parametrii au aceeai semnificaie
ca i n cazul funciei scanf.
Rezult c funcia fscanf poate fi apelat printr-o expresie de atribuire de forma:
nr = fscanf (pf, control, par
1
, par
2
, , par
n
);
unde pf este pointer spre tipul FILE i valoarea lui a fost definit prin apelul funciei
fopen (acesta definete fiierul din care se face citirea), iar ceilali parametri sunt identici
cu cei utilizai la apelul funciei scanf.
Funcia fscanf, ca i scanf, returneaz numrul cmpurilor citite din fiier. La
ntlnirea sfritului de fiier se returneaz valoarea EOF definit n fiierul stdio.h. Pentru
pf= stdin, funcia fscanf este identic cu scanf.
Funcia fprintf este analog cu funcia printf. Primul ei parametru este un pointer spre
tipul FILE i el definete fiierul n care se scriu datele. Ceilali parametrii sunt identici cu
cei ai funciei printf.
231
Limbajul de programare C
Funcia fprintf, ca i funcia printf, returneaz numrul caracterelor scrise n fiier sau
1 caz de eroare. Pentru pf = stdout, funcia fprintf este identic cu printf. De asemenea,
se pot utiliza pointerii standard obinuii:
stderr afiarea mesajelor de eroare pe terminal;
stdaux comunicaie serial;
stdprn ieirea paralel la imprimant.
Menionm c la comunicaia serial se poate conecta o imprimant serial.
6.12.2.5. Intrri/ieiri de iruri de caractere
Biblioteca standard a limbajului C conine funciile fgets i fputs care permit citirea
respective scrierea ntr-un fiier ale crui nregistrri sunt irurile de caractere.
Funcia fgets are prototipul:
char*fgets(char*s, int n, FILE*pf);
unde: - s este pointerul spre zona n care se face citirea caracterelor;
- n 1 este numrul maxim de caractere care se citesc iar pf este pointerul spre
tipul FILE care definete fiierul din care se face citirea.
De obicei s este numele unui tablou de tip char de dimensiune cel puin n. Dac irul se
termin cu \n, citirea se oprete. n acest caz, n zona receptoare se transfer caracterul
\n i apoi caracterul NUL(\0). n mod normal, funcia returneaz valoarea pointerului
s. La ntlnirea sfritului de fiier se returneaz valoarea NULL.
Funcia fputs scrie ntr-un fiier un ir de caractere care se termin prin \n. Ea are
prototipul:
int fputs(const char *s, FILE *pf);
unde: - s este pointerul spre zona care conine irul de caractere care se scrie;
- pf este pointerul spre tipul FILE care definete fiierul n care se scrie.
Funcia fputs returneaz codul ASCII al utilimului caracter scris sau 1 n caz de eroare.
Aceste funcii sunt realizate folosind funcia getc pentru fgets i putc pentru fputs.
Pentru a citi de la intrarea standard stdin, se poate folosi funcia gets, care nu mai are
parametrii pf i n. Parametrul pf este implicit stdin. Funcia gets citete caracterele de la
intrarea standard pn la ntlnirea caracterului \n care nu mai este pstrat n zona spre
care pointeaz s. irul de caractere citit se termin i n acest caz cu \0.
n mod analog, pentru a scrie la ieirea standard se poate folosi funcia puts, care nu mai
are parametrul pf, acesta fiind implicit stdout. n rest, funcia puts este la fel ca i funcia
fputs.
6.12.2.6. Poziionarea ntr-un fiier
232
Limbajul de programare C
Biblioteca standard a limbajului C conine funcia fseek, cu ajutorul creia se poate
deplasa capul de citire/scriere al discului n vederea prelucrrii nregistrrilor fiierului
ntr-o ordine oarecare, diferit de cea secvenial (acces aleator). Aceast funcie este
asemntoare cu funcia lseek. Ea are prototipul:
int fseek (FILE * pf,long deplasament, int origine);
unde pf este pointerul spre tipul FILE care definete fiierul n care se face poziionarea
capului de citire/scriere iar deplasament i origine au aceeai semnificaie ca i n cazul
funciei lseek.
Funcia fseek returneaz valoarea zero la poziionare corect i o valoare diferit de zero n
caz de eroare.
O alt funcie util n cazul accesului aleator este funcia ftell, care indic poziia capului
de citire n fiier. Ea are prototipul:
long ftell (FILE * pf);
unde pf este pointerul spre tipul FILE care definete fiierul n cauz.
Funcia returneaz o valoare de tip long care definete poziia curent a capului de
citire/scriere i anume reprezint deplasamentul n octei a poziiei capului fa de
nceputul fiierului.
6.12.2.7. Prelucrarea fiierelor binare
Fiierele organizate ca date binare (octeii nu sunt considerai ca fiind coduri de caractere)
pot fi prelucrate la acest nivel folosind funciile fread i fwrite.
n acest caz, se consider c nregistrarea este o colecie de date structurate numite
articole. La o citire se transfer ntr-o zon special, numit zon tampon, un numr de
articole care se presupune c au o lungime fix. n mod analog, la scriere se transfer din
zona tampon un numr de articole de lungime fix. Cele dou funcii au prototipurile de
mai jos:
unsigned fread (void * ptr, unsigned dim, unsigned nrart, FILE * pf);
unsigned fwrite (const void * ptr, unsigned dim, unsigned nrart, FILE * pf);
unde:
- ptr este pointerul spre zona tampon ce conine articole citite (nregistrarea citit);
- dim este un ntreg ce reprezint lungimea unui articol;
- nrart este un ntreg ce reprezint numrul de articole ce se transfer;
- pf este un pointer spre tipul FILE care definete fiierul din care se face citirea.
Ambele funcii returneaz numrul de articole transferate sau l n caz de
eroare.
Exemple:
1. Programul citete de la intrarea stdin datele ale cror formate sunt definite mai jos i le
scrie n fiierul misc.dat din directorul curent. Formatul datelor de intrare este urmtorul
233
Limbajul de programare C
tip denumire um cod pret cantitate
I REZISTENTA010KO 123456789 1.5 10000.0
#include <stdio.h>
#define MAX 50
typedef struct { char tip [2];
char den [MAX +1];
int val;
char unit[3];
long unit [3]
float pret;
float char; }
ARTICOL;
union {ARTICOL a[6]; /* 6 articole in zona tampon */
} buf;
int cit (ARTICOL *str)
// citeste datele de la intrarea standard si le scrie in structura spre care pointeaza str
{ int c, nr;
float x,y;
while ((nr = scanf(%1s%50s%3d&2s%1d, str tip,
str den, &str val, str unit, & str cod)) !=
if(nr ==EOF) return EOF;
printf(rand eronat; se reia \n);
while ((c=getchar())!=\n&&c!=EOF);/*avans pana la newline sau EOF*/
if (c ==EOF) return EOF; } /*sfarsit while */
str pret = x; str cant = y;
} /* sfarsit cit */
void main ()
/* creeaza fisierul misc.dat cu datele citite de la stdin */
{ FILE *pf;
ARTICOL a;
int i, n;
/* se deschide fisierul in scriere binara */
if ((pf = fopen (misc.dat, wb)) ==NULL)
{ printf(nu se poate deschide in creare fisierul misc.dat\n);
exit(1); }
for ( ; ;) /* se umple zona tampon a fisierului */
{ for (i = 0; i < 6; i++) {
234
Limbajul de programare C
if ((n = cit (&a)) ==EOF) break;
buf.a[i] = a; }
if (i !=0) /*se scrie zona tampon daca nu este vida*/
if (i !=fwrite (buf.zt, sizeof(ARTICOL), i, pf))
{ /*eroare la scrierea in fisier*/
printf(eroare la scrierea in fisier\n);
exit(1); }
if (n == EOF) break;
} /* sfarsit for */
fclose(pf);
} /* sfarsit main */
Observaie:
n TURBO C se pot face atribuiri de structuri, spre deosebire de unele versiuni ale
limbajului C care nu admit acest lucru. Deci atribuirea buf.a[i] = a; utilizat anterior este
corect.
2. Programul urmtor listeaz articolele fiierului misc.dat, creat prin programul anterior,
pentru care tip = I.
#include<stdio.h>
#define MAX 50
typedef struct { char tip[2];
char den[MAX + 1];
int val;
char unit[3];
long cod;
float pret;
float cant;
}
ARTICOL;
union { ARTICOL a[6];
char zt[6*sizeof (ARTICOL)]; } buf;
void main () /* citeste fisierul misc.dat */
{ FILE *pf;
int i,n; ARTICOL a;
if ((pf = fopen(misc.dat, rb)) == NULL)
{ printf(nu se poate deschide fisierul misc.dat in citire\n);
exit(1); }
235
Limbajul de programare C
printf(%60s\n\n\n, INTRARI\n\n); /* se citeste o inregistrare */
while ((n = fread (buf.zt, sizeof(ARTICOL), 6, pf )) > 0)
/* se listeaza articolele de tip I dintr-o inregistrare */
for (i = 0; i < n; i++)
{ a = buf.a[i];
if (a.tip[0] == I)
printf (%-50s %03d%s %91d %.2f %.2f\n,
a.den, a.val, a.unit, a.cod, a.pret, a.cant);
}
fclose(pf);
}
6.12.2.8. tergerea unui fiier
Un fiier poate fi ters apelnd funcia unlink. Aceasta are prototipul:
int unlink(const char *cale);
unde cale este un pointer spre un ir de caractere identic cu cel utilizat la crearea fiierului
n funcia creat sau fopen.
Funcia unlink returneaz valoarea zero la o tergere, respective 1 n caz de eroare.
6.13. Realizarea programelor din surse multiple
6.13.1. Introducere
Scrierea unui program complex, care s respecte regulile programrii structurate,
presupune mprirea programului n module. Folosirea modulelor pentru structurarea
programului ofer mai multe avantaje, motiv pentru care este acceptat ca o metod de lucru
general n scrierea programelor. n primul rnd, dezvoltarea aplicaiei se poate face n echip,
fiecare membru al echipei realiznd anumite module ale aplicaiei. n al doilea rnd, aplicaia
poate fi depanat i adaptat mai uor. De asemenea, codul din aceste module poate fi refolosit
i n alte aplicaii.
Modulele sunt, de obicei, funcii C, avnd diverse scopuri. Funciile C sunt folosite
pentru a mpri anumite pri ale programului i pentru construirea de funcii de calcul. Exist
dou metode de lucru, pentru construirea programului. Prima metod, const n scrierea
acestor funcii n unul sau mai multe fiiere surs C i ncluderea lor n fiierul principal ce
conine funcia main(), folosind directiva de compilare #include. A doua metod, const n
folosirea proiectelor C pentru gruparea fiierelor surs i a bibliotecilor folosite n program i
realizarea aplicaiei finale prin intermediul proiectului.
236
Limbajul de programare C
Fiecare metod are avantaje i dezavantaje. Prima se poate folosi pentru aplicaii
simple, nu foarte mari. n schimb, a doua metod este recomandat pentru aplicaii ce necesit
cod mult (i timp de dezvoltare mare). Le vom prezenta n detaliu, n continuare:
6.13.2. Realizarea programului prin includerea fiierelor surs prin
intermediul directivei #include
Aceast metod este folosit, de obicei, n faza de nvarea a unui limbaj sau pentru
realizarea unei aplicaii simple. Programatorul i scrie codul funciilor n unul sau mai multe
fiiere surs. Dac programul nu are foarte multe funcii, aceste funcii se depun ntr-un singur
fiier surs, fiier ce va conine i funcia main(). Eventual, dac programatorul definete
tipuri de date, poate construi un fiier cu extensia h (numit header) n care s depun
definiiile de tipuri i prototipurile funciilor din fiierul surs, header ce se include i el n
fiierul surs. n schimb, dac programul are multe funcii, cu funciuni diferite, ele se pot
scrie n unul sau mai multe fiiere surs C, pe care s le includ n fiierul cu funcia main().
Vom considera un exemplu practic pentru a putea surprinde elementele de baz ale
acestei metode. S presupunem c dorim realizarea unui program care s permit operarea cu
structuri de date de tip vector. Pentru aceasta, fiierul surs va trebui s conin funcii pentru
citire, afiare, ordonare i pentru calculul sumei elementelor vectorului. Soluia va consta n
realizarea unui singur fiier surs, care s conin rutinele necesare operrii cu strucura de tip
vector i funcia main().
Programul va arta astfel :
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
// Se scriu prototipurile funciilor ce se folosesc n program, eventual la fiecare
// funcie se scrie sub form de comentariu la ce folosete
void Citire(int X[],int &N);
void Afisare(int X[],int N);
void Ordonare(int X[],int N);
long Suma(int X[],int N);
void Citire(int X[],int &N)
{ int i;
printf("\nDati numarul de elemente ale vectorului:");
scanf("%d",&N);
for (i=0;i<N;i++)
{ printf("X[%d]=",i);
scanf("%d",&X[i]);
}
237
Limbajul de programare C
}
void Afisare(int X[],int N)
{ int i;
printf("\nVectorul ");
for(i=0;i<N;i++)
printf("\n%d",X[i]);
}
void Ordonare(int X[],int N)
{ int i,k,temp;
do
{ k=0;
for(i=0;i<N-1;i++)
if(X[i]>X[i+1])
{ temp=X[i];
X[i]=X[i+1];
X[i+1]=temp;
k=1;
}
}
while (k!=0);
}
long Suma(int X[],int N)
{ int i;
long S;
for(S=0,i=0;i<N;i++)
S+=X[i];
return S;
}
void main()
{ int Optiune,Y;
int X[100],N;
do
{ clrscr();
printf("\n 1-Citire");
printf("\n 2-Afisare");
printf("\n 3-Calcul suma");
printf("\n 4-Ordonare");
printf("\n 5-Iesire");
238
Limbajul de programare C
printf("\nAlegeti optiunea ");
scanf("%d",&Optiune);
switch(Optiune)
{ case 1 :Citire(X,N); break;
case 2 :Afisare(X,N); break;
case 3 :printf("\nSuma elementelor este %ld",Suma(X,N));
break;
case 4 :Ordonare(X,N); break;
}
getch();
}
while ( Optiune!=8);
}
n schimb, dac aplicaia de operare cu structura de tip vector mai necesit i alte
funcii, ca de exemplu de determinarea minimului din vector, determinarea numrului de
elemente cu o anumit proprietate, cutarea unui element, sau dac se dorete utilizarea i n
alte programe a acestor funcii, programatorul va trebui s sparg programul n dou fiiere
surs: un fiier coninnd codul acestor funcii i fiierul principal coninnd funcia main() (i
eventual i alte funcii), n care s foloseasc funciile definite. Varianta aceasta conine
urmtoarele fiiere:
Fiierul surs, fiier ce conine codul funciilor, s-i spunem funcii.cpp este urmtorul:
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
void Citire(int X[],int &N);
void Afisare(int X[],int N);
void Ordonare(int X[],int N);
long Suma(int X[],int N);
int Minim(int X[],int N);
int NumararePoz(int X[],int N);
int Cautare(int X[],int N,int Y);
void Citire(int X[],int &N)
{ int i;
printf("\nDati numarul de elemente ale vectorului:");
scanf("%d",&N);
for (i=0;i<N;i++)
{ printf("X[%d]=",i);
scanf("%d",&X[i]); }
239
Limbajul de programare C
}
void Afisare(int X[],int N)
{ int i;
printf("\nVectorul ");
for(i=0;i<N;i++)
printf("\n%d",X[i]);
}
void Ordonare(int X[],int N)
{ int i,k,temp;
do
{ k=0;
for(i=0;i<N-1;i++)
if(X[i]>X[i+1])
{ temp=X[i];
X[i]=X[i+1];
X[i+1]=temp;
k=1;
}
}
while (k!=0);
}
long Suma(int X[],int N)
{ int i;
long S;
for(S=0,i=0;i<N;i++)
S+=X[i];
return S;
}
int Minim(int X[],int N)
{ int i;
int Min;
Min=X[0];
for(i=0;i<N;i++)
if (Min>X[i])
Min=X[i];
return Min;
}
int NumararePoz(int X[],int N)
240
Limbajul de programare C
{ int i;
int Nr;
Nr=0;
for(i=0;i<N;i++)
if (X[i]>0)
Nr++;
return Nr;
}
int Cautare(int X[],int N,int Y) //intoarce pozitia sau -1 in ca ca nu gaseste
{ int i;
for(i=0;i<N;i++)
if (X[i]==Y)
return 1;
return -1;
}
Al doilea fiier, fiierul principal ce folosete funciile prin intermediul proiectului,
s-i spunem princip.cpp arat astfel:
#include <stdio.h>
#include <conio.h>
#include "funcii.cpp" // se include fiierul surs, existent n directorul curent
void main()
{ int Optiune,Y;
int X[100],N;
do
{ clrscr();
printf("\n 1-Citire");
printf("\n 2-Afisare");
printf("\n 3-Calcul suma");
printf("\n 4-Ordonare");
printf("\n 5-Minim elemente");
printf("\n 6-Numarare elemente pozitive");
printf("\n 7-Cautare pozitie element");
printf("\n 8-Iesire");
printf("\nAlegeti optiunea ");
scanf("%d",&Optiune);
switch(Optiune)
{
case 1 :Citire(X,N);
241
Limbajul de programare C
break;
case 2 :Afisare(X,N);
break;
case 3 :printf("\nSuma elementelor este %ld",Suma(X,N));
break;
case 4 :Ordonare(X,N);
break;
case 5 :printf("\nMinimul elementelor este %d",Minim(X,N));
break;
case 6 :printf("\nNumarul elementelor pozitive este %d",
NumararePoz(X,N));
break;
case 7 :printf("\Dati elementul pe care il cautati");scanf("%d",&Y);
printf("\nPozitia elementului este %d",Cautare(X,N,Y));
break;
}
getch();
}
while ( Optiune!=8);
}
Aceast variant este mult mai flexibil dect varianta cu un singur fiier surs. De
exemplu, putei refolosi fiierul surs i n alte aplicaii, doar prin includerea lui n acele
aplicaii .
6.13.3. Realizarea programului prin folosirea proiectelor

Metoda prezentat anterior are cteva dezavantaje. n primul rnd, dac se fac
modificri ntr-una din funcii, la recompilarea programului se vor recompila i celelalte (n
mod inutil ). Acest lucru are ca efect (mai ales pentru programe mari) un consum de timp
mare. n al doilea rnd, dac se dorete reutilizarea codului, fr a oferi i codul surs, prima
variant nu permite acest lucru. De asemenea, utilizarea bibliotecilor obj sau lib, nu este
permis n aceea variant. Aceste dezavantaje dispar dac programul este format din mai multe
fiiere surs, legate ntre ele prin intermediul proiectului. Aceast metod a devenit astzi o
metod standard de lucru. Cea mai mare parte a mediile de programare, inclusiv cele vizuale
de ultim generaie, folosesc noiunea de proiect pentru a pune la un loc entitile programului.
Ce este de fapt un proiect C? Proiectul este de fapt un fiier cu extensia PRJ, fiier ce
conine informaii despre prile programului. Acest proiect se construiete i gestioneaz prin
intermediul comenzilor din meniul Project al mediului de programare (toate variantele
Borland conin acest meniu).
242
Limbajul de programare C
Vom prezenta mai departe modul de lucru cu proiecte. Realizarea unui proiect,
presupune parcurgerea mai multor etape:
scrierea funciilor C n mai multe fiiere surs, de obicei grupate dup funciuni i
fr a depi un numr de aproximativ 15 funcii.
realizarea fiierelor header, fiiere cu extensia h, ce conin definiiile de tipuri,
declaraiile de variabile i prototipurile funciilor dintr-un fiier surs. Pentru
fiecare fiier surs ( mai puin cel principal) se va realiza un fiier header. Acest
fiier header se va include n alte fiiere surs ce folosesc funcii care nu sunt
definite acolo (folosind directiva #include). De exemplu, n fiierul principal va
trebui s includem fiierul header, dac vom folosi funcii de operare cu vectori.
Acest lucru este necesar compilatorului, acesta fiind avertizat despre existena
funciilor ( prin urmare, nu se va mai semnala o eroare legat de neexistena acelor
funcii).
crearea proiectului, folosind din meniul Compile, comanda Open, urmat de
numele proiectului. Atenie, numele proiectului va deveni automat i numele
aplicaiei. Dup crearea proiectului, se adaug fiierele surs n fereastra Project,
ce va aprea n partea de jos a ecranului. Adugarea o putei face folosind tasta
Insert. De asemenea, dac ai inserat greit o surs (atenie nu fiierele header se
includ), o putei terge folosind tasta Delete ( dup ce v-ai poziionat pe fiierul
n cauz). Putei insera surse scrise n limbaj de asamblare sau bilioteci obj sau lib.
se trece la compilarea i execuia aplicaiei finale, folosind comenzile din meniul
Compile i meniul Run ( Compile, Run , Make, Link ).
nchiderea proiectului, folosind din meniul Project , comanda Close. Atenie,
proiectul este prioritar oricrui fiier surs, aa c dac uitai s-l nchidei i trecei
la un alt program se va executa tot proiectul.
Vom exemplifica mai departe lucrul cu proiecte, folosind exemplu anterior referitor la
operarea cu structura de tip vector. Soluia cu proiecte va presupune crearea unui proiect care
s includ dou fiiere surs. Prin urmare, pentru rezolvarea problemei va trebui s se creeze
un project n care s se includ dou fiiere: cel cu codul funciilor i cel cu funcia main,
unde se apeleaz funciile definite anterior. De asemenea, va fi nevoie s se creeze un fiier cu
extensia h coninnd prototipurile funciilor din surs. Le prezentm mai departe.
1. Fiierul surs, fiier ce conine codul funciilor, funcii.cpp :
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
void Citire(int X[],int &N)
{ int i;
printf("\nDati numarul de elemente ale vectorului:");
scanf("%d",&N);
243
Limbajul de programare C
for (i=0;i<N;i++)
{ printf("X[%d]=",i);
scanf("%d",&X[i]); }
}
void Afisare(int X[],int N)
{ int i;
printf("\nVectorul ");
for(i=0;i<N;i++)
printf("\n%d",X[i]); }
void Ordonare(int X[],int N)
{ int i,k,temp;
do
{ k=0;
for(i=0;i<N-1;i++)
if(X[i]>X[i+1])
{ temp=X[i];
X[i]=X[i+1];
X[i+1]=temp;
k=1; }
}
while (k!=0);
}
long Suma(int X[],int N)
{ int i;
long S;
for(S=0,i=0;i<N;i++)
S+=X[i];
return S; }
int Minim(int X[],int N)
{ int i;
int Min;
Min=X[0];
for(i=0;i<N;i++)
if (Min>X[i])
Min=X[i];
return Min; }
int NumararePoz(int X[],int N)
{ int i;
244
Limbajul de programare C
int Nr;
Nr=0;
for(i=0;i<N;i++)
if (X[i]>0)
Nr++;
return Nr; }
int Cautare(int X[],int N,int Y) //intoarce pozitia sau -1 in ca ca nu gaseste
{ int i;
for(i=0;i<N;i++)
if (X[i]==Y)
return 1;
return -1; }
2. Fiierul cu extensia h, numit functii.h, va fi :
void Citire(int X[],int &N);
void Afisare(int X[],int N);
void Ordonare(int X[],int N);
long Suma(int X[],int N);
int Minim(int X[],int N);
int NumararePoz(int X[],int N);
int Cautare(int X[],int N,int Y);
3. Al doilea fiier surs, va fi fiierul principal ce folosete funciile prin intermediul
proiectului, s-i spunem princip.cpp:
#include <stdio.h>
#include <conio.h>
#include "functii.h"
void main()
{ int Optiune,Y;
int X[100],N;
do
{ clrscr();
printf("\n 1-Citire");
printf("\n 2-Afisare");
printf("\n 3-Calcul suma");
printf("\n 4-Ordonare");
printf("\n 5-Minim elemente");
printf("\n 6-Numarare elemente pozitive");
printf("\n 7-Cautare pozitie element");
printf("\n 8-Iesire");
245
Limbajul de programare C
printf("\nAlegeti optiunea ");
scanf("%d",&Optiune);
switch(Optiune)
{ case 1 :Citire(X,N);break;
case 2 :Afisare(X,N);break;
case 3 :printf("\nSuma elementelor este %ld",Suma(X,N));
break;
case 4 :Ordonare(X,N);break;
case 5 :printf("\nMinimul elementelor este %d",Minim(X,N));
break;
case 6 :printf("\nNumarul elementelor pozitive este %d",
NumararePoz(X,N));break;
case 7 :printf("\Dati elementul pe care il cautati");
scanf("%d",&Y);
printf("\nPozitia elementului este
%d",Cautare(X,N,Y));break;
}
getch();
}
while ( Optiune!=8);
}
n final, se va crea proiectul C folosind comanda Open din meniul Project i se vor
include doar cele dou fiiere surs.
6.13.4. Etapele de realizare a unei aplicaii

n general, orice problem care se cere rezolvat cu ajutorul calculatorului implic
parcurgerea ctorva etape pn la obinerea unei aplicaii funcionale. Aceste etape sunt:
Analiza problemei se stabilesc datele de intrare, ieire i se formuleaz cerinele
utilizatorilor n detaliu.
Modelarea aplicaiei descompunerea aplicaiei pe module, pe subprobleme.
Analiza fiecrei subprobleme determinarea metodelor de rezolvare pentru fiecare
subproblem.
Transpunerea algoritmilor folosind un limbaj de programare se traduce
pseudocodul n limbajul de programare ales.
Testarea programului se testeaz rutinele aplicaiei, n diverse situaii i se
elimin eventualele bug-uri.
Elaborarea documentaiei utilizator se alctuiete un fel de help al aplicaiei, un
manual de utilizare a aplicaiei.
246
Limbajul de programare C
Verificarea produsului final const n verificarea prodususlui final, n totalitatea
sa.
Vom prezenta aceste etape pe o aplicaie ce permite evidena candidailor la un examen
de admitere.
1. Analiza problemei
Programatorul, n funcie de sistemul de admitere al acelei instituii ( probabil discut
cu responsabilii acelei instituii sau pe baza unui caiet de sarcini), decide structura datelor,
decide ce structuri de date va folosi pentru modelarea problemei.
n primul rnd, trebuie s lmureasc ce date vor trebui introduse i ce rezultate se vor
afia. De asemenea, unde se vor pstra aceste date, care vor fi structurile de date folosite la
memorarea lor, n funcie i de limbajul folosit.
Pentru aplicaia noastr, realizat n limbajul C, vom folosi fiiere binare pentru a
pstra datele candidailor. n principal vom avea un fiier Candidat.dat pentru a pstra datele
candidailor (inclusiv notele). De asemenea, se vor folosi alte dou fiiere de date, numite
admisi.dat i respini.dat care s pstreze candidaii admii, respectiv, pe cei respini. Fiierul
principal candidat.dat va conine, pentru fiecare candidat, numele, prenumele, iniiala tatlui,
notele la cele dou probe de concurs i media. Pentru rezultatele care trebuie s apar la
imprimant se vor folosi fiiere text.
2. Modelarea aplicaiei.
Problema admiterii este mprit n mai multe module (subprobleme) dup cum
urmeaz :
Introducerea datelor personale ale candidailor, pe baza datelor de la inscrierea
candidailor. Pentru asta vom folosi modul Adaugare.
mprirea candidailor pe sli, alfabetic, cte 20 n sli. Pentru asta vom folosi
modulul AfiareSli.
Introducerea notelor i calculul mediei finale, dup ce probelele de examen s-au
desfurat. Modulul care corespunde acestei operaii este IntroducereNote.
Determinarea candidailor admii - conform criteriului de admitere al instituiei
trebuie obinut lista cu candidaii admii.
Determinarea candidailor respini - conform criteriului de admitere al instituiei
trebuie obinut lista cu candidaii respini.
3-4. Analiza fiecrei subprobleme i transpunerea n limbajul de programare a
fiecrei subprobleme.
Pentru fiecare subproblem se construiete un subprogram corespunztor, subprogram
care s permit realizarea acelor cerine. Pentru exemplul nostru, vor fi funcii care lucreaz cu
fiiere. Programul principal va conine un meniu text, ce permite selectarea uneia din opiunile
de lucru:
247
Limbajul de programare C
Funcia Adugare - adaug candidaii n fiierul principal candidat.dat. Ea poate fi
apelat de oricte ori este nevoie ( ea adaug la sfritul fiierului de date )
Funcia AfisareSali - ordoneaz datele din fiierul de date alfabetic i creaz un
fiier text cu candidaii mprii cte 20 ntr-o sal, fiier text ce poate fi apoi
tiprit la imprimant.
Funcia Introducere Note permite s se introduc notele pentru fiecare candidat
i s se calculeze media la fiecare, note i medie care sunt apoi depuse n fiierul
candidat.dat.
Funciile Admisi i Respinsi- construiesc alte dou fiiere, fiecare coninnd
candidaii admii, respectiv respini. Primul conine candidaii care au obinut cel
puin 5 la ambele probe de examen, ordonai descresctor dup medie. Al doilea,
cel cu candidaii respini, conine candidaii care nu au obinut cel puin 5 la fiecare
prob, ordonai alfabetic. pentru a tipri aceste date la imprimant, se creaz dou
fiiere text, care apoi se pot tipri sau se pot vizualiza.
Urmeaz apoi testarea fiecrui modul (a fiecrei proceduri) i n final, testarea
programului final. De asemenea, se va realiza un manual de utilizare.
Programul C corespunztor este prezentat mai jos:
# include <conio.h>
# include <stdio.h>
# include <string.h>
# include <alloc.h>
# include <stdlib.h>
typedef struct Cand
{ char Nume[20],Prenume[20];
char Init[3];
char Dn[11] ;
int Ob1,Ob2;
float MedieG; } Candidat;
int Optiune;
void Adaugare(void)
{ FILE *F;
Candidat *C;
char Rasp[2];
F = fopen("candidat.dat","r+b");
if (F==NULL)
F = fopen("candidat.dat","wb");
fseek(F,0,2);
C = (Candidat *)malloc(sizeof(Candidat));
248
Limbajul de programare C
do
{ printf("\n Nume :");
scanf("%s", C->Nume);
printf("\n Initiala tatalui :");
scanf("%s", C->Init);
printf("\n Prenume :");
scanf("%s", C->Prenume);
printf("\n Data nasterii :");
scanf("%s", C->Dn);
fwrite(C,sizeof(Candidat),1,F);
printf("\n Mai aveti candidati de adaugat :");
scanf("%s",Rasp); }
while(stricmp(Rasp,"NU")!=0);
fclose(F);
}
void AfisareSali(void)
{ FILE *F;
FILE *F1;
Candidat *C,*C1;
unsigned K;
int I,Nr;
F=fopen("Candidat.dat","r+b");

F1=fopen("Sali.txt", "w");
//Fisierul text ce va contine lista cu candidatii pe sali
//Ordonam alfabetic candidatii pentru a-i imparti pe sali,
//ordonand fizic fisierul
C = (Candidat *)malloc(sizeof(Candidat));
C1 = (Candidat *)malloc(sizeof(Candidat));
do
{ K=0;
fseek(F,0,0);
while (! feof(F))
{
fread(C,sizeof(Candidat),1,F);
if (feof(F)) break;
fread(C1,sizeof(Candidat),1,F);
if (feof(F)) break;
249
Limbajul de programare C
if ((strcmp(C->Nume,C1->Nume)>0)||((strcmp(C->Nume,C1-
>Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
{ fseek(F,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,F);
fwrite(C,sizeof(Candidat),1,F);
K=1; }
fseek(F,-(long)sizeof(Candidat),1);
}
}
while (K);
I=1;
Nr=1;
fseek(F,0,0);
fprintf(F1,"\nSala numarul %d",Nr);
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F)) break;
fprintf(F1,"\n%3d%s %s%s",I,C->Nume,C->Init,C->Prenume);
I++;
if (I==21)
{ Nr++;
fprintf(F1,"\nSala numarul :%d",Nr);
I=1; }
}

fclose(F);
fclose(F1);
}
void ModificareDate(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
printf("\nDati numele candidatului pentru care se modifica datele:");
scanf("%s",Nm);
printf("\nDati prenumele candidatului pentru care se modifica datele:");
scanf("%s",Pn);
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
while (! feof(F))
250
Limbajul de programare C
{ fread(C,sizeof(Candidat),1,F);
if ((strcmp(Nm,C->Nume)==0) && (strcmp(C->Prenume,Pn)==0))
{ printf("\nObiect1 : ");scanf("%d",&C->Ob1);
printf("\nObiect2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);break; }
}
fclose(F);
}
void IntroducereNote(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
int Poz;
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
Poz=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F))
break;
printf("\nCandidatul %s %s %s",C->Nume,C->Init,C->Prenume);
printf("\nNota la obiectul 1 : ");scanf("%d",&C->Ob1);

printf("\nNota la obiectul 2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2.0;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);
Poz++;
fseek(F,(long)Poz*sizeof(Candidat),0);
}
fclose(F);
}
void AfisareAdmisi(void)
{ FILE *F,*G;
FILE *G1;
Candidat *C,*C1;
unsigned K;
251
Limbajul de programare C
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul contine lista cu admisii
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii admisi in fisierul Admisi.dat
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1>=5) && (C->Ob2>=5))
{ write(C,sizeof(Candidat),1,F);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati admisi
{ G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat, nu mai trebuie ordonat
{
/*Ordonam descrescator candidatii admisi (adica au peste 5 la fiecare obiect
ordonand fizic fisierul */
do
{ K=0;
fseek(G,0,0);

while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((C->MedieG < C1->MedieG)||((C->MedieG==C1->MedieG)&&((strcmp(C-Nume,
C1->Nume)>0) || ((strcmp(C->Nume,C1->Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))))
{ fseek(G,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1; }
fseek(G,-(long)sizeof(Candidat),1);
}
252
Limbajul de programare C
}
while (K);
}
G1=fopen("Admisi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
admisi, fisier ce se poate tipari la imprimanta */
Nr=1;
printf("\nCandidati admisi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f",Nr,C->Nume,C->Init,C->Prenume,
C->Ob1,C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf(""); //Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii admisi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("Nu avem candidati admisi");
}
void AfisareRespinsi(void)
{ FILE *F,*G;
FILE *G1;
Candidat *C,*C1;
unsigned K;
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul va contine lista cu respinsii
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii respinsi in fisierul respinsi.dat
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1<5) || (C->Ob2<5))
253
Limbajul de programare C
{ write(C,sizeof(Candidat),1,G);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati respinsi
{ //Ordonam alfabetic candidatii respinsi, ordonand fizic fisierul
G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat, nu mai trebuie ordonat
{
do
{ K=0;
fseek(G,0,0);
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((strcmp(C->Nume,C1->Nume)>0) || ((strcmp(C->Nume,C1-Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
{ fseek(G,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1; }

fseek(G,-(long)sizeof(Candidat),1);
}
}
while (K);
G1=fopen("Respinsi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
respinsi, fisier ce se poate tipari la imprimanta }*/
Nr=1;
printf("\nCandidati respinsi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f",Nr,C->Nume,C->Init,C->Prenume,
C->Ob1,C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
254
Limbajul de programare C
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf("");
//Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii respinsi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("\nNu avem candidati respinsi");
}
}
void main()
{ do
{ //clrscr();
printf("\nAlegeti optiunea:");
printf("\n1-Adaugare candidati");
printf("\n2-Afisare sali");
printf("\n3-Introducere note-calcul medie");
printf("\n4-Modificare note gresite-recalcularea mediei");
printf("\n5-Admisi");
printf("\n6-Respinsi");
printf("\n7-Iesire din program");

scanf("%d",&Optiune);
switch (Optiune)
{ case 1 : Adaugare();break;
case 2 : AfisareSali();break;
case 3 : IntroducereNote();break;
case 4 : ModificareDate();break;
case 5 : AfisareAdmisi();break;
case 6 : AfisareRespinsi();break; }
}
while (Optiune!=7);
}
Aplicaia a fost realizat folosind un singur fiier surs. Vom rescrie aplicaia
folosind un proiect. Pentru aceasta vom mpri sursa n dou fiiere :
1. fiierul admitere.cpp, ce va conine programul principal, prezentat n continuare:
#include <stdio.h>
255
Limbajul de programare C
#include <conio.h>
#include "functii.h"
void main()
{ int Optiune;
do
{ //clrscr();
printf("\nAlegeti optiunea:");
printf("\n1-Adaugare candidati");
printf("\n2-Afisare sali");
printf("\n3-Introducere note-calcul medie");
printf("\n4-Modificare note gresite-recalcularea mediei");
printf("\n5-Admisi");
printf("\n6-Respinsi");
printf("\n7-Iesire din program");
scanf("%d",&Optiune);
switch (Optiune)
{ case 1 : Adaugare();break;
case 2 : AfisareSali();break;
case 3 : IntroducereNote();break;
case 4 : ModificareDate();break;
case 5 : AfisareAdmisi();break;
case 6 : AfisareRespinsi();break; }
}
while (Optiune!=7);
}
2. fiierul funcii.cpp, coninnd funciile de prelucrare a datelor despre candidai.
# include <conio.h>
# include <stdio.h>
# include <string.h>
# include <alloc.h>
# include <stdlib.h>
#include "functii.h"
void Adaugare(void)
{FILE *F;
Candidat *C;
char Rasp[2];
F = fopen("candidat.dat","r+b");
if (F==NULL)
F = fopen("candidat.dat","wb");
fseek(F,0,2);
256
Limbajul de programare C
C = (Candidat *)malloc(sizeof(Candidat));
do
{ printf("\n Nume :");
scanf("%s", C->Nume);
printf("\n Initiala tatalui :");
scanf("%s", C->Init);
printf("\n Prenume :");
scanf("%s", C->Prenume);
printf("\n Data nasterii :");
scanf("%s", C->Dn);
fwrite(C,sizeof(Candidat),1,F);
printf("\n Mai aveti candidati de adaugat :");
scanf("%s",Rasp); }
while(stricmp(Rasp,"NU")!=0);
fclose(F);
}
void AfisareSali(void)
{ FILE *F;
FILE *F1;
Candidat *C,*C1;
unsigned K;
int I,Nr;
F=fopen("Candidat.dat","r+b");
F1=fopen("Sali.txt", "w");
//Fisierul text ce va contine lista cu candidatii pe sali
//Ordonam alfabetic candidatii pentru a-i imparti pe sali,
//ordonand fizic fisierul
C = (Candidat *)malloc(sizeof(Candidat));
C1 = (Candidat *)malloc(sizeof(Candidat));
do
{ K=0;
fseek(F,0,0);
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F)) break;
fread(C1,sizeof(Candidat),1,F);
if (feof(F)) break;
if ((strcmp(C->Nume,C1->Nume)>0)||((strcmp(C->Nume,C1->Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
257
Limbajul de programare C
{ fseek(F,-2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,F);
fwrite(C,sizeof(Candidat),1,F);
K=1; }
fseek(F,-(long)sizeof(Candidat),1);
}
}
while (K);
I=1;
Nr=1;
fseek(F,0,0);
fprintf(F1,"\nSala numarul %d",Nr);
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F)) break;
fprintf(F1,"\n%3d%s %s%s",I,C->Nume,C->Init,C->Prenume);
I++;
if (I==21)
{ Nr++;
fprintf(F1,"\nSala numarul :%d",Nr);
I=1; }
}
fclose(F);
fclose(F1);
}
void ModificareDate(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
printf("\nDati numele candidatului pentru care se modifica datele:");
scanf("%s",Nm);
printf("\nDati prenumele candidatului pentru care se modifica datele:");
scanf("%s",Pn);
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((strcmp(Nm,C->Nume)==0)&&(strcmp(C->Prenume,Pn)==0))
{ printf("\nObiect1 : ");scanf("%d",&C->Ob1);
258
Limbajul de programare C
printf("\nObiect2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);break;
}
}
fclose(F);
}
void IntroducereNote(void)
{ FILE *F;
Candidat *C;
char Nm[20],Pn[20];
int Poz;
F=fopen("Candidat.dat","r+b");
C=(Candidat *)malloc(sizeof(Candidat));
Poz=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if (feof(F))break;
printf("\nCandidatul %s %s %s",C->Nume,C->Init,C->Prenume);
printf("\nNota la obiectul 1 : ");scanf("%d",&C->Ob1);
printf("\nNota la obiectul 2 : ");scanf("%d",&C->Ob2);
C->MedieG=(C->Ob1+C->Ob2) /2.0;
fseek(F,-(long)sizeof(Candidat),1);
fwrite(C,sizeof(Candidat),1,F);

Poz++;
fseek(F,(long)Poz*sizeof(Candidat),0);
}
fclose(F);
}
void AfisareAdmisi(void)
{ FILE *F,*G;
FILE *G1;
Candidat *C,*C1;
unsigned K;
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul va contine lista cu admisi
259
Limbajul de programare C
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii admisi in fisierul Admisi.dat
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1>=5) && (C->Ob2>=5))
{ fwrite(C,sizeof(Candidat),1,F);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati
admisi
{ G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat , nu mai trebuie ordonat
{
/*Ordonam descrescator candidatii admisi (adica au peste 5 la fiecare obiect
ordonand fizic fisierul */
do
{ K=0;
fseek(G,0,0);
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);

if (feof(G)) break;
if ((C->MedieG < C1->MedieG)||((C->MedieG==C1->MedieG)&&((strcmp(C->
Nume ,C1->Nume)>0) || ((strcmp(C->Nume,C1->Nume)==0)&&(strcmp(C->Prenume
,C1->Prenume)>0)))))
{ fseek(G,-
2*(long)sizeof(Candidat),1);

fwrite(C1,sizeof(Candidat),1,G);

fwrite(C,sizeof(Candidat),1,G);
K=1;
}
260
Limbajul de programare C
fseek(G,-(long)sizeof(Candidat),1);
}
}
while (K);
}
G1=fopen("Admisi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
admisi, fisier ce se poate tipari la imprimanta */
Nr=1;
printf("\nCandidati admisi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f" ,Nr,C->Nume,C->Init,C->Prenume,C->Ob1
,C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf("");
//Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii admisi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("Nu avem candidati admisi");
}
void AfisareRespinsi(void)
{ FILE *F,*G;
FILE *G1;

Candidat *C,*C1;
unsigned K;
unsigned int I,Nr;
F=fopen("Candidat.dat","rb");
G=fopen("Candidat.dat","wb"); //Fisierul va contine lista cu respinsi
C=(Candidat *)malloc(sizeof(Candidat));
C1=(Candidat *)malloc(sizeof(Candidat));
//Depunem candidatii respinsi in fisierul respinsi.dat
261
Limbajul de programare C
I=0;
while (! feof(F))
{ fread(C,sizeof(Candidat),1,F);
if ((C->Ob1<5) || (C->Ob2<5))
{ fwrite(C,sizeof(Candidat),1,G);
I++; }
}
fclose(F);
fclose(G);
if (I>0) //Se verifica daca au fost candidati
respinsi
{
//Ordonam alfabetic candidatii respinsi, ordonand fizic fisierul
G=fopen("Candidat.dat","r+b");
if (I>1) //Daca este un singur candidat , nu mai trebuie ordonat
{
do
{ K=0;
fseek(G,0,0);
while (! feof(G))
{ read(C,sizeof(Candidat),1,G);
if (feof(G)) break;
fread(C1,sizeof(Candidat),1,G);
if (feof(G)) break;
if ((strcmp(C->Nume,C1->Nume)>0) || ((strcmp(C->Nume,C1->Nume)==0)&&
(strcmp(C->Prenume,C1->Prenume)>0)))
{ fseek(G,-
2*(long)sizeof(Candidat),1);
fwrite(C1,sizeof(Candidat),1,G);
fwrite(C,sizeof(Candidat),1,G);
K=1; }
fseek(G,-(long)sizeof(Candidat),1);
}
}

while (K);
G1=fopen("Respinsi.txt","w");
/*Fisierul text ce va contine lista cu candidatii
respinsi, fisier ce se poate tipari la imprimanta }*/
Nr=1;
262
Limbajul de programare C
printf("\nCandidati respinsi");
while (! feof(G))
{ fread(C,sizeof(Candidat),1,G);
fprintf(G1,"\n%3d %s %s %s %3d %3d %5.2f" ,Nr,C->Nume,C->Init,C->Prenume,C->Ob1,
C->Ob2,C->MedieG);
Nr++;
if (Nr % 61==0)
//Pe o pagina se pot scrie aproximativ 60 de randuri
{ printf("");
//Cod pentru a cere imprimantei sa scoata hartia
printf("Candidatii respinsi :"); }
}
fclose(G);
fclose(G1);
}
else
printf("\nNu avem candidati respinsi");
}
}
3. fiierul cu prototipurile funciilor i definiia tipului de date candidat, fiier numit
funcii.h:
typedef struct Cand { char Nume[20],Prenume[20];
char Init[3];
char Dn[11] ;
int Ob1,Ob2;
float MedieG; } Candidat;
void Adaugare(void);
void AfisareSali(void);
void ModificareDate(void);
void IntroducereNote(void);
void AfisareAdmisi(void);
void AfisareRespinsi(void);
263

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