Вы находитесь на странице: 1из 0

1

INTRODUCERE
Limbajul C este un limbaj de programare general, cu structuri de control !i de date
moderne !i cu un bogat set de operatori. Limbajul C nu este de nivel foarte nalt !i nici nu este
specializat pentru anumite aplica"ii. Absen"a restric"iilor !i generalitatea sa l fac foarte
eficient !i mai utilizat dect multe limbaje de programare mai puternice.
Limbajul C a fost proiectat ini"ial de Dennis Ritchie n anul 1972 !i a fost destinat
pentru scrierea sistemului de operare Unix. Acest sistem de operare, compilatorul C precum !i
cele mai importante aplica"ii snt scrise n C. Limbajul C este adecvat pentru scrierea de
sisteme de operare, compilatoare, editoare de texte !i altele. Cu toate acestea, C poate fi folosit
n orice domeniu, programele scrise n acest limbaj fiind foarte mici !i rapide n raport cu alte
limbaje de programare.
ncepnd cu explozia calculatoarelor personale pe plan mondial, limbajul C a devenit din
ce n ce mai popular, r#spndindu-se dincolo de originile sale ca limbajul sistemului de
operare UNIX. n 1983 a fost stabilit un standard al limbajului, cunoscut !i sub denumirea de
standardul ANSI, care este respectat de majoritatea implement#rilor limbajului pe diferite
tipuri de calculatoare !i sub diverse sisteme de operare. De remarcat faptul c# nu toate
compilatoarele respect# n totalitate standardul ANSI. Pe parcursul lucr#rii poate fi ntlnit# de
multe ori expresia rezultatul este nedefinit dac!. Aceasta nseamn# c# opera"ia care
produce acest rezultat nu respect# standardul ANSI !i rezultatul este dependent de
implementarea limbajului (de compilator).
Principala caracteristic# a limbajului C este faptul c# ofer# mijloace pentru a construi
programe complexe pornind de la elemente simple. Limbajul C este considerat de unii
programatori ca fiind de nivel sc#zut n compara"ie cu alte limbaje ca !i Pascal, Basic, Prolog
etc, deoarece pune la dispozi"ie mecanisme pentru utilizarea direct# a resurselor hardware ale
calculatorului gazd#, cum ar fi utilizarea regi!trilor, adresarea direct# a oric#rei loca"ii de
memorie, utilizarea caracterelor n reprezentarea lor intern# etc. Pe de alt# parte, putem
considera C !i ca un limbaj de programare de nivel nalt, deoarece el are toate structurile de
control care permit scrierea de programe structurate. n plus, spre deosebire de alte limbaje
cum ar fi Pascal, Basic etc., n C se pot scrie programe foarte compacte !i eficiente, care pot
deveni foarte greu de descifrat.
Limbajul C nu ofer# nici o facilitate pentru a trata obiecte compuse cum ar fi !iruri de
caractere, mul"imi sau tablouri, considerate ca un ntreg. Nu este definit# nici o facilitate de
alocare de memorie n afar# de defini"iile statice !i de aloc#rile din stiv#, provenite prin
transmiterea parametrilor !i prin declararea de variabile locale func"iilor. De asemenea, n C
nu exist# facilit#"i de I/O: nu exist# instruc"iuni de tipul READ sau WRITE !i nici metode
interne limbajului pentru accesul fi!ierelor. Toate aceste mecanisme de nivel nalt trebuie
accesate prin apel explicit de func"ii. Cu toate c# absen"a tuturor acestor facilit#"i poate ar#ta
ca o grav# deficien"# (de exemplu pentru a compara dou# !iruri de caractere trebuie s# apel#m
o func"ie), men"inerea limbajului la acest nivel sc#zut are o serie de avantaje importante. Unul
dintre acestea este acela c#, deoarece C este un limbaj relativ mic, el poate fi descris u!or !i
poate fi nv#"at repede.

2
Orice compilator C este nso"it de una sau mai multe biblioteci de func"ii, care ofer#
toate mecanismele pentru a putea scrie programe orict de complexe. De!i limbajul C este
adaptat pe marea majoritate a calculatoarelor existente, el este independent de orice
arhitectur# particular# a unui calculator, !i, cu pu"in# aten"ie, putem scrie u!or programe
portabile, care s# func"ioneze identic pe calculatoare de tipuri diferite !i sub sisteme de
operare diferite, cu modific#ri minime n textul surs# al programelor.
Materialul de fa"# !i propune s# prezinte limbajul C standard, a!a cum este implementat
pe calculatoarele compatibile IBM-PC, f#r# a sublinia n mod deosebit facilit#"ile oferite de un
compilator particular. Anumite facilit#"i ale limbajului snt exemplificate pentru compilatorul
Microsoft Visual C++ versiunea 6.0, n asemenea situa"ii precizndu-se acest lucru n mod
explicit. De men"ionat faptul c# func"iile care fac parte din biblioteca standard a limbajului
snt identice n toate implement#rile limbajului C. Cele mai importante din aceste func"ii snt
descrise n primele 7 capitole. Func"ia dat# mai sus ca exemplu nu face parte din biblioteca
standard ANSI (ci UNIX) !i este dependent# de sistemul de operare. Cu modific#ri minime (a
acelor func"ii care nu respect# standardul ANSI sau Unix), programele se pot adapta pentru
orice implementare a limbajului C.
n carte exist# dou# capitole care se refer# la interfa"a limbajului C cu sistemul de
operare MS DOS !i aspecte ale program#rii n Win32, care pot fi utilizate n Windows NT
4.0, Windows 2000, Windows 95 !i Windows 98. Aceste interfe"e snt foarte asem#n#toare cu
interfa"a cu sistemul de operare UNIX.
O parte din func"iile existente n carte pot fi nlocuite cu func"ii din biblioteca standard a
limbajului. n unele locuri (mai ales la nceput) am preferat scrierea func"iilor n locul
utiliz#rii celor de bibliotec#, n scopul de a ilustra unele aspecte specifice program#rii n C (n
realitate marea majoritate a func"iilor de bibliotec# snt scrise tot n C).
n carte snt incluse un num#r destul de mare de probleme (peste 80!), multe din ele
fiind relativ simple, dar existnd !i un num#r de probleme de o complexitate mai mare. n
general problemele se pot rezolva numai dup# parcurgerea suficient de atent# a materialului
expus. Cititorul care rezolv# singur toate problemele este imposibil s# nu se descurce n orice
mprejurare n limbajul C.
Forma de prezentare a limbajului C poate surprinde, ea bazndu-se pe comentarea unor
programe. Dac# la nceput este prezentat tot programul !i apoi se fac comentariile pe baza lui,
pe m#sur# ce studiul limbajului progreseaz#, snt prezentate dintr-un program doar acele p#r"i
care snt necesare pentru n"elegerea no"iunilor expuse. Astfel programul este alc#tuit din mai
multe fragmente (n general func"ii complete), iar o func"ie care a fost prezentat# anterior este
reluat# numai dac# are modific#ri. Cititorul este invitat s# fac# din toate fragmentele de
program un program ntreg care s# func"ioneze, n acest fel fiind sigur c# a n"eles no"iunile
respective. Pentru a u!ura munca de c#utare a unei func"ii, la sfr!it este inclus un index cu
toate func"iile prezentate.
Cartea se adreseaz# tuturor celor care vor s# se ini"ieze n studiul limbajului C, precum
!i celor care cunosc limbajul, dar poate c# vor g#si n aceast# carte unele aspecte care din
p#cate snt tratate mai superficial n alte c#r"i similare din literatura noastr# de specialitate.
Pentru a putea parcurge cartea, se presupune c# cititorul are cuno!tiin"e de baz# privind
programarea, cum ar fi no"iunile de variabil#, instruc"iuni, cicluri, func"ii, stiv#, recursivitate
etc., precum !i no"iuni despre sistemele de operare MS-DOS, Windows NT 4.0, Windows 95
!i Windows 98 (execu"ia unui program, no"iuni despre fi!iere !i opera"ii care se pot efectua cu
acestea, no"iunea de microprocesor etc.). Este de preferat ca limbajul C s# nu fie primul limbaj
cu care cititorul face cuno!tin"#, deoarece este ceva mai dificil de st#pnit, n sensul c#
dintr-un program C se poate face cu calculatorul aproape orice (chiar !i lucruri mai pu"in
pl#cute). De aceea este recomandat ca nainte de a studia acest# carte, cititorul s# cunoasc#

3
!i un alt limbaj, de preferin"# Pascal (de obicei Pascal este primul limbaj de programare
nv#"at). n carte se fac dese referiri la limbajul Pascal pentru a sublinia diferen"ele fa"# de C
sau pentru a scoate n eviden"# avantajele limbajului C fa"# de Pascal.
Materialul expus n lucrare, cu excep"ia capitolelor referitoare la interfa"a cu sistemele
de operare, pot fi utilizate pentru orice implementare a limbajului C n orice sistem de operare.
Marea majoritate a func"iilor standard de bibliotec# se g#sesc n toate implement#rile
limbajului C. n cazul n care snt utilizate func"ii de bibliotec# specifice unei anumite
implement#ri a limbajului C, acest lucru este precizat (n general am omis utilizarea unor
asemenea func"ii, dar n capitolele referitoare la interfa"a cu sistemele de operare acest lucru
nu a fost posibil).
Oradea, 1999

4
1. INI!IERE N C
n acest capitol vom face o scurt# incursiune n C, ncercnd s# ar#t#m elementele
esen"iale ale limbajului n programe reale, dar f#r# a intra prea mult n am#nunte. n capitolele
urm#toare vom relua anumite lucruri din acest capitol, dar mai n detaliu !i cu explica"iile
necesare.
1.1 Primii pa"i
Cel mai sigur mod de a nv#"a un nou limbaj de programare este de a scrie programe n
acel limbaj. Din aceast# cauz#, prezentarea limbajului C o vom face prin exemplificarea
structurilor sintactice ale limbajului n programe concrete.
Primul program pe care l vom scrie este acela!i n toate limbajele !i constituie piatra
de ncercare n nv#"area unui nou limbaj:
Tip!ri"i cuvintele
Hello, world
Pentru a dep#!i acest obstacol trebuie s# fim capabili s# scriem textul programului, s#
compil#m programul cu succes, s# l lans#m n execu"ie !i s# vedem cum va ar#ta rezultatul
execu"iei programului. Pentru a face lucrarea independent# de implement#rile limbajului C, nu
vom face nici o referire la modul de utilizare a vreunui compilator C. Pentru a putea utiliza un
anumit compilator cititorul trebuie s# studieze documenta"ia de utilizare aferent#. Dac# a"i
realizat toate aceste opera"ii cu succes, atunci snte"i pe drumul cel bun, iar nv#"area
limbajului C nu mai constituie o greutate.
n C programul pentru a tip#ri aceste cuvinte arat# astfel:
#include <stdio.h>
void main()
{
printf(Hello, world\n);
}
n continuare vom da cteva explica"ii despre cum func"ioneaz# acest program. Un
program C, indiferent de m#rimea sa, const# din una sau mai multe func"ii care specific#
opera"iile pe care trebuie s# le execute. Func"iile n C snt similare cu func"iile din alte limbaje
de programare, ca de exemplu Pascal. n exemplul nostru, main este o astfel de func"ie. Avem
posibilitatea de a da unei func"ii ce nume dorim, dar main este un nume special.
Orice program C trebuie s! con"in! func"ia main, deoarece execu"ia programului
const! n execu"ia acestei func"ii .

5
Func"ia main comunic# cu alte func"ii pentru a putea realiza ceea ce trebuie s# fac#.
Aceste func"ii snt scrise de programator sau snt func"ii de bibliotec#. Prima linie din
program,
#include <stdio.h>
informeaz# compilatorul c# trebuie incluse n program informa"ii despre biblioteca standard
de I/O. Aceast# linie apare la nceputul majorit#"ii programelor C. Vom reveni cu informa"ii
suplimentare despre biblioteca standard de I/O n Intr#ri !i ie!iri.
O metod# de comunicare ntre func"ii este comunicarea prin argumente. Parantezele
care urmeaz# dup# numele func"iei cuprind lista argumentelor. n acest exemplu func"ia main
nu are nici un parametru, dar parantezele snt obligatorii, chiar dac# nu avem nici un
argument. Cuvntul void care apare naintea numelui func"iei semnific# faptul c# func"ia nu
returneaz# nici o valoare.
Acoladele cuprind corpul func"iei. Ele snt analogul cuvintelor cheie din Pascal BEGIN
(pentru { ) !i respectiv END (pentru } ). O func"ie este apelat# prin numele s#u, urmat
obligatoriu de paranteze, chiar dac# func"ia nu are argumente. Func"ia main con"ine doar o
singur# instruc"iune,
printf(Hello, world\n);
O func"ie este apelat# punnd dup# numele ei lista de argumente n paranteze, a!a cum
este ilustrat aici apelul func"iei printf cu argumentul Hello, world\n. Func"ia printf este o
func"ie de bibliotec# !i afi!eaz# argumentele pe ecran.
O secven"# alc#tuit# dintr-un num#r oarecare de caractere cuprinse ntre ghilimele
formeaz# un #ir de caractere sau o constant! #ir. Secven"a \n este echivalentul lui WRITELN
din Pascal !i semnific# trecerea la urm#torul rnd pe ecran. Acesta este unicul mod de a trece
la o nou# linie pe ecran realizat de func"ia printf.
Programul
#include <stdio.h>
void main()
{
printf(Hello, );
printf(world);
printf(\n);
}
produce un rezultat identic. De notat c# \n reprezint# un singur caracter.
1.2 Variabile "i expresii aritmetice
n continuare ne vom ocupa despre declararea de variabile !i efectuarea de calcule
matematice simple. Programul urm#tor tip#re!te tabelul cu temperaturile Fahrenheit !i
echivalentul lor n grade Celsius, utiliznd formula C=(5/9)(F-32).
#include <stdio.h>
/ tipareste tabela FahrenheitCelsius
pentru f=0,20,40,...,300 /
void main()
{
int lower,upper,step;

6
float fahr,celsius;
lower=0; /limita de jos a tabelei de temperaturi/
upper=300; /limita de sus a tabelei de temperaturi /
step=20; /marimea pasului/
fahr=lower;
while(fahr<=upper)
{
celsius=5(fahr32)/9;
printf(%4.0f\t%6.$f\n,fahr,celsius);
fahr=fahr+step;
}
}
Liniile
/ tipareste tabela FahrenheitCelsius
pentru f=0,20,40,...,300 /
constituie un comentariu, care n acest caz explic# pe scurt ce face programul. Orice caractere
cuprinse ntre / !i / snt ignorate de compilator !i formeaz# un comentariu, care poate apare
oriunde n program unde poate fi un spa"iu sau o linie nou# (newline). De men"ionat c# nu
putem avea comentarii incluse (nested) n alte comentarii.
n C toate variabilele trebuie declarate nainte de a fi utilizate (de regul# la nceputul
func"iei), naintea instruc"iunilor executabile. Lipsa declara"iei unei variabile produce eroare la
compilare. O declara"ie const# dintr-un tip !i o list! de variabile care au acest tip, ca !i n
int lower,upper,step;
float fahr,celsius;
Tipul int se folose!te pentru variabile ntregi, iar tipul float pentru variabile reale.
Limbajul C furnizeaz# !i alte cteva tipuri de baz# pe lng# int !i float, a c#ror m#rime este
dependent# de calculator:
char Caracter
short ntreg scurt
long ntreg lung
double numere reale n dubl# precizie
Calculul conversiei de temperatur# ncepe cu urm#toarele instruc"iuni de asignare:
lower=0;
upper=300;
step=20;
fahr=lower;
care atribuie variabilelor valorile de start. Instruc"iunile individuale se termin# cu ;.
Fiecare linie din tabel este calculat# n acela!i mod, de aceea utiliz#m o bucl# care se
repet# o dat# pentru fiecare linie. Aceast# bucl# este realizat# cu ajutorul instruc"iunii while:
while(fahr<=upper)
{
...
}
Corpul buclei while poate fi alc#tuit din una sau mai multe instruc"iuni cuprinse ntre
acolade, sau dintr-o singur# instruc"iune f#r# acolade, ca n

7
while(i<j)
i=2i;
Partea important# a calcului este dat# n corpul buclei. Temperaturile Celsius snt
calculate !i asignate variabilei celsius n instruc"iunea
celsius=5(fahr32)/9;
Motivul pentru care mai nti am realizat nmul"irea cu 5 !i numai la sfr!it am mp#r"it
cu 9 este acela c# n C mp!r"irea ntregilor trunchiaz! zecimalele, astfel c# 5/9 este
echivalent cu 5 DIV 9 din Pascal, deci rezultatul ar fi fost zero.
Exemplul acesta ne arat# un pic mai mult despre cum lucreaz# printf. Func"ia printf este
o func"ie de I/O care realizeaz# conversii !i ie!iri cu format !i va fi descris# complet n Intr#ri
!i ie!iri. Primul argument al func"iei printf este un !ir de caractere care va fi tip#rit, fiecare
semn % indicnd faptul c# unul din caracterele urm#toare va fi substituit !i sub ce form# va fi
tip#rit. De exemplu, n instruc"iunea
printf(%4.0f\t%6.$f\n,fahr,celsius);
specificatorul de conversie %4.0f spune c# va fi tip#rit un num#r flotant ntr-un spa"iu de cel
pu"in 4 caractere, f#r# cifre dup# punctul zecimal. %6.1f arat# c# urm#torul num#r va fi tip#rit
pe cel pu"in 6 caractere, cu o cifr# dup# punctul zecimal, iar numerele vor fi desp#r"ite de un
tab (\t). Pot fi omise p#r"i din aceste specifica"ii: %6f spune c# num#rul va avea cel pu"in 6
caractere l#"ime; %.2f necesit# dou# pozi"ii dup# punctul zecimal, dar lungimea nu este
determinat#; %f spune doar c# num#rul trebuie tip#rit ca un num#r flotant. Func"ia printf
recunoa!te !i al"i specificatori de conversie, ca de exemplu %d pentru ntregi zecimali, %x
pentru numere hexazecimale, %c pentru un caracter, %s pentru un !ir de caractere etc. n
Intr#ri !i ie!iri vom prezenta to"i specificatorii de conversie standard recunoscu"i de func"ia
printf.
Fiecare construc"ie % din primul argument este mperecheat# cu corespondentul s#u din
al doilea, al treilea, argument. Ele trebuie s# corespund# ca !i num!r !i tip, altfel vom
ob"ine rezultate nea!teptate.
Func"ia printf nu face parte din limbajul C. Nu exist# intr#ri sau ie!iri definite n
limbajul C nsu!i. printf este doar o func"ie care face parte din biblioteca standard a
limbajului. n scopul de a ne concentra mai mult asupra limbajului, vom vorbi despre intr#ri !i
ie!iri pe larg n Intr#ri !i ie!iri.
Exerci#iul 1. Scrie"i un program care s# afi!eze tabela cu temperaturile Celsius !i
echivalentul lor Fahrenheit.
1.2.1 Instruc#iunea for
Exist# o mul"ime de variante n care putem scrie un program. Iat# mai jos o alt# variant#
pentru conversia de temperatur#:
#include <stdio.h>
void main()
{
int fahr;
for(fahr=0;fahr<=300;fahr=fahr+20)
printf(%4d\t%6.$f\n,fahr,(5.0/9.0)(fahr32));

8
}
Acest program produce acela!i r#spuns, dar arat# altfel. O schimbare esen"ial# este
nl#turarea majorit#"ii variabilelor. A r#mas numai variabila fahr de tip int. Limita inferioar#
!i superioar#, precum !i m#rimea pasului, apar doar ca ni!te constante n instruc"iunea for, ea
ns#!i o nou# construc"ie, iar expresia care calculeaz# temperaturile Celsius apare ca al treilea
argument al func"iei printf, n loc de o instruc"iune separat# de asignare.
Aceast# ultim# schimbare este un exemplu al unei reguli generale n limbajul C:
n orice context unde este permis! utilizarea valorii unei variabile de un tip oarecare,
putem utiliza o expresie de acel tip.
Astfel, al treilea argument al func"iei printf trebuie s# fie un num#r flotant, deci n locul
s#u se poate utiliza orice expresie care produce un rezultat flotant.
Instruc"iunea for este un ciclu, mai precis o generalizare a lui while. Ea con"ine trei
p#r"i separate de ;. Prima parte este cea de ini"ializare:
fahr=0
!i este executat# o singur# dat#, nainte de intrarea n ciclu. Partea a doua este testul sau
condi"ia care controleaz! ciclul:
fahr<=300
Aceast# condi"ie este evaluat#. Dac# este adev#rat#, corpul ciclului este executat (aici
doar printf). Apoi este efectuat pasul de reini"ializare
fahr=fahr+20
!i condi"ia este reevaluat#. Ciclul se termin# cnd condi"ia devine fals#. Ca !i la while, corpul
ciclului poate fi format dintr-o singur# instruc"iune sau dintr-un grup de instruc"iuni cuprinse
ntre acolade. Ini"ializarea #i reini"ializarea pot fi orice expresie.
Exerci#iul 2. Modifica"i programul de conversie al temperaturilor tip#rind tabela n
ordine invers#, de la 300 de grade la 0.
1.2.2 Constante simbolice
nainte de a p#r#si definitiv programul de conversie a temperaturilor, vom face o
observa"ie final#. Este o practic# gre!it# folosirea n program de numere ca 300 sau 20. Ele
exprim# pu"ine informa"ii pentru cineva care ar dori s# citeasc# programul mai trziu, !i de
asemenea snt greu de schimbat n mod sistematic. Limbajul C furnizeaz# un mod de evitare a
folosirii unor astfel de numere, analogul defini"iei CONST din Pascal.
Cu ajutorul construc"iei #define la nceputul programului, putem defini o constant!
simbolic! sau un nume simbolic, care este un !ir particular de caractere. Compilatorul va
nlocui toate apari"iile numelui (neincluse n ghilimele sau ntr-un alt nume) cu !irul de
caractere corespunz#tor. Substituirea pentru nume poate fi de fapt orice text, nu numai un
num#r.
#include <stdio.h>
#define LOWER 0
#define UPPER 300

9
#define STEP 20
void main()
{
int fahr;
for(fahr=LOWER;fahr<=UPPER;fahr=fahr+STEP)
printf(%4d\t%6.$f\n,fahr,(5.0/9.0)(fahr32));
}
Cantit#"ile LOWER, UPPER !i STEP snt constante simbolice, astfel c# ele nu apar n
declara"ii. Constantele simbolice snt scrise de obicei cu majuscule pentru a putea fi distinse
de numele de variabile scrise cu litere mici. Not#m c# nu se pune ; la sfr!itul defini"iei unei
constante simbolice.
1.3 Introducerea "i afi"area caracterelor
n continuare vom prezenta o serie de programe utile pentru a face opera"ii simple
asupra caracterelor. Din exemplele prezentate se va vedea cum pot fi realizate programe
complexe pornind de la elemente simple.
Biblioteca standard a limbajului C furnizeaz# func"ii pentru citirea !i scrierea
caracterelor. Func"ia getchar cite!te urm!torul caracter din intrare de fiecare dat# cnd este
apelat# !i returneaz# acest caracter. Deci, dup# apelul
c=getchar()
variabila c con"ine urm#torul caracter din intrare (de regul# de la tastatur#).
Func"ia putchar este complementul lui getchar, deci apelul
putchar(c)
scrie con"inutul variabilei c (de regul# pe ecran). Nu se poate spune nimic special despre
getchar !i putchar. Ele nu fac parte din limbajul C, dar snt universal disponibile.
1.3.1 Copierea fi"ierelor
Avnd func"iile getchar !i putchar, putem scrie foarte multe programe f#r# a !ti nimic n
plus despre intr#ri !i ie!iri. Cel mai simplu exemplu este un program care copiaz# textul din
intrare n ie!ire, cte un caracter o dat#:
cite#te un caracter
ct timp caracterul citit nu este sfr#itul de fi#ier
scrie caracterul citit
cite#te un nou caracter
Acest program scris n C este
#include <stdio.h>
void main()
{
int c;
c=getchar();
while(c!=EOF)

10
{
putchar(c);
c=getchar();
}
}
Operatorul rela"ional != nseamn# nu este egal cu. Problema principal# este detectarea
sfr!itului introducerii. Prin conven"ie, func"ia getchar returneaz# o valoare care nu este un
caracter valid atunci cnd ntlne!te sfr!itul introducerii (sfr!itul fi!ierului). n acest mod
programul poate detecta cnd a ajuns la sfr!itul datelor. Constanta simbolic# EOF este
definit# n fi!ierul <stdio.h>. Declar#m variabila c de tip int !i nu char, astfel nct ea s# poat#
primi valoarea returnat# de getchar n momentul cnd ntlne!te sfr!itul fi!ierului. n realitate
EOF este de regul# egal cu 1, deci este o valoare ntreag# !i nu de tip char. Este indicat s#
utiliz#m constanta EOF atunci cnd lucr#m cu func"iile getchar !i putchar.
Programul de copiere poate fi n realitate scris mult mai concis, ntr-un mod poate mai
neobi!nuit pentru cineva care cunoa!te alte limbaje de programare. n C, orice asignare, ca !i
c=getchar()
poate fi utilizat! ntr-o expresie. Valoarea sa este valoarea care va fi asignat# p#r"ii din stnga.
Dac# asignarea unui caracter lui c este pus# n interiorul p#r"ii de test al lui while, programul
de copiere a fi!ierelor poate fi scris sub forma
#include <stdio.h>
void main()
{
int c;
while((c=getchar())!=EOF)
putchar(c);
}
Programul cite!te un caracter, l atribuie lui c !i apoi testeaz# dac# acel caracter a fost
sfr!itul fi!ierului. Dac# nu a fost, corpul lui while este executat, tip#rind caracterul, dup# care
while se repet#. Cnd este ntlnit sfr!itul de fi!ier, while se termin# !i de asemenea !i
programul. n aceast# versiune a programului avem un singur apel la getchar, ceea ce duce la
mic!orarea programului !i la u!urarea n"elegerii lui. Acest stil de scriere a unui program este
specific limbajului C !i l vom ntlni foarte des n cele ce urmeaz#.
Introducerea atribuirii n cadrul testului este unul din modurile prin care se ob"in n C
programe foarte concise. Este important s# re"inem c# parantezele care ncadreaz# atribuirea
snt necesare. Preceden"a operatorului != este mai mare dect a lui =, ceea ce nseamn# c# n
absen"a parantezelor testul rela"ional != va fi f#cut naintea asign#rii. Astfel, instruc"iunea
c=getchar()!=EOF
este echivalent# cu
c=(getchar()!=EOF)
Aceast# instruc"iune are un efect nedorit, !i anume setarea lui c pe 0 sau 1, n func"ie de
ntlnirea sfr!itului de fi!ier. Detalii suplimentare vom da n Tipuri de date, operatori !i
expresii.

11
1.3.2 Contorizarea caracterelor
Urm#torul program contorizeaz# caracterele. El este o mic# dezvoltare a programului de
copiere anterior.
#include <stdio.h>
void main()
{
long nc;
nc=0;
while(getchar()!=EOF)
++nc;
printf(%ld\n,nc);
}
Instruc"iunea
++nc;
ne arat# un nou operator, ++, care nseamn# incrementarea cu 1. Putem scrie nc=nc+1, dar
++nc este mult mai concis !i deseori mult mai eficient. Exist# !i un operator corespunz#tor
pentru decrementarea cu 1. Operatorii ++ !i pot fi sau operatori prefix (++nc), sau
operatori postfix (nc++). Aceste dou# forme au diferite valori n expresii, a!a cum vom vedea
n Tipuri de date, operatori !i expresii, dar !i nc++ !i ++nc fac acela!i lucru: incrementeaz# pe
nc cu 1. Pentru moment ne vom mul"umi cu forma prefix.
Pentru a face fa"# unui num#r mare de caractere, putem utiliza nc de tip double n loc de
long, !i de asemenea instruc"iunea for n loc de while, astfel c# noua variant# a programului
arat# ca mai jos.
#include <stdio.h>
void main()
{
double nc;
for(nc=0;getchar()!=EOF;++nc)
;
printf(%.0f\n,nc);
}
Func"ia printf utilizeaz# %f pentru ambele tipuri float !i double. %.0f suprim# tip#rirea
p#r"ii zecimale (care n cazul nostru este inexistent#).
Corpul ciclului for este vid, deoarece toat# treaba este f#cut# n test !i n reini"ializare
(ncerca"i s# face"i acest lucru cu instruc"iunea FOR n Pascal). Sintaxa limbjului C necesit#
ca instruc"iunea for s# aib# un corp, chiar dac# acesta este vid. Caracterul ; izolat, practic o
instruc"iune nul#, este prezent pentru a satisface aceast# necesitate. Am pus acest caracter pe o
linie separat# pentru a face mai vizibil acest lucru.
Observ#m c# dac# la intrare nu avem nici un caracter, testul din while sau for e!ueaz# de
la primul apel al lui getchar, iar programul ne afi!eaz# valoarea zero, ceea ce este corect.
1.3.3 Contorizarea liniilor
Urm#torul program contorizeaz# liniile din intrare. Aceste linii presupunem c# snt
terminate cu caracterul newline (\n) care a fost ad#ugat la fiecare linie scris#.

12
#include <stdio.h>
void main()
{
int c,nl;
nl=0;
while((c=getchar())!=EOF)
if(c==\n)
++nl;
printf(%d\n,nl);
}
Corpul lui while const# acum dintr-un if, care controleaz# incrementarea lui nl.
Instruc"iunea if testeaz# condi"ia din parantez# !i dac# este adev#rat# execut# instruc"iunea
(sau grupul de instruc"iuni din acolade) care urmeaz#.
Semnul == este nota"ia C a operatorului logic de egalitate, analog cu = din Pascal. Acest
simbol este utilizat pentru a distinge testul de egalitate de operatorul = == = utilizat pentru atribuire.
Orice caracter singular poate fi scris ntre apostroafe pentru a produce o valoare egal# cu
valoarea numeric# a acelui caracter din setul de caractere al calculatorului. Un astfel de
caracter se nume!te constant! caracter. De exemplu, A este o constant# caracter a c#rei
valoare n setul de caractere ASCII este 65 (reprezentarea intern# a caracterului A).
Binen"eles c# este de preferat utilizarea constantei A n loc de 65, deoarece n primul rnd
este mai u!or de n"eles, iar n al doilea rnd aceast# valoare este independent# de setul de
caractere al calculatorului.
Secven"a escape utilizat# n !irurile de caractere este de asemenea legal# n constantele
caracter, deci n teste !i expresii aritmetice \n reprezint# valoarea caracterului newline.
Trebuie s# fim aten"i c# \n este un singur caracter !i n expresii este echivalent cu un ntreg,
iar \n este un !ir de caractere care con"ine un singur caracter. Diferen"ele dintre caractere !i
!irurile de caractere le vom discuta pe larg n Tipuri de date, operatori !i expresii.
Exerci#iul 3. Scrie"i un program care contorizeaz# spa"iile, caracterele tab !i caracterele
newline. Caracterul tab se reprezinta prin secven"a escape \t.

Exerci#iul 4. Scrie"i un program care copiaz# intrarea n ie!ire, nlocuind fiecare !ir de
unul sau mai multe spa"ii cu un singur spa"iu.

Exerci#iul 5. Scrie"i un program care copiaz# intrarea n ie!ire, nlocuind fiecare tab cu
\t !i fiecare backslash cu \\.
1.3.4 Contorizarea cuvintelor
Un alt program util este descris n continuare. El contorizeaz# liniile, cuvintele !i
caracterele. Prin cuvnt n"elegem orice secven"# de caractere care nu con"ine spa"iu, tab !i
newline.
#include <stdio.h>
#define DA $
#define NU 0

13
void main()
{
int c,nl,nw,nc,inword;
inword=NU;
nl=nw=nc=0;
while((c=getchar())!=EOF)
{
++nc;
if(c==\n)
++nl;
if(c== ||c==\n||c==\t)
inword=NU;
else if(inword==NU)
{
inword=DA;
++nw;
}
}
printf(%d %d %d\n,nl,nw,nc);
}
De fiecare dat# cnd programul ntlne!te primul caracter al unui cuvnt, el l
contorizeaz#. Variabila inword nregistreaz# cnd programul este n interiorul unui cuvnt sau
nu; ini"ial nu este n interiorul unui cuvnt, deci are valoarea NU. Vom prefera folosirea
constantelor simbolice DA !i NU n locul lui 0 !i 1, deoarece programul este mai u!or de
n"eles.
Linia
nl=nw=nc=0;
seteaz# toate trei variabilele pe zero. Acesta nu este un caz special, ci o consecin"# a faptului
c# o asignare are o valoare !i asign#rile se asociaz# de la dreapta la stnga. n realitate linia
poate fi scris#
nl=(nw=(nc=0));
Operatorul || este echivalentul lui OR din Pascal. Exist# !i operatorul corespunz#tor &&
pentru AND. Expresiile legate prin || sau && snt evaluate de la stnga la dreapta !i este
garantat c! evaluarea se opre#te imediat ce se #tie dac! expresia este fals! sau adev!rat!. De
exemplu, dac# c con"ine un spa"iu, nu mai este nevoie de a testa dac# el con"ine un caracter
newline sau tab, astfel c# aceste teste nu mai snt efectuate.
Exemplul ilustreaz# de asemenea folosirea ramurii else, care specific# o alternativ# care
va fi executat# dac# testul din if este fals.
Exerci#iul 6. Scrie"i un program care copiaz# intrarea n ie!ire, cte un cuvnt pe un rnd.
1.4 Tablouri
n continuare vom scrie un program pentru a num#ra apari"iile fiec#rei cifre, al spa"iilor
albe (spa"iu, tab !i newline, cunoscute !i sub numele de white spaces), precum !i a tuturor

14
celorlalte caractere. Acest program este artificial, dar ne permite s# ilustr#m !i alte aspecte ale
program#rii n C.
Exist# 12 categorii de intr#ri, deci este convenabil s# utiliz#m un tablou pentru a re"ine
num#rul de apari"ii ale fiec#rei cifre, n loc de a utiliza variabile individuale. Mai jos d#m o
versiune a programului.
#include <stdio.h>
void main()
{
int c,i,nwhite,nother;
int ndigit[$0];
nwhite=nother=0;
for(i=0;i<$0;++i)
ndigit[i]=0;
while((c=getchar())!=EOF)
if(c>=0&&c<=9)
++ndigit[c0];
else if(c== ||c==\n||c==\t)
++nwhite;
else
++nother;
printf(cifre=);
for(i=0;i<$0;++i)
printf( %d,ndigit[i]);
printf(\nspatii=%d, alte caractere=%d,nwhite,nother);
}
Declara"ia
int ndigit[$0];
declar# ndigit ca fiind un tablou de 10 ntregi. Indicii de tablou ncep n C ntotdeauna de la
zero, deci elementele sale snt ndigit[0], ndigit[$],, ndigit[9].
Un indice poate fi orice expresie ntreag#, care con"ine variabile !i constante. Acest
program se bazeaz# pe proprietatea reprezent#rii cifrelor ca !i caractere. De exemplu, testul
if(c>=0&&c<=9)
determin# cnd caracterul din c este cifr#. Dac# c este cifr#, atunci valoarea numeric# a acestei
cifre este
c0
Prin defini"ie, opera"iile aritmetice n care intervin date de tip char !i int convertesc
ntotdeauna datele char la un int nainte de a fi executate, astfel c# variabilele char snt
identice cu cele int n context aritmetic. Acest lucru este natural !i convenabil. De exemplu,
c 0 este o expresie ntreag# cu o valoare ntre 0 !i 9 memorat# n c, !i este de asemenea !i un
indice valid pentru tabloul ndigit.
Decizia dac# un caracter este cifr#, spa"iu sau altceva este realizat# de secven"a
if(c>=0&&c<=9)
++ndigit[c0];
else if(c== ||c==\n||c==\t)
++nwhite;
else
++nother;

15
Construc"ia
if(expresie)
instruc"iune
1

else if(expresie)
instruc"iune
2

...
else
instruc"iune
n

apare frecvent n programe ca !i o cale de a exprima o decizie multipl#. Binen"eles c# prin
instruc"iune putem n"elege mai multe instruc"iuni cuprinse n acolade. Dac# nici una din
condi"ii nu este satisf#cut#, este executat# instruc"iune
n
de dup# else final dac# exist#. Dac#
else !i instruc"iune
n
finale snt omise, nu se execut# nimic. Num#rul de construc"ii
else if(expresie)
instruc"iune
dintre if ini"ial !i else final este arbitrar.
Exerci#iul 7. Scrie"i un program care afi!eaz# un tabel cu cuvintele din intrare !i
lungimea fiec#ruia.

Exerci#iul 8. Scrie"i un program care afi!eaz# un tabel cu frecven"a caracterelor din
intrare.
1.5 Func#ii
n C o func"ie este echivalent# cu un subprogram din Pascal. O func"ie ne furnizeaz# un
mijloc convenabil !i simplu de a ncapsula unele calcule ntr-un tot unitar, care apoi poate fi
folosit n programe, f#r# s# ne facem griji despre ceea ce se ntmpl# n interiorul ei. Limbajul
C a fost proiectat pentru a utiliza func"iile ct mai u!or, convenabil !i eficient.
Pn# n prezent am utilizat func"ii precum printf, getchar !i putchar, care snt furnizate
mpreun# cu compilatorul. A sosit timpul ca s# scriem propriile noastre func"ii.
Deoarece C nu are un operator exponen"ial (de ridicare la putere), vom ilustra sintaxa
unei func"ii scriind o func"ie power(m,n) care ridic# ntregul m la puterea ntreag# pozitiv# n.
Binen"eles c# aceast# func"ie nu nlocuie!te n totalitate operatorul exponen"ial, ci lucreaz#
doar pentru puteri pozitive a unor numere ntregi mici.
#include <stdio.h>
int power(int m, int n);
main()
{
int i;
for(i=0;i<$0;++i)
printf(%d %d %d\n,i,power(2,i),power(3,i));
return 0;
}

16
int power(int baza, int n)
{
int i,p;
p=$;
for(i=$;i<=n;++i)
p=pbaza;
return p;
}
Defini"ia unei func"ii are forma:
tip nume-func"ie(lista parametri)
{
declara"ii
instruc"iuni
}
Func"iile pot apare n orice ordine, ntr-un singur fi!ier surs# sau n mai multe. Desigur
c# dac# sursa apare n dou# sau mai multe fi!iere, va trebui s# comunic#m compilatorului mai
multe informa"ii, att la compilare, ct !i la editarea leg#turilor. Pentru moment presupunem c#
sursa se afl# ntr-un singur fi!ier.
Func"ia power este apelat# de dou# ori n printf !i de fiecare dat# ea returneaz# o valoare
ntreag#. Nu toate func"iile returneaz# valori ntregi. Vom aborda n detaliu problema
func"iilor n Structura programelor C.
Prima linie a func"iei power,
int power(int baza, int n)
declar# tipul parametrilor !i tipul rezultatului returnat de func"ie. Numele parametrilor snt
locali func"iei !i nu snt accesibili din afara ei (nu pot fi referi"i dintr-o alt# func"ie, nici chiar
din main).
n general no"iunea de parametru este utilizat# pentru o variabil# aflat# n parantezele
din defini"ia unei func"ii, iar no"iunea de argument este utilizat# pentru valoarea cu care este
apelat# func"ia.
Valoarea calculat# de func"ia power este returnat# func"iei main cu ajutorul instruc"iunii
return, care are forma:
return expresie;
Nu ntotdeauna o func"ie returneaz# o valoare. De asemenea func"ia apelant# poate
ignora valoarea returnat# de func"ia apelat#.
Observ#m c# !i n func"ia main avem o instruc"iune return. Acest lucru este posibil,
deoarece main este o func"ie ca oricare alta. Valoare returnat# de main poate fi utilizat# de
programul care a apelat func"ia, adic# de programul care a lansat n execu"ie programul nostru.
n general returnarea valoarii zero nseamn# o terminare f#r# eroare a programului. Este bine
s# comunic#m dintr-un program modul n care s-a terminat acel program, iar dac# a ap#rut
vreo eroare, valoarea returnat# poate reflecta tipul erorii. Dup# cum se poate observa, n
programele anterioare nu am returnat o valoare din func"ia main. Standardul ANSI precizeaz#
faptul c# func"ia main trebuie s# returneze o valoare ntreag!, deoarece valoarea returnat# este
recep"ionat# de sistemul de operare. De aceea, n cele ce urmeaz# func"ia main va returna o
valoare ntreag# !i nu va mai fi declarat# de tip void.
Declara"ia
int power(int m,int n);

17
care este imediat naintea lui main, spune c# power este o func"ie care are dou# argumente de
tip int !i returneaz# un int. Aceast# declara"ie se nume!te prototipul func"iei !i ea trebuie s!
fie n concordan"! cu defini"ia func"iei. Dac# nu se respect# aceast# concordan"#, este
semnalat# o eroare la compilare.
n prototipul unei func"ii se pot omite numele parametrilor, astfel c# prototipul func"iei
power poate fi scris !i sub forma
int power(int,int);
Atunci cnd scriem o func"ie trebuie s# "inem cont de urm#toarea regul# a limbajului C:
Argumentele func"iilor snt transmise numai prin valoare, iar func"ia apelat! nu
poate modifica parametrii din func"ia apelant!.
Aceasta nseamn# c# func"iei apelate i se transmit valorile argumentelor n variabile
temporare (de fapt unei func"ii se pot transmite ca parametri nu numai variabile, ci !i
expresii). Apelul prin valoare conduce la programe mai compacte, cu mai pu"ine variabile
auxiliare, deoarece argumentale pot fi tratate ca !i variabile locale ini"ializate. De exemplu,
mai jos avem o versiune a func"iei power care utilizeaz# aceast# facilitate.
int power(int baza,int n)
{
int p;
for(p=$;n>0;n)
p=pbaza;
return p;
}
Parametrul n este folosit ca !i o valoare temporar# !i este decrementat pn# cnd devine
zero. Valoarea luat# de n n interiorul func"iei power nu are nici o influen"# asupra
argumentului cu care a fost apelat# func"ia.
Dac# este necesar, este posibil ca o func"ie s# modifice o variabil#. n acest caz func"ia
trebuie apelat# cu adresa acelei variabile (adic# trebuie transmis un pointer la acea variabil#).
Vom trata acest lucru n detaliu n Pointeri.
n cazul unui tablou, lucrurile stau pu"in diferit. Cnd numele unui tablou este utilizat ca
!i argument, valoarea transmis# func"iei este n realitate adresa de nceput a tabloului.
Elementele de tablou nu se copiaz# n stiv#. Prin indicarea indicelui, func"ia poate accesa #i
modifica orice element al tabloului, iar modificarea f#cut# r!mne !i dup# ce func"ia se
termin#. Acest lucru este ilustrat n paragraful urm#tor.
1.5.1 Tablouri de caractere
Probabil cel mai utilizat tip de tablou n C este tabloul de caractere. Pentru a ilustra
utilizarea tablourilor de caractere !i manipularea lor de c#tre func"ii, vom scrie un program
care cite!te un set de linii !i o tip#re!te pe cea mai lung#. n pseudocod putem schi"a
programul astfel:

18
ct timp mai avem alt! linie
dac! linia e mai lung! dect cea mai lung! linie precedent!
salveaz! linia
salveaz! lungimea ei
tip!re#te linia cea mai lung!
Aceast# secven"# ne arat# clar c# programul se mparte n mod natural n mai multe
p#r"i. O parte cite!te o nou# linie, o alta o testeaz#, o alta o salveaz#, iar restul controleaz#
procesul.
Pentru a u!ura scrierea programului, l vom mp#r"i n mai multe func"ii, corespunz#tor
cu descrierea de mai sus. Vom avea o func"ie getline care va citi o nou# linie. Aceast# func"ie
este o generalizare a lui getchar. Pentru a o putea folosi !i mai trziu, vom ncerca s# o facem
ct mai flexibil#. Func"ia getline va trebui s# returneze cel pu"in un semnal despre posibilul
sfr!it de fi!ier. Cea mai general# proiectare ar trebui s# returneze lungimea liniei sau zero
dac# a fost ntlnit sfr!itul de fi!ier. Zero nu este niciodat# o lungime valid# pentru o linie,
deoarece fiecare linie are cel pu"in un caracter, caracterul newline. Se poate adopta !i
conven"ia ca func"ia s# returneze 1 n cazul n care a fost ntlnit sfr!itul de fi!ier. n aceast#
situa"ie valoarea zero semnific# faptul c# linia con"ine numai caracterul newline !i ea se va
numi linie goal!.
Cnd g#sim o linie mai lung# dect cea mai lung# precedent#, ea trebuie salvat# undeva.
De aceea vom folosi o a doua func"ie, copy, pentru a putea copia noua linie ntr-un loc sigur.
n final vom avea nevoie de programul principal pentru a controla func"iile getline !i copy.
Rezultatul poate fi v#zut n programul care urmeaz#.
#include <stdio.h>
#define MAX $000 /lungimea maxima a unei linii/
int getline(char [],int);
void copy(char [],char []);
main()
{
int len; /lungimea liniei curente/
int max; /lungimea maxima intilnita/
char linie[MAX]; /linia curenta/
char lunga[MAX]; /cea mai lunga linie salvata aici/
max=0;
while((len=getline(linie,MAX))>0)
if(len>max)
{
max=len;
copy(lunga,linie);
}
if(max>0) /a fost o linie/
printf(%s,lunga);
return 0;
}
int getline(char s[],int lim)
{
int c,i;
for(i=0;i<lim$ && (c=getchar())!=EOF && c!=\n;++i)
s[i]=c;

19
if(c==\n)
{
s[i]=c;
++i;
}
s[i]=\0;
return i;
}
void copy(char s[],char t[])
{
int i;
i=0;
while((s[i]=t[i])!=\0)
++i;
}
Func"iile main !i getline comunic# ntre ele prin dou# argumente !i printr-o valoare
returnat#. n func"ia getline argumentele snt declarate de linia
int getline(char s[],int lim);
care specific# faptul c# primul argument este un tablou de caractere, iar al doilea este un
ntreg. Lungimea tabloului nu este necesar# n getline, deoarece ea este determinat# n main.
De asemenea valoarea returnat# de getline este un int. n astfel de cazuri (cnd valoarea
returnat# este un int), ea poate fi omis#, deoarece int este tipul implicit returnat de o func"ie.
Exist# func"ii care snt utilizate doar pentru a executa ceva, f#r# ca s# fie necesar s#
returneze o valoare. n acest caz tipul returnat de o astfel de func"ie (a!a cum este aici cazul
func"iei copy) este void, care precizeaz# faptul c# func"ia respectiv# nu returneaz# nimic. De
asemenea, dac# o func"ie nu are argumente, atunci n prototipul ei trebuie s# specific#m acest
lucru utiliznd tipul void, defini"ia func"iei putnd s# nu con"in# nimic n paranteze.
Func"ia getline adaug# caracterul \0 (caracterul null, a c#rui valoare este zero) la
sfr!itul tabloului creat, pentru a marca sfr!itul !irului de caractere. Aceast# conven"ie este
utilizat# de asemenea de compilatorul C, !i anume: cnd un !ir constant ca !i
hello\n
este scris ntr-un program C, compilatorul creaz# un tablou de caractere con"innd caracterele
!irului !i l termin# cu \0, astfel c# func"ii ca printf pot detecta sfr!itul:
h e l l o \n \0
Specificatorul de format %s din printf a!teapt# un !ir reprezentat n aceast# form#.
Exerci#iul 9. Scrie"i un program care elimin# spa"iile !i tab-urile de la sfr!itul fiec#rei
linii din intrare !i !terge n ntregime liniile goale.

Exerci#iul 10. Scrie"i o func"ie reverse(s) care inverseaz# caracterele din !irul s.
Utiliza"i aceast# func"ie pentru a scrie un program care inverseaz# liniile din intrare.

20
1.6 Variabile externe
Variabilele din main (linie, lunga etc.) snt private sau locale func"iei main. Deoarece
ele au fost declarate n interiorul lui main, nici o alt# func"ie nu poate avea acces direct la ele.
Acela!i lucru este valabil !i pentru variabilele din alte func"ii. Fiecare variabil# local# dintr-o
func"ie apare numai cnd func"ia este apelat#, !i dispare cnd func"ia se termin#. Acesta este
motivul pentru care astfel de variabile se numesc automatice.
Variabilele automatice nu re"in valoarea de la un apel la altul al unei func"ii #i
trebuie ini"ializate explicit la fiecare apel.
O alternativ# la variabilele automatice este definirea de variabile care snt externe
tuturor func"iilor, adic# variabile globale care pot fi accesibile prin nume oric#rei func"ii.
Aceste variabile re"in valorile atribuite, chiar dup# ce func"ia care le-a dat valori s-a terminat.
O variabil# extern# trebuie s# fie definit! n afara oric#rei func"ii. Cnd este definit# o
variabil# extern#, se aloc# memorie pentru ea. Variabila trebuie de asemenea declarat! n
fiecare func"ie care dore!te s# o acceseze. Acest lucru poate fi f#cut fie explicit printr-o
declara"ie extern, fie implicit prin context.
Pentru a ilustra lucrul cu variabilele externe, vom rescrie programul anterior, folosind
variabilele externe linie, lunga !i max. Acest lucru implic# schimbarea declara"iilor !i
corpurile func"iilor.
#include <stdio.h>
#define MAX $000
int max;
char linie[MAX];
char lunga[MAX];
int getline(void);
void copy(void);
main()
{
int len;
extern int max;
extern char lunga[];
max=0;
while((len=getline())>0)
if(len>max)
{
max=len;
copy();
}
if(max>0)
printf(%s,lunga);
return 0;
}
int getline(void)
{
int c,i;
extern char linie[];
for(i=0;i<MAX$ && (c=getchar())!=EOF && c!=\n;++i)

21
linie[i]=c;
if(c==\n)
{
linie[i]=c;
++i;
}
linie[i]=\0;
return i;
}
void copy(void)
{
int i;
extern char linie[],lunga[];
i=0;
while((lunga[i]=linie[i])!=\0)
++i;
}
Variabilele externe din main (max !i lunga) snt definite prin utilizarea declara"iei
extern naintea tipului variabilei. nainte ca o func"ie s# poat# utiliza o variabil# extern#,
numele variabilei externe trebuie s# fie cunoscut de acea func"ie. Un mod de a face acest lucru
este de a scrie o declara"ie extern n func"ie.
n anumite cazuri declara"ia extern poate fi omis#. Dac# definirea variabilei externe
apare n fi!ierul surs# naintea utiliz#rii ei ntr-o func"ie, atunci nu mai este nevoie de
declara"ia extern n func"ie. n realitate se plaseaz# de obicei toate defini"iile variabilelor
externe la nceputul fi!ierului surs#, iar apoi se omit toate declara"iile extern.
Dac# programul este n mai multe fi!iere surs#, iar o variabil# este definit# ntr-un fi!ier,
atunci n alt fi!ier n care dorim s# utiliz#m acea variabil# avem nevoie de o declara"ie extern
pentru a conecta cele dou# apari"ii ale variabilei.
Trebuie s# remarc#m diferen"a dintre declara"ie !i defini"ie, referitor la o variabil#
extern#:
Defini"ia unei variabile externe se refer! la locul unde variabila este creat! sau i se
aloc! memorie. Declara"ia unei variabile externe se refer! la locul unde se face cunoscut!
natura variabilei, dar nu i se aloc! memorie.

Exerci#iul 11. Scrie"i un program detab care nlocuie!te tab-urile din intrare cu num#rul
coresponz#tor de spa"ii pn# la urm#torul stop tab. Stopii tab se g#sesc n pozi"ii fixe, dup#
fiecare n caractere, ncepnd cu coloana n+1 (implicit n=8).

Exerci#iul 12. Scrie"i un program entab care nlocuie!te spa"iile din intrare cu un num#r
minim de caractere tab !i spa"ii astfel nct s# ob"inem aceea!i aliniere. $ine"i cont de faptul c#
un caracter tab apare n acelea!i pozi"ii fixe, la fel ca n programul detab de mai sus (de obicei
n pozi"iile 9, 17, 25, etc.) !i c# nu orice succesiune de spa"ii se nlocuie!te cu un caracter tab.


22
Exerci#iul 13. Scrie"i un program care sparge liniile prea lungi din intrare n dou# sau
mai multe linii mai scurte dup# ultimul caracter non-spa"iu care apare naintea celui de-al
n-lea caracter. Dac# nu este posibil altfel, desp#r"irea se va face dup# cel de-al n-lea caracter,
iar naintea coloanei specificate nu trebuie s# mai fie caractere spa"iu sau tab.

Exerci#iul 14. Scrie"i un program care elimin# toate comentariile dintr-un program C.
$ine"i cont de !irurile de caractere !i de secven"ele escape. n C nu putem avea un comentariu
inclus ntr-un alt comentariu.

Exerci#iul 15. Scrie"i un program care elimin#, dac# este posibil, toate declara"iile
extern dintr-un program C, punnd toate defini"iile variabilelor externe care apar n acel fi!ier
la nceputul fi!ierului.

Exerci#iul 16. Scrie"i un program care caut# ntr-un program C erori de sintax# simple,
cum ar fi paranteze rotunde, drepte sau acolade nenchise, apostroafe, ghilimele sau
comentarii nenchise. $ine"i cont de secven"ele escape, comentarii !i !iruri de caractere.
1.7 Rezumat
n acest capitol am trecut n revist# principalele facilit#"i ale limbajului C. Chiar dac#
limbajul a fost prezentat destul de sumar, cititorul !i-a putut forma o p#rere despre facilit#"ile
!i eventual lipsurile acestuia. De!i la nceput problemele au fost simple, odat# cu avansarea
prezent#rii limbajului dificultatea acestora cre!te, astfel nct ultimele probleme din acest
capitol pun la ncercare cititorul !i testeaz# destul de temeinic cuno!tiin"ele dobndite pn#
aici. Se poate observa num#rul destul de mare de probleme dedicate acestui capitol.

23
2. TIPURI DE DATE, OPERATORI $I EXPRESII
Variabilele !i constantele snt obiecte fundamentale manipulate ntr-un program.
Declara"iile listeaz# variabilele care vor fi utilizate n program !i stabilesc ce tip au !i eventual
valoarea lor ini"ial#. Operatorii specific# ce este de f#cut cu variabilele !i constantele, iar
expresiile combin# variabilele !i constantele pentru a produce noi valori.
2.1 Numele variabilelor
De!i nu a reie!it pn# acum, exist# cteva restric"ii asupra numelui variabilelor. Numele
snt alc#tuite din litere !i cifre, iar primul caracter trebuie s# fie liter#. Liniu"a de subliniere
(underscore) este considerat# ca !i liter# !i este util# pentru alc#tuirea numelor lungi.
Spre deosebire de alte compilatoare (ca de exemplu Pascal), compilatorul C face
distinc"ie ntre literele mari #i mici, deci V$ #i v$ snt considerate dou! variabile distincte.
Practica tradi"ional# n C este de a utiliza literele mici pentru numele de variabile, iar
numai litere mari pentru constantele simbolice.
Cuvintele cheie ale limbajului C nu pot fi utilizate ca #i nume de variabile #i se scriu
numai cu litere mici.
Standardul limbajului C garanteaz# c# n numele variabilelor interne cel pu"in primele
31 caractere snt semnificative. Pentru numele func"iilor !i al variabilelor externe este bine s#
utiliz#m mai pu"ine caractere, deoarece numele externe pot fi utilizate de alte limbaje asupra
c#rora nu avem control din C. Pentru numele externe standardul garanteaz# c# numai primele
6 caractere snt unice.
2.2 Tipuri de date
n C exist# doar cteva tipuri de date de baz#:
char un singur octet, capabil s# memoreze un caracter din setul
de caractere al calculatorului
int un ntreg, de obicei reflectnd m#rimea natural# a ntregilor
din calculatorul gazd#
float num#r real n simpl# precizie
double num#r real n dubl# precizie

24
n completare exist# un num#r de calificatori care se pot aplica acestor tipuri de baz#.
Calificatorii short !i long pot fi aplica"i tipului int. Ei se refer# la m#rimi diferite ale
ntregilor.
Calificatorii signed !i unsigned se pot aplica tipului char sau oric#rui tip ntreg.
Numerele unsigned se supun regulilor aritmetice modulo 2
n
, unde n este num#rul de bi"i
dintr-un int. Numerele unsigned snt ntotdeauna pozitive. Declara"iile pentru calificatori
arat# astfel:
short int x;
long int y;
unsigned int z;
Cuvntul int poate fi omis n astfel de situa"ii (!i de regul# este omis).
n leg#tur# cu diferen"a dintre tipurile short, int !i long exist# urm#toarele restric"ii:
numerele short !i int trebuie s# aib# cel pu"in 16 bi"i, iar numerele long cel pu"in 32
de bi"i;
un num#r short nu poate fi mai mare dect un int, iar un int nu poate fi mai mare
dect un long.
M#rimea maxim# a tuturor tipurilor numerice se g#se!te n fi!ierele header standard
<limits.h> !i <float.h>, sub forma unor constante simbolice. Con"inutul acestor fi!iere
(m#rimea constantelor) este dependent de implementarea limbajului C, dar programele care
utilizeaz# constante simbolice din aceste fi!iere snt independente de implementare.
2.3 Constante
Constantele ntregi se consider# c# snt octale dac# ncep cu un zero, hexazecimale dac#
ncep cu 0x sau 0X !i zecimale n caz contrar. Constantele lungi snt scrise cu litera l sau L la
sfr!it, ca de exemplu 123L. Dac# un ntreg este prea lung pentru a ncape ntr-un int, el este
considerat automat long. Constantele ntregi f#r# semn snt scrise cu litera u sau U la sfr!it,
iar cele lungi f#r# semn snt scrise cu sufixul ul sau UL.
Pentru constantele reale se poate folosi de asemenea nota"ia !tiin"ific# 123.456e 7 sau
0.12e3. Orice constant# real# este considerat# de tip double dac# nu are un sufix. Sufixul f sau
F indic# o constant# float, iar sufixul l sau L indic# o constant# long double.
O constant! caracter este un ntreg scris ca un singur caracter ntre apostroafe, ca de
exemplu x. Valoarea unei constante caracter este valoarea numeric# a caracterului din setul
de caractere al calculatorului. De exemplu, 48 este valoarea ASCII a caracterului 0 (zero).
Scriind 0 n loc de 48, programul nostru devine independent de setul de caractere folosit pe
un anumit calculator. Constantele caracter particip# n opera"ii numerice exact ca orice alte
numere, de!i ele snt mult mai utilizate n opera"ii de comparare cu alte caractere.
Exist# cteva caractere non-grafice care pot fi reprezentate ca !i constant# caracter
utiliznd o secven"! escape ca \n. Aceste caractere arat# ca !i dou# caractere, dar n realitate
reprezint# un singur caracter. n plus, poate fi reprezentat ca !i o secven"# escape orice
caracter pe un octet astfel:
\ddd
unde ddd snt cifre octale, sau:
\xhh

25
unde hh snt cifre hexazacimale.
De exemplu:
#define ESC \033
#define BELL \x7
Secven"ele escape snt date n tabelul urm#tor.
\a bell \\ backslash
\b backspace \? Semnul ntreb#rii
\f form feed \ apostrof
\n newline \ ghilimele
\r return \ooo num#r octal
\t tab \xhh num#r hexazecimal
\v tab vertical
Caracterul constant \0 reprezint# caracterul cu valoarea zero (sau caracterul null).
Scriem \0 n loc de 0 pentru a eviden"ia faptul c# este vorba de un caracter !i nu de un num#r.
O expresie constant! este o expresie n care se utilizeaz# numai constante. Astfel de
expresii snt evaluate la compilare !i pot s# apar# n orice loc unde poate apare o constant#, ca
n
#define MAX $000
char linie[MAX+$];
O constant! #ir de caractere sau un #ir constant este o secven"# de zero sau mai multe
caractere ncadrate de ghilimele, ca !i n
Eu sint un sir
sau
/ sirul nul /
Ghilimelele nu fac parte din !ir, ele se utilizeaz# numai pentru a-l delimita. Dac# dorim
s# reprezent#m ghilimelele n interiorul !irului, vom folosi secven"a escape \.
n realitate un !ir de caractere constant este un tablou ale c#rui elemente snt caractere.
Compilatorul adaug# automat caracterul nul \0 la sfr!itul fiec#rui asemenea !ir, astfel c#
programele pot g#si convenabil sfr!itul. Prin utilizarea acestei reprezent#ri rezult# c# nu
exist# o limit# real# asupra lungimii !irurilor de caractere, dar programele trebuie s# le
parcurg# complet pentru a le determina lungimea. Memoria fizic# necesar# pentru a memora
un !ir de caractere este cu o loca"ie mai mult dect num#rul de caractere scrise ntre ghilimele.
Func"ia care urmeaz#, strlen, returneaz# lungimea unui !ir de caractere, excluznd
terminatorul \0. Men"ion#m c# aceast# func"ie, mpreun# cu alte func"ii care lucreaz# cu
!iruri, snt declarate n fi!ierul header standard <string.h>. Ele vor fi prezentate n Intr#ri !i
ie!iri.
int strlen(char s[])
{
int i;
i=0;
while(s[i]!=\0)
++i;
return i;
}

26
Exist! o deosebire esen"ial! ntre o constant! caracter #i un #ir de caractere care
con"ine un singur caracter: x este un caracter care ne d! valoarea numeric! a literei x din
setul de caractere, pe cnd x reprezint! un #ir de caractere care con"ine un caracter (litera
x) #i terminatorul \0 .
n afara acestor tipuri de constante exist# !i constanta enumerare. O enumerare este o
list# de valori ntregi constante, ca de exemplu:
enum boolean {FALSE,TRUE};
Primul nume dintr-o enumerare are valoarea 0, urm#torul nume are valoarea 1, !i a!a
mai departe, dac# nu se specific# valori pentru aceste nume. Dac# se specific# valori dar nu
pentru toate numele, numele pentru care nu se precizeaz# valori continu# progresia aritmetic#
de la ultima valoare precizat#. Acest lucru este ilustrat n exemplele urm#toare:
enum spatiu {SPATIU= ,TAB=\t,NEWLINE=\n};
enum luni {IAN=$,FEB,MAR,APR,MAI,IUN,IUL, AUG,SEP,OCT,NOV,DEC};
/ FEB este 2, MAR este 3, etc./
Numele utilizate n enumer#rile dintr-un program trebuie s# fie distincte, pe cnd
valorile nu trebuie s# fie distincte, nici chiar ntr-o aceea!i enumerare.
Enumerarea furnizeaz# o alternativ# pentru directiva #define, care prezint# avantajul c#
valorile pot fi generate de compilator.
2.4 Declara#ii
n C orice variabil! trebuie s! fie declarat! nainte de utilizare.
Anumite declara"ii pot fi f#cute implicit prin context. O declara"ie specific# un tip !i
este urmat# de o list# de una sau mai multe variabile de acest tip, ca !i n
int lower,upper,step;
char c,linie[$000];
Variabilele pot fi declarate n orice mod. Lista de mai sus poate fi scris# !i sub forma:
int lower;
int upper;
int step;
char c;
char linie[$000];
Aceast# form# ocup# mai mult spa"iu, dar este convenabil# pentru a ad#uga comentarii
la fiecare declara"ie, sau pentru modific#ri ulterioare.
Variabilele pot fi de asemenea ini"ializate la declararea lor, de!i exist# cteva restric"ii.
Dac# numele variabilei este urmat de semnul egal !i de o constant#, aceasta serve!te ca !i o
ini"ializare, ca !i n
char backshlash=\\;

27
int i=0;
float eps=$.0e8;
Dac# variabila n cauz# este extern# sau static#, ini"ializarea este f#cut# doar o singur#
dat#, conceptual nainte ca programul s# !i nceap# execu"ia. Variabilele automatice
ini"ializate explicit snt ini"ializate de fiecare dat# cnd func"ia sau blocul care le con"ine snt
activate. n acest caz, ini"ializatorul poate fi orice expresie. Variabilele externe !i statice snt
ini"ializate implicit cu zero, dar este bine s# le ini"ializ#m explicit. Variabilele automatice
pentru care nu exist# o ini"ializare explicit# au o valoare nedefinit#.
n declararea oric#rei variabile putem utiliza calificatorul const, pentru a specifica faptul
c# acea valoarea acelei variabile nu poate fi modificat#. Dac# utiliz#m calificatorul const
pentru un tablou, atunci elementele tabloului nu pot fi modificate.
const double e=2.7$828$82845905;
const char mesaj[]=eroare;
Declararea const poate fi utilizat# !i cu argumente tablou, pentru a indica faptul c#
func"ia nu poate modifica tabloul:
enum boolean {FALSE,TRUE};
Ce se ntmpl# dac# ncerc#m s# modific#m un obiect const depinde de implementarea
limbajului.
2.5 Operatori aritmetici
Operatorii aritmetici binari snt +, , , / !i % (operatorul modulo). mp#r"irea ntreag#
trunchiaz# orice parte frac"ionar#, dar modul n care se realizeaz# acest lucru n cazul
numerelor negative este dependent de implementarea limbajului. Expresia
x%y
ne d# restul mp#r"irii ntregi a lui x la y !i este zero cnd y divide pe x. De exemplu, un an este
bisect dac# este divizibil cu 4 dar nu este divizibil cu 100, exceptnd anii divizibili cu 400,
care snt bisec"i (de exemplu, anul 1900 nu este bisect, dar anul 2000 este bisect). Astfel:
if(an%4==0 && an%$00!=0 || an%400==0)
printf(%d este an bisect\n,an);
else
printf(%d nu este an bisect\n,an);
Operatorul % %% % nu poate fi aplicat pentru nu num#r float sau double. Preceden"a tuturor
operatorilor poate fi v#zut# n Preceden"a !i ordinea de evaluare.
2.6 Operatori rela#ionali "i logici
Operatorii rela"ionali snt:
> >= < <=

28
Ace!ti operatori au aceea!i preceden"#. Cu preceden"# imediat inferioar# snt operatorii
rela"ionali:
== !=
Expresiile n care snt folosi"i operatorii logici && !i || snt evaluate de la stnga la
dreapta, iar evaluarea se opre!te imediat ce se cunoa!te dac# rezultatul este adev#rat sau fals.
Multe programe C se bazeaz# pe acest lucru, a!a cum se poate vedea n secven"a care
urmeaz#:
for(i=0;i<lim$ && (c=getchar())!=\n && c!=EOF;i++)
s[i]=c;
nainte de a citi un nou caracter, este necesar n primul rnd s# vedem dac# avem unde s#
l memor#m, deci testul i<lim 1 trebuie s# fie primul, deoarece dac# acest test e!ueaz#, nu
trebuie s# mai citim nici un alt caracter.
Deoarece preceden"a operatorului != este mai mare dect a operatorului de asignare,
trebuie s# utiliz#m paranteze n expresia
(c=getchar())!=\n
pentru a ob"ine rezultatul corect.
Prin defini"ie, valoarea numeric! a unei expresii rela"ionale sau logice este 0 dac!
expresia este fals! #i $ n caz contrar.
Operatorul unar de nega"ie ! converte!te un operand nenul n zero, iar un operand nul n
1. Este mult mai expresiv n unele situa"ii s# utiliz#m acest operator, n locul testului de
egalitate cu zero, a!a cum se poate vedea n secven"a urm#toare:
if(!gata)
n loc de
if(gata==0)
2.7 Conversii de tip
Cnd un operator are operanzi de tipuri diferite, ei snt converti"i la un tip comun
conform unor reguli de conversie, nainte de efectuarea opera"iei. n general, singurele
conversii care se fac automat snt acelea care convertesc un operand mai mic ntr-unul mai
mare, f#r# a pierde din informa"ie, a!a cum este de exemplu conversia unui ntreg ntr-un real
n expresii de tipul f+i. Expresii care nu au sens, ca de exemplu utilizarea unui float ca indice
de tablou, snt interzise. Expresii n care prin conversie se pot pierde informa"ii, ca de
exemplu conversia unui ntreg la o form# mai scurt# sau a unui real ntr-un ntreg, pot genera
mesaje de avertizare, dar nu snt interzise.
Un char se comport# ca un ntreg mic, deci poate fi utilizat n expresii n care este
nevoie de un ntreg. Acest lucru are o serie de avantaje n ceea ce prive!te lucrul cu caractere,

29
a!a cum se poate vedea n func"ia urm#toare, care converte!te un !ir de cifre n num#rul
echivalent.
int atoi(char s[])
{
int i,n;
n=0;
for(i=0;s[i]>=0 && s[i]<=9;i++)
n=$0n+s[i]0;
return n;
}
Un alt exemplu de conversie a unui char n int este dat de func"ia lower, care transform#
o liter# mare n liter# mic#. De men"ionat c# aceast# func"ie poate fi utilizat# numai pentru
setul de caractere ASCII.
int lower(int c)
{
if(c>=A && c<=Z)
return c+aA;
else
return c;
}
n fi!ierul header standard <ctype.h> snt definite mai multe func"ii care fac teste de
conversie !i care snt independente de setul de caractere. De exemplu, func"ia tolower este o
versiune portabil# a func"iei lower de mai sus, iar func"ia isdigit(c) poate fi folosit# pentru a
testa dac# c este cifr#. n cele ce urmeaz# pentru astfel de situa"ii vom utiliza func"iile definite
n <ctype.h>.
Defini"ia limbajului nu specific# dac# variabilele char snt entit#"i cu sau f#r# semn.
Atunci cnd un char este convertit la un int, iar bitul cel mai semnificativ este 1, pe unele
calculatoare vom ob"ine un num#r negativ, iar pe altele vom ob"ine un num#r pozitiv.
Defini"ia limbajului C garanteaz# c# orice caracter din setul standard de caractere nu va fi
niciodat# convertit la un num#r negativ, deci n expresii aceste caractere vor fi entit#"i
pozitive.
Conversiile aritmetice implicite se realizeaz# astfel nct operandul de tip inferior este
convertit la tipul superior nainte de efectuarea calculelor, iar rezultatul va fi de tipul
superior. Regulile dup# care se realizeaz# aceste conversii snt:
Dac# unul din operanzi este de tip long double, cel#lalt operand va fi convertit la
long double.
Altfel, dac# unul din operanzi este de tip double, cel#lalt operand va fi convertit la
double.
Altfel, dac# unul din operanzi este de tip float, cel#lalt operand va fi convertit la
float.
Dac# nici una din condi"iile de mai sus nu snt ndeplinite, vor fi realizate conversiile
ntregi ambilor operanzi de la un tip inferior la unul superior (numite integral
promotions), n modul urm#tor:
Dac# unul din operanzi este de tip unsigned long int, cel#lalt operand va fi
convertit la unsigned long int.
Altfel, dac# unul din operanzi este de tip long int, iar cel#lalt de tip
unsigned int, atunci, dac# long int poate reprezenta toate valorile unui

30
unsigned int, ambii operanzi vor fi converti"i la long int, iar n caz contrar
ambii operanzi vor fi converti"i la unsigned long int. (Un unsigned int poate
s# nu ncap# ntr-un long int n acele implement#ri ale limbajului C n care un
int este egal cu un long).
Altfel, dac# unul din operanzi este de tip long int, cel#lalt operand va fi
convertit la long int.
Altfel, dac# unul din operanzi este de tip unsigned int, cel#lalt operand va fi
convertit la unsigned int.
Altfel, ambii operanzi snt converti"i la int.
Not#m faptul c# ntr-o expresie un float nu este convertit automat ntr-un double. n
general, func"iile matematice definite n <math.h> utilizeaz# tipul double pentru o mai bun#
aproximare a rezultatului.
Regulile de conversie devin mai complicate dac# n expresii avem numai operanzi
ntregi !i intervin !i operanzi de tip unsigned. Problema apare datorit# faptului c# n astfel de
situa"ii compara"iile ntre valori cu semn !i f#r# semn snt dependente de m#rimea ntregilor
pe un anumit calculator.
De exemplu, s# presupunem c# tipul int are 16 bi"i, iar long are 32 de bi"i. Atunci
1L<1U, deoarece num#rul 1U este convertit la tipul signed long, fiind de tip int. Dar
1L>1UL, deoarece num#rul -1L este convertit la tipul unsigned long, ob"inndu-se astfel un
num#r mult mai mare dect 1UL.
Conversiile se efectueaz# !i n instruc"iunile de asignare, valoarea membrului drept fiind
convertit# la tipul membrului stng, care va fi !i tipul rezultatului. n acest mod se poate pierde
informa"ie. De exemplu, dac# f este un float !i i este un int, atunci instruc"iunile f=i !i i=f
cauzeaz# conversii. Conversia lui float n int provoac# trunchierea p#r"ii zecimale.
Referitor la conversiile care se efectueaz# n instruc"iunile de asignare, pot apare unele
capcane. S# consider#m secven"a de mai jos.
int a=$000,b=$000;
long int c=ab;
Care va fi valoarea lui c dup# execu"ia acestei secven"e? Un prim r#spuns este c#
valoarea lui c va fi 10001000=1000000, adic# va fi corect#. Dac# vom analiza cu aten"ie
regulile de conversie de mai sus !i dac# vom "ine cont de ordinea de evaluare, vom vedea c#
lucrurile nu stau chiar a!a. Prima opera"ie care se execut# va fi nmul"irea, iar rezultatul care
se ob"ine (de tip int!) va fi atribuit lui c. Dar rezultatul nmul"irii lui a cu b nu ncape ntr-un
int, deci sau va fi trunchiat, sau opera"ia va produce dep#!ire (acest lucru este dependent de
implementarea limbajului C). Prin urmare, secven"a de mai sus nu este corect#. Secven"a de
mai sus va fi corect# dac# a doua instruc"iune o nlocuim cu
long int c=(long int)ab;
Not#m c# instruc"iunea
long int c=(long int)(ab);
este de asemenea incorect#. De ce?
Deoarece argumentele unei func"ii snt expresii, cnd argumentele snt transmise unei
func"ii au loc de asemenea conversii. n absen"a prototipului func"iei, char !i short snt
convertite la int, iar float la double.

31
Exist# numeroase situa"ii n care o dat# de un anumit tip trebuie convertit# n mod
explicit la un alt tip. Acest lucru se poate realiza cu ajutorul unui operator unar numit cast.
Acest operator se utilizeaz# astfel: n construc"ia
(tip)expresie
expresia este convertit# la tipul din parantez#, conform cu regulile de conversie de mai sus.
Efectul acestei construc"ii este acela!i ca !i n cazul n care expresia este asignat# unei
variabile de tipul precizat n paranteze. De exemplu, func"ia sqrt (al c#rei prototip este n
<math.h>) necesit# ca argumentul cu care este apelat# s# fie de tip double, n caz contrar
rezultatul fiind imprevizibil. Deci dac# aceast# func"ie este apelat# cu un argument ntreg,
atunci trebuie s# folosim o construc"ie cast n felul urm#tor:
sqrt((double)n)
De men"ionat c# aceast# construc"ie cast produce valoarea lui n de tipul precizat,
valoarea lui n r#mnnd neschimbat#, iar func"ia sqrt recep"ioneaz# valoarea rezultat# n urma
aplic#rii operatorului cast. Operatorul cast are aceea!i preceden"# ca !i operatorii unari.
Dac# n prototipul func"iei snt declarate argumentele, atunci n cazul n care func"ia este
apelat# cu parametri de alt tip, are loc conversia automat# a parametrilor la tipul argumentelor,
f#r# a mai necesita utilizarea unui operator cast. Deci, dac# func"ia sqrt are prototipul
double sqrt(double);
(a!a cum este de fapt declarat# n <math.h>), atunci apelul
x=sqrt(2);
converte!te mai nti pe 2 la double, f#r# a fi nevoie s# mai utiliz#m operatorul cast.
Ca un exemplu de utilizare a conversiei explicite, prezent#m mai jos o func"ie care este
un generator de numere pseudo-aleatoare.
unsigned long int next=$;
/se genereaza un intreg pseudoaleator in intervalul 0..32767/
int rand(void)
{
next=next$$035$5245+$2345;
return (unsigned int)(next/65535)%32768;
}
/initializeaza generatorul de numere pseudoaleatoare/
void srand(unsigned int n)
{
next=n;
}
Exerci#iul 17. Scrie"i o func"ie C care converte!te un !ir de caractere reprezentnd un
num#r hexazecimal (care poate include op"ional caracterele 0x sau 0X) n valoarea sa
numeric# echivalent#. Caracterele permise snt cifrele de la 0 la 9, literele de la a la f !i literele
de la A la Z.


32
Exerci#iul 18. Scrie"i o func"ie C care converte!te un num#r ntreg zecimal n valoarea
sa hexazecimal# corespunz#toare, num#rul hexazecimal fiind reprezentat sub forma unui !ir
de caractere precedat de caracterele 0x sau 0X.
2.8 Operatori de incrementare "i decrementare
Pentru incrementarea !i decrementarea variabilelor limbajul C are doi operatori mai
pu"in obi!nui"i. Ace!ti operatori snt ++ pentru incrementare !i pentru decrementare.
Neobi!nuit la ace!ti operatori este faptul c# ambii pot fi utiliza"i sau ca operatori prefix
(naintea variabilei, ca n ++n), sau ca operatori postfix (dup# variabil#, ca n n++). n ambele
cazuri efectul este acela!i, !i anume incrementarea (respectiv decrementarea) variabilei. Dar
o expresie de tipul ++n l incrementeaz# pe n nainte de a-i utiliza valoarea, n timp ce o
expresie de tipul n++ l incrementeaz# pe n dup! ce i utilizeaz# valoarea. Acest lucru
nseamn# c# dac# n este utilizat ntr-o expresie, poate avea importan"# dac# operatorul de
incrementare (sau de decrementare) este prefix sau sufix. De exemplu, dac# n este 5, atunci
x=n++;
atribuie lui x valoarea 5, iar
x=++n;
atribuie lui x valoarea 6. n ambele cazuri n devine 6. n cazul operatorului de decrementare
lucrurile snt analoage. De remarcat faptul c# operatorii de incrementare !i decrementare se
pot aplica numai variabilelor, deci o expresie de tipul
(x+n)++
este ilegal#. n cazul cnd valoarea unei variabile incrementat# sau decrementat# nu este
utilizat# ntr-o expresie, ca n secven"a
if(c==\n)
nl++;
este indiferent care form# a operatorului o utiliz#m. Exist# situa"ii n care este important care
form# a operatorului de incrementare sau decrementare este utilizat#. De exemplu, s#
consider#m func"ia squeeze(s,c), care !terge toate apari"iile caracterului c din !irul s.
void squeeze(char s[],int c)
{
int i,j;
for(i=j=0;s[i]!=\0;i++)
if(s[i]!=c)
s[j++]=s[i];
s[j]=\0;
}
n cazul n care este ntlnit un caracter diferit de c, acesta este copiat la pozi"ia j !i abia
dup# aceea este incrementat j.

33
Ca un alt exemplu, s# consider#m func"ia standard strcat(s,t) (al c#rei antet se g#se!te n
<string.h>), care concateneaz# !irul t la sfr!itul !irului s. Func"ia standard returneaz# un
pointer (vezi Pointeri) la !irul rezultat, pe cnd func"ia descris# mai jos nu returneaz# nici o
valoare. Pentru a func"iona corect, !irul s trebuie s# fie suficient de mare pentru a putea
cuprinde rezultatul.
void strcat(char s[],char t[])
{
int i,j;
i=j=0;
while(s[i]!=\0)
i++;
while((s[i++]=t[j++])!=\0)
;
}
Dup# cum se poate observa, n primul ciclu while operatorul de incrementare nu este
utilizat n partea de test, ci n corpul ciclului. De ce?
Exerci#iul 19. Scrie"i o generalizare a func"iei squeeze(s$,s2) care s# !tearg# toate
caracterele din !irul s$ care apar n !irul s2.

Exerci#iul 20. Scrie"i o generalizare a func"iei squeeze(s$,s2,n) care s# !tearg# primele
n apari"ii a oric#rui caracter din !irul s$ n !irul s2.

Exerci#iul 21. Scrie"i o func"ie any(s$,s2) care returneaz# prima pozi"ie din !irul s$ n
care apare un caracter oarecare din !irul s2.
2.9 Operatori pe bi#i
Pentru opera"ii pe bi"i limbajul C are !ase operatori, care pot fi aplica"i numai
operanzilor de tip ntreg (char, short, int !i long), cu sau f#r# semn (prefixat sau nu de
unsigned).
n cazul operatorilor pe bi"i trebuie f#cut# observa"ia c# pozi"ia bi"ilor dintr-un cuvnt se
numeroteaz# cu 0,1, de la dreapta spre stnga, iar ponderea bi"ilor cre!te tot de la dreapta
spre stnga. De exemplu, n baza 2 num#rul 8 se scrie 1000. Bitul egal cu 1 ocup# pozi"ia 3 !i
n acest num#r are ponderea cea mai mare.
Operatorul AND pe bi"i & este utilizat n special pentru a !terge bi"i. De exemplu
n=n&0$77;
pozi"ioneaz# pe zero to"i bi"ii, cu excep"ia celor 7 de pondere minim#. Mai concret, s#
presupunem c# n=0x1fd2. Scris n baza 2, num#rul n arat# astfel:
n=000$$$$$$$0$00$0
n urma opera"iei de mai sus vom ob"ine

34
n=000000000$0$00$0
Operatorul OR pe bi"i | este utilizat n special pentru a seta bi"i. De exemplu,
n=n|0$77;
pozi"ioneaz# pe unu cei 7 bi"i de pondere minim#, l#snd restul bi"ilor nemodifica"i. Mai
concret, dac# vom considera exemplul numeric de mai sus, n urma opera"iei din instruc"iunea
anterioar# vom ob"ine
n=000$$$$$$$$$$$$$
Trebuie s# facem distinc"ie ntre operatorii pe bi"i & !i | !i operatorii logici && !i ||. De
exemplu, dac# x este 1 !i y este 2, atunci x&y este zero, iar x&&y este 1. De ce?
Operatorul OR exclusiv pe bi"i (^) se aplic# conform tabelului de mai jos.
^ 0 1
0 0 1
1 1 0
Operatorii de deplasare << !i >> realizeaz# deplasarea spre stnga (respectiv spre
dreapta) a operandului stng cu un num#r de bi"i dat de operandul drept, care trebuie s# fie un
num#r ntreg pozitiv. Deplasarea spre stnga cu n bi"i se face cu umplerea ultimilor n bi"i cu
zero, fiind echivalent# cu nmul"irea cu 2
n
. Deplasarea spre dreapta a unei entit#"i f#r# semn se
face ntotdeauna prin umplerea cu zero a bi"ilor cei mai semnificativi elibera"i cu zero. n
cazul unei entit#"i cu semn deplasarea spre dreapta este dependent# de calculator, n sensul c#
bi"ii cei mai semnificativi elibera"i vor fi umplu"i cu zero pe unele calculatoare (deplasare
logic#), sau cu bitul semn pe altele (deplasare aritmetic#).
Operatorul unar ~ nseamn# complementul fa"# de 1 a unui ntreg, adic# fiecare bit din
operand este schimbat cu complementul s#u (0 n 1 !i 1 n 0). De exemplu, expresia
x=x&~077
pune pe zero ultimii !ase bi"i ai lui x. De notat faptul c# aceast# expresie este independent# de
lungimea cuvntului din calculator, pe cnd o expresie de tipul x&0177700 presupune c#
lungimea cuvntului din calculator este de 16 bi"i.
Ca un exemplu de utilizare a operatorilor pe bi"i, s# scriem o func"ie care !terge un bit
de pe pozitia p dintr-un ntreg f#r# semn n, deplasnd bi"ii mai semnificativi pe pozi"ia
vacant#. Pentru a fi mai clar, s# consider#m c# p=3 !i n=215. Scris n baza 2, num#rul n este
n=00000000$$0$0$$$
Prin !tergerea bitului de pe pozi"ia 3 se ob"ine num#rul (binar!)
n=000000000$$0$$$$
Se observ# c# prin !tergerea bitului de pe pozi"ia 3 acest bit dispare !i bi"ii afla"i la
stnga snt spre dreapta cu o pozi"ie. Func"ia care realizeaz# aceast# !tergere este dat# mai jos.

35
unsigned long delete_bit(unsigned long n, int p)
{
return (n>>p+$)<<p | n & ~(~0<<p);
}
Argumentul func"iei !i rezultatul le declar#m de tip unsigned pentru ca deplasarea spre
dreapta s# fie logic# !i nu aritmetic# (s# nu depind# de implementarea limbajului !i de
calculatorul pe care e rulat programul).
Exerci#iul 22. Scrie"i func"ia insert_bit(n,p,x), inversa func"iei delete_bit, care s#
insereze n pozi"ia p din ntregul n un bit dat de x (care poate fi numai 0 sau 1), utiliznd
numai operatori pe bi"i.

Exerci#iul 23. Scrie"i func"ia right_rot(n,p) care realizeaz# deplasarea circular# spre
dreapta a num#rului f#r# semn n cu p pozi"ii, utiliznd numai operatori pe bi"i. Prin deplasare
circular# spre dreapta cu un bit se n"elege c# ultimul bit (cel mai pu"in semnificativ) va fi pus
pe prima pozi"ie (va deveni cel mai semnificativ).

Exerci#iul 24. Scrie"i func"ia left_rot(n,p) care realizeaz# deplasarea circular# spre
stnga a num#rului f#r# semn n cu p pozi"ii, utiliznd numai operatori pe bi"i. Prin deplasare
circular# spre stnga cu un bit se n"elege c# primul bit (cel mai semnificativ) va fi pus pe
ultima pozi"ie (va deveni cel mai pu"in semnificativ).
2.10 Operatori de asignare "i expresii
Expresii n care variabila din membrul stng se repet# imediat n membrul drept, dup#
operator, ca de exemplu
i=i+3
pot fi scrise utiliznd un operator de asignare compus ntr-o form# prescurat# astfel:
i+=3
Marea majoritate a operatorilor binari au un operator de asignare compus op=, unde op
este unul din operatorii:
+ / % << >> & ^ |
Astfel, dac# exp
1
!i exp
2
snt expresii, atunci
exp
$
op= exp
2

este echivalent cu
exp
$
= (exp
$
) op (exp
2
)

36
cu deosebirea c# exp
1
este evaluat# o singur# dat#. De men"ionat ordinea efectu#rii
calculelelor. De exemplu, expresia
x=y+$
este echivalent# cu
x=x(y+$)
Ace!ti operatori de asignare compu!i snt utili n special n expresii mai complexe, n
care evaluarea o singur# dat# a expresiei din stnga conduce la un program mai explicit !i mai
rapid, ca de exemplu:
x[y[i+j]+y[l+m]]+=2
n cele prezentate anterior am v#zut c# instruc"iunea de asignare are o valoare care poate
fi utilizat# n expresii. Acela!i lucru este valabil !i pentru operatorii compu!i, de!i este mai
pu"in utilizat# aceast# facilitate. n astfel de expresii, tipul exprsiei de asignare este tipul
operandului stng, iar valoarea este cea ob"inut# dup# asignare.
2.11 Expresii condi#ionale
Construc"iile de forma
if(a>b)
max=a;
else
max=b;
pot fi scrise utiliznd operatorul ternar ?:. Astfel, ntr-o expresie de tipul
exp
$
? exp
2
: exp
3

exp
1
este evaluat# prima. Dac# este nenul# (adev#rat#), atunci este evaluat# expresia exp
2
!i
acesta este rezultatul expresiei condi"ionale. n caz contrar este evaluat# expresia exp
3
!i
acesta este rezultatul expresiei condi"ionale. Numai una din expresiile exp
2
!i exp
3
este
evaluat#. De exemplu, secven"a de mai sus poate fi scris#
max=(a>b)?a:b;
De notat c# expresia condi"ional# poate fi utilizat# oriunde n program unde poate apare
o expresie. Dac# exp
2
!i exp
3
snt de tipuri diferite, tipul rezultatului este determinat de
regulile de conversie prezentate anterior. Parantezele n jurul primei expresii nu snt necesare,
deoarece operatorul condi"ional are prioritatea foarte mic#. Ele se pun de obicei pentru a vedea
mai u!or partea condi"ional#.

37
2.12 Preceden#a "i ordinea de evaluare
n tabelul urm#tor d#m regulile de preceden"# !i asociativitate a tuturor operatorilor,
inclusiv a acelora care nu au fost nc# prezenta"i. Ace!tia vor fi prezenta"i n capitolele
urm#toare, pe m#sur# ce utilizarea lor devine necesar#.
Operatorul Asociativitatea
( ) [ ] > . de la stnga la dreapta
! !! ! ~ + ++ ++ ++ + + ++ + & (tip)
sizeof
de la dreapta la stnga
/ % de la stnga la dreapta
+ ++ + de la stnga la dreapta
< << << << < > >> >> >> > de la stnga la dreapta
< << < < << <= == = > >> > > >> >= == = de la stnga la dreapta
= == == == = ! !! != == = de la stnga la dreapta
& de la stnga la dreapta
^ de la stnga la dreapta
| de la stnga la dreapta
&& de la stnga la dreapta
|| de la stnga la dreapta
?: de la dreapta la stnga
= == = + ++ += == = = == = = == = /= == = % %% %= == = &= == = ^= == =
|= == = < << << << <= == = > >> >> >> >= == =
de la dreapta la stnga
, de la stnga la dreapta
Operatorii unari +, !i au preceden"a mai mare dect operatorii binari corespunz#tori.
Ca !i n multe alte limbaje, n C nu se specific# ordinea evalu#rii operanzilor !i
operatorilor, cu excep"ia operatorilor &&, ||, ?: !i ,. De exemplu, ntr-o instruc"iune de tipul
x=f()+g();
func"ia f poate fi evaluat# naintea lui g sau invers. Aceasta nseamn# c# dac# una din func"iile
f sau g modific# valori ale unor variabile de care depind altele, valoarea lui x poate depinde de
ordinea de evaluare. Pentru a evita acest lucru se utilizeaz# variabile temporare pentru
p#strarea unor rezultate intermediare.
De asemenea nu este specificat# nici ordinea n care snt evaluate argumentele func"ilor,
astfel c# o instruc"iune de tipul
printf(%d %d\n,++n,power(2,n));
poate produce rezultate diferite pentru diferite compilatoare.
Apelurile de func"ii, instruc"iunile de asignare imbricate, mpreun# cu operatorii de
incrementare !i decrementare produc efecte laterale (side effects), adic# unele variabile snt
modificate ca urmare a evalu#rii unei expresii. n orice expresie care produce efecte laterale,

38
valoarea expresiei poate depinde de ordinea evalu#rii !i de ordinea n care variabilele din
expresie snt actualizate. O situa"ie de acest tip este
a[i]=i++;
O ntrebare care se poat pune n mod firesc este dac#, de exemplu, i=5 naintea
execu"iei acestei instruc"iuni, care element va fi modificat !i ce valoare va avea el dup#
modificare? Astfel de situa"ii ca cea de mai sus snt interpretate diferit de diverse
compilatoare !i pot produce rezultate diferite, n func"ie de arhitectura calculatorului.
n concluzie, scrierea de programe care depind de ordinea evalu#rii expresiilor nu este
recomandat# n nici un limbaj.
2.13 Rezumat
n acest capitol au fost prezentate n detaliu tipurile de date !i operatorii limbajului C,
mpreun# cu regulile de conversie de la un tip la altul. Capitolul a avut mai mult un aspect
teoretic, ceea ce a putut fi remarcat !i prin num#rul relativ mic de probleme existente. Trebuie
remarcat faptul c# operatorii au un rol central ntr-un program C. De aceea preceden"a !i
ordinea de evaluare snt deosebit de importante, de cele mai multe ori corectitudinea unui
program depinznd de folosirea gre!it# a unui operator sau de ignorarea preceden"ei sau a
ordinii de evaluare.
Spre deosebire de alte limbaje, n C o parte din operatori produc !i efecte laterale. De
aceea n programe este bine s# se "in# cont de acestea, deoarece snt situa"ii n care efectele
laterale snt utile, dar n multe cazuri ele pot genera erori foarte greu de depistat.

39
3. INSTRUC!IUNI
n acest capitol snt prezentate instruc"iunile limbajului C. O mare parte din acestea
le-am mai ntlnit n capitolele anterioare. n cele ce urmeaz# lucrurile vor fi reluate, dar mai n
detaliu !i cu noi exemple de programe.
Facem men"iunea c# n mod inten"ionat nu prezent!m instruc"iunea goto !i nu o utiliz#m
n nici un exemplu. Facem acest lucru deoarece orice program poate fi scris f#r# a utiliza
aceast# instruc"iune. Cititorii care doresc s# vad# cum se utilizeaz# instruc"iunea goto pot
consulta bibliografia. Exist# limbaje de programare (ca de exemplu Clipper, FoxPro, dBASE)
n care nici nu exist# o instruc"iune de salt. n aceste limbaje exist# comanda GOTO, dar ea
are cu totul alt scop.
3.1 Instruc#iuni "i blocuri
O expresie de tipul x=0 sau i++ devine instruc"iune atunci cnd este urmat# de caracterul
;, ca n
x=0;
i++;
n C, spre deosebire de alte limbaje precum Pascal, caracterul ; este terminator de
instruc"iune !i nu separator.
Acoladele { !i } snt folosite pentru a delimita o instruc"iune compus! (un bloc). Un
bloc este din punct de vedere sintactic echivalent cu o singur! instruc"iune. Dup# acolada de
terminare a unui bloc nu se pune caracterul ;.
3.2 Instruc#iuni condi#ionale
3.2.1 Instruc#iunea if
Aceast# instruc"iune am ntlnit-o deja n programele prezentate n capitolele anterioare.
O relu#m aici n primul rnd pentru a fi cuprinse toate instruc"iunile ntr-un singur capitol.
Sintaxa acestei instruc"iuni este:
if(expresie)
instruc"iune
1

else
instruc"iune
2


40
unde partea else este op"ional#. Instruc"iunea if se execut# astfel: se evalueaz# expresie. Dac#
este adevarat# (adic# are o valoare nenul#), este executat# instruc"iune
1
, iar n caz contrar este
executat# instruc"iune
2
.
Deoarece instruc"iunea if testeaz# doar valoarea numeric# a unei expresii, este posibil#
utilizarea unor prescurt#ri, una din cele mai des ntlnite fiind
if(expresie)
n loc de
if(expresie!=0)
Partea else este asociat# ntotdeauna cu if anterior care nu are un else !i este n acela!i
bloc. De exemplu, n secven"a
if(n>=0)
for(i=0;i<n;i++)
if(s[i]>0)
return i;
else
printf(n este negativ\n);
else se dore!te a fi asociat cu instruc"iunea if cea mai exterioar#, dar compilatorul l asociaz#
cu instruc"iunea if cea mai interioar#. Pentru a putea face asocierea a!a cum dorim, secven"a
de mai sus trebuie scris# astfel:
if(n>=0)
{
for(i=0;i<n;i++)
if(s[i]>0)
return i;
}
else
printf(n este negativ\n);
De remarcat c# n Pascal !i prima secven"# era corect#, deoarece caracterul ; dup# if
nseamn# c# acel if nu mai are else, deci asocierea se face corect. n C caracterul ; fiind
terminator de instruc"iune, efectul acestuia este diferit fa"# de cel din Pascal. n acest sens s#
remarc#m c# n secven"a
if(a>=b)
max=a;
else
max=b;
dup# max=a trebuie s# punem ; deoarece dup# un if urmeaz# obligatoriu o instruc"iune, iar
max=a
este o expresie, instruc"iunea corespunz#toare fiind
max=a;
3.2.2 Construc#ia else-if
Construc"ia

41
if(expresie)
instruc"iune
1

else if(expresie)
instruc"iune
2

...
else
instruc"iune
n

am ntlnit-o deja n Ini"iere n C ca !i o cale de a exprima o decizie multipl#. Pentru a ilustra
utilizarea acestei construc"ii vom prezenta o func"ie care face c#utarea binar# a unui element
ntr-un tablou (de numere). De men"ionat c# pentru a putea utiliza func"ia n mod corect (adic#
rezultatul c#ut#rii s# fie corect), tabloul trebuie s# fie ordonat cresc#tor. Func"ia poate fi
extins# pentru a face c#utarea !i n tablouri nenumerice (a!a cum vom vedea n capitolele
urm#toare).
int binsearch(int x,int v[],int n)
{
int low=0,high=n$,mid;
while(low<=high)
{
mid=(low+high)/2;
if(x<v[mid])
high=mid$;
else if(x>v[mid])
low=mid+$;
else / Am gasit elementul /
return mid;
}
return $; / Nu am gasit elementul/
}
3.2.3 Instruc#iunea switch
Instruc"iunea switch este o instruc"iune de decizie multipl! care testeaz# dac# valoarea
unei expresii ntregi este egal# cu o constant! ntreag! dintr-o list# de expresii constante !i
ramific# programul n mod corespunz#tor. Sintaxa instruc"iunii este:
switch(expresie)
{
case exp
1
: instruc"iune
1

...
case exp
n
: instruc"iune
n

default: instruc"iune
n+1

}
Pentru a ilustra modul de utilizare a acestei instruc"iuni, vom rescrie programul din
Ini"iere n C de contorizare a cifrelor, spa"iilor !i a celorlalte caractere, utiliznd instruc"iunea
switch n locul lui if.
#include <stdio.h>
main()
{
int c,i,nwhite,nother;
int ndigit[$0];

42
nwhite=nother=0;
for(i=0;i<$0;++i)
ndigit[i]=0;
while((c=getchar())!=EOF)
switch(c)
{
case 0: case $: case 2: case 3: case 4:
case 5: case 6: case 7: case 8: case 9:
++ndigit[c0];
break;
case :
case \n:
case \t:
++nwhite;
break;
default:
++nother;
break;
}
printf(cifre=);
for(i=0;i<$0;++i)
printf( %d,ndigit[i]);
printf(\nspatii=%d, alte caractere=%d,nwhite,nother);
return 0;
}
S# explic#m pe scurt cum se utilizeaz# aceast# instruc"iune. Instruc"iunea switch
evalueaz# expresia ntreag# din paranteze (n acest caz caracterul c) !i compar# valoarea ei cu
toate cazurile. Fiecare caz trebuie s# fie distinct !i etichetat cu un ntreg, o constant! caracter
sau o expresie constant!. Dac# un caz coincide cu valoarea expresiei, execu"ia ncepe cu acel
caz. Cazul etichetat cu default este executat dac# nici unul din celelalte cazuri nu este
satisf#cut. Acest caz este op"ional, iar dac# lipse!te !i nici unul din celelalte cazuri nu este
satisf#cut, nu se execut# nimic. Toate cazurile, inclusiv cel etichetat cu default, pot apare n
orice ordine !i ele servesc numai ca ni!te etichete.
Instruc"iunea break cauzeaz# ie!irea imediat# din switch. Deoarece cazurile servesc doar
ca ni!te etichete, dup# ce codul pentru o etichet# este executat, se trece implicit la execu"ia
codului pentru cazul urm#tor. Din aceast# cauz# trebuie s# utiliz#m break pentru a ie!i for"at
din switch. Instruc"iunea break poate fi utilizat# !i pentru a ie!i for"at dintr-un ciclu while, for
sau do.
Trecerea implicit# de la un caz la altul are dou# aspecte, unul pozitiv !i altul negativ. Ca
latur# pozitiv#, putem avea cazuri multiple pentru o singur# ac"iune (a!a cum reiese din
programul de mai sus). Acest lucru implic# utilizarea obligatorie a lui break pentru a preveni
trecerea implicit# la urm#toarea ac"iune. Ca latur# negativ# remarc#m faptul c#, spre deosebire
de alte limbaje ca Pascal, la instruc"iunea switch nu putem vorbi de cazuri propriu-zise. Din
aceast# cauz#, dac# uit#m s# ie!im for"at din switch la terminarea unui caz, programul va
produce un alt rezultat dect cel dorit !i o astfel de eroare este mai greu de urm#rit.
Exerci#iul 25. Scrie"i o func"ie escape(s,t) care converte!te caracterele newline !i tab n
secven"ele escape \n !i \t corespunz#toare, utiliznd instruc"iunea switch.

43
3.3 Instruc#iuni repetitive
3.3.1 Ciclurile while "i for
Aceste instruc"iuni le-am ntlnit deja n programe. Instruc"iunea while are sintaxa:
while(expresie)
instruc"iune
Execu"ia acestei instruc"iuni se desf#!oar# astfel: este evaluat# expresia din parantez#.
Dac# valoarea ei este nenul#, este executat# instruc"iunea !i expresia este re-evaluat#. Ciclul
continu# pn# cnd expresia devine zero, dup# care execu"ia lui while se termin#.
Instruc"iunea for are sintaxa:
for(exp
$
;exp
2
;exp
3
)
instruc"iune
!i este echivalent! cu secven"a
exp
$
;
while(exp
2
)
{
instruc"iune
exp
3
;
}
cu excep"ia cazului n care este utilizat# instruc"iunea continue, care este descris# n sec"iunea
urm#toare.
Observ#m c#, spre deosebire de alte limbaje cum ar fi Pascal, instruc"iunea for este o
generalizare efectiv! a instruc"iunii while.
Din punct de vedere sintactic, cele trei componente ale ciclului for snt expresii. n mod
normal exp
1
!i exp
3
snt instruc"iuni de asignare sau apeluri de func"ii, iar exp
2
este o expresie
rela"ional#. Oricare din cele trei componente poate lipsi (chiar !i toate trei), dar caracterul ;
care le separ# trebuie s# r#mn#. Dac# exp
2
lipse!te, ea este considerat# ntotdeauna adev#rat#,
astfel c# secven"a
for(;;)
{
...
}
este un ciclu infinit din care trebuie s! ie#im utiliznd alte mecanisme (ca de exemplu break
sau return).
Utilizarea lui while sau for n programe se face n func"ie de situa"ia concret#. Dac# nu
avem partea de ini"ializare (exp
1
) sau reini"ializare (exp
3
), este mai natural# utilizarea lui
while, dar se poate folosi la fel de bine !i for.
Instruc"iunea for este preferabil de utilizat atunci cnd snt prezente toate componentele,
deoarece se pune n eviden"# ciclul, f#cnd o analogie cu instruc"iunea FOR din Pascal.
Analogia nu este perfect#, deoarece ntr-un for din C se pot modifica n ciclu exp
2
!i exp
3
, iar
ciclul for nu este limitat doar la progresii aritmetice. Cu toate aceste generalit#"i, nu este
indicat ca n partea de ini"ializare sau reini"ializare (incrementare) s# fie utilizate alte calcule,
cu excep"ia celor strict legate de ciclu.

44
Ca un alt exemplu legat de utilizarea ciclului for, prezent#m n continuare o alt#
versiune a func"iei atoi, care converte!te un !ir de caractere n valoarea sa numeric#
echivalent#.
#include <ctype.h>
int atoi(char s[])
{
int i,n,semn;
for(i=0;isspace(s[i]);i++)
;
semn=(s[i]==)? $:$;
if(s[i]==+ ||s[i]==)
i++;
for(n=0;isdigit(s[i]);i++)
n=$0n+s[i]0;
return semnn;
}
Pentru conversia !irurilor n numere exist# func"iile mult mai Complexe din biblioteca
standard, strtol !i strtod.
Exerci#iul 26.Scrie"i o func"ie btoi(n,s,b) care converte!te un !ir de caractere
reprezentnd un num#r ntreg n n baza b (b36) n valoarea sa numeric# corespunz#toare. n
particular, btoi(n,s,10) este func"ia atoi prezentat# mai sus. Dac# baza b este mai mare dect
10, utiliza"i literele alfabetului pentru cifrele bazei.
Avantajul p#str#rii centralizate a controlului ciclului for este mult mai evident atunci
cnd avem mai multe cicluri incluse unele n altele. Acest lucru este ilustrat prin exemplul
urm#tor, n care avem o func"ie care implementeaz# metoda de sortare Shell (shellsort) pentru
ordonarea cresc#toare a unui tablou de ntregi.
void shellsort(int v[],int n)
{
int i,j,gap,temp;
for(gap=n/2;gap>0;gap/=2)
for(i=gap;i<n;i++)
for(j=igap;j>=0 && v[j]>v[j+gap];j=gap)
{
temp=v[j];
v[j]=v[j+gap];
v[j+gap]=temp;
}
}
Un operator care are utiliz#ri destul de frecvente ntr-un ciclu for este operatorul virgul#
,. O pereche de expresii separate de virgul# este evaluat# de la stnga la dreapta !i rezultatul
are valoarea !i tipul operandului din dreapta. Prin utilizarea acestui operator este posibil ca
ntr-un for s# utiliz#m mai multe expresii n oricare din cele trei p#r"i. Acest lucru este ilustrat
de func"ia de mai jos, care inverseaz# un !ir de caractere f#r# a utiliza un !ir suplimentar.
#include <string.h>

45
void reverse(char s[])
{
int c,i,j;
for(i=0,j=strlen(s)$;i<j;i++,j)
{
c=s[i];
s[i]=s[j];
s[j]=c;
}
}
Virgulele care separ! argumentele unei func"ii, declararea variabilelor etc., nu snt
operatori virgul! #i nu garanteaz! evaluarea de la stnga la dreapta a expresiilor.
3.3.2 Ciclul do-while
Ciclurile for !i while prezentate pn# acum testeaz# condi"ia de terminare nainte de
intrarea n ciclu. Cel de-al treilea ciclu n C, do while, testeaz# condi"ia de terminare dup! ce
a fost executat corpul ciclului, deci corpul este executat ntotdeauna cel pu"in o dat!. Acest
ciclu are sintaxa
do
instruc"iune
while(expresie)
Spre deosebire de instruc"iunea analoag# REPEATUNTIL din Pascal, ciclul do while
se termin# atunci cnd expresia devine fals#.
Practica scrierii programelor C a demonstrat c# ciclul do while este mult mai pu"in
utilizat dect celelalte dou# cicluri. Exist# ns# unele situa"ii, ca cea ilustrat# n exemplul
urm#tor, n care utilizarea instruc"iunii do while este mai avantajoas#. S# consider#m func"ia
itoa, inversa func"iei atoi, care converte!te un num#r ntr-un !ir de caractere.
void itoa(int n,char s[])
{
int i,semn;
if((semn=n)<0)
n=n;
i=0;
do {
s[i++]=n%$0+0;
} while((n/=$0)>0);
if(semn<0)
s[i++]=;
s[i]=\0;
reverse(s);
}
Deoarece n !irul s avem cel pu"in un caracter, este mai avantajos s# utiliz#m do while.
Chiar dac# corpul ciclului este alc#tuit dintr-o singur# instruc"iune, am utilizat acoladele
pentru a face distinc"ie ntre partea while de la un do while !i ciclul while.

46
Exerci#iul 27. Versiunea func"iei itoa de mai sus nu trateaz# corect cazul celui mai mare
num#r ntreg negativ. Explica"i de ce !i modifica"i func"ia itoa pentru a trata corect !i acest
caz, indiferent de m#rimea ntregilor din calculator.

Exerci#iul 28. Scrie"i o func"ie itob(n,s,b) care converte!te num#rul ntreg n ntr-un !ir
de caractere n baza b (b36). n particular, itob(n,s,16) transform# num#rul n ntr-un !ir de
caractere n baza 16. Dac# baza b este mai mare dect 10, utiliza"i literele alfabetului pentru
cifrele bazei.

Exerci#iul 29. Scrie"i o alt# versiune a func"iei itoa care s# aib# trei argumente, al treilea
argument fiind dimensiunea minim# a cmpului pe care se reprezint# num#rul. Dac# num#rul
convertit are mai pu"ine cifre dect dimensiunea minim#, el este completat n stnga cu spa"ii
(este aliniat la dreapta).
3.4 Instruc#iunile break "i continue
Exist# situa"ii n care este util s# putem ie!i dintr-un ciclu !i n alt mod dect prin
testarea condi"iei de terminare a ciclului. Instruc"iunea break are ca efect ie!irea imediat# din
cel mai interior ciclu for, while sau do, precum !i dintr-o instruc"iune switch care o con"ine.
Ca un exemplu de utilizare a instruc"iunii break, s# consider#m func"ia trim, care
elimin# spa"iile, tab-urile !i newline de la sfr!itul unui !ir de caractere.
int trim(char s[])
{
int n;
for(n=strlen(s)$;n>=0;n)
if(s[n]!= && s[n]!=\t && s[n]!=\n)
break;
s[n+$]=\0;
return n;
}
Este evident c# instruc"iunea break putea fi evitat#, dar n acest caz testul din for s-ar fi
complicat destul de mult !i ar fi condus la un program mai greu de n"eles. Este bine ca n
situa"ii de acest gen s# nu complic#m inutil programul !i s# utiliz#m instruc"iunea break, care
face ca programele s# fie mai compacte !i mai clare.
Instruc"iunea continue este nrudit# cu break, dar este mult mai rar utilizat#. Ea are ca
efect s#rirea peste toate instruc"iunile care urmeaz# !i trecerea la urm#toarea itera"ie a celui
mai interior ciclu for, while sau do care o con"ine. Aceast# instruc"iune nu se aplic#
instruc"iunii switch, ci numai ciclurilor. continue se utilizeaz# n special atunci cnd
instruc"iunile din ciclu care urmeaz# dup# ea snt complexe, iar evitarea lor ar complica foarte
mult programul.
Ca un exemplu de utilizare a instruc"iunii continue s# consider#m func"ia inclus(a,b),
care testeaz# dac# elementele !irului a apar"in !irului b, n ordinea n care ele apar n cele dou#
!iruri.
#define FALSE 0

47
#define TRUE $
int inclus(char a[], char b[])
{
int i,j;
for(i=0,j=0;b[j]!=\0 && a[i]!=\0;j++)
if(a[i]!=b[j])
continue;
else
i++;
return (a[i]!=\0) ? FALSE : TRUE;
}
Exerci#iul 30. Scrie"i o generalizare a func"iei squeeze(s$,s2) care s# !tearg# toate
apari"iile sub!irului s$ n !irul s2.

Exerci#iul 31. Scrie"i o generalizare a func"iei squeeze(s$,s2,n) care s# !tearg# primele
n apari"ii ale sub!irului s$ n !irul s2.

Exerci#iul 32. Scrie"i o generalizare a func"iei squeeze(s$,s2,n) care s# !tearg# ultimele
n apari"ii ale sub!irului s$ n !irul s2.
3.5 Rezumat
n acest capitol au fost prezentate instruc"iunile limbajului C, cu o singur# excep"ie,
instruc"iunea goto. Aceast# instruc"iune nu este necesar# pentru scrierea unui program !i nu
este prezent# n acest material. De!i exist# situa"ii cnd folosirea lui goto ar u!ura scrierea
programului, acestea pot fi evitate prin utilizarea instruc"iunilor existente. De!i marea
majoritate a instruc"iunilor au fost prezentate !i utilizate nc# din primul capitol, aici s-a dat
sintaxa complet# a fiec#rei instruc"iuni, mpreun# cu considera"iile teoretice necesare
n"elegerii !i utiliz#rii corecte a acestora n programe. n acest capitol nu a fost prezentat#
instruc"iunea return, ea fiind subiectul capitolului urm#tor. %i acest capitol a avut mai mult un
aspect teoretic, fapt reflectat de num#rul mic de probleme existent.

48
4. STRUCTURA PROGRAMELOR C
Am v#zut n Ini"iere n C c# un program C este alc#tuit din una sau mai multe func"ii,
dintre care una trebuie s# aib# un nume special, main. De asemenea am men"ionat !i faptul c#
execu"ia unui program C nseamn# execu"ia func"iei main. n cele ce urmeaz# vom prezenta
noi aspecte despre func"iile C !i modul de utilizare al acestora.
Limbajul C a fost proiectat, spre deosebire de alte limbaje, s# lucreze foarte eficient !i
simplu cu func"iile. Programele C constau n general din una sau mai multe func"ii !i se pot
g#si n unul sau mai multe fi!iere surs#. Aceste fi!iere surs# pot fi compilate separat !i legate,
mpreun# cu func"iile de bibliotec#, la editarea leg#turilor. Modul n care se realizeaz#
compilarea !i editarea leg#turilor este specific fiec#rei implement#ri a limbajului pe un anumit
calculator !i acest subiect dep#!e!te cadrul acestei c#r"i, deci nu este prezentat. Oricum, pentru
a lucra cu un anumit compilator, cititorul trebuie s# studieze mai nti modul de utilizare al
acelui compilator, nainte de a trece la scrierea de programe.
Dup# cum am v#zut n Ini"iere n C, fiecare func"ie trebuie s# aib# un prototip. Acest
lucru u!ureaz# sarcina compilatorului !i evit# unele posibile erori n ceea ce prive!te apelul
func"iilor.
Standardul limbajului C precizeaz# regulile de valabilitate ale identificatorilor.
Fiecare obiect extern (variabil! sau func"ie) trebuie s! aib! o singur! defini"ie n
cadrul unui program C.
4.1 Func#ii
4.1.1 No#iuni de baz%
Pentru a exemplifica utilizarea func"iilor, vom scrie un program care tip#re!te fiecare
linie din intrare care con"ine un tipar (pattern) particular (care este un !ir de caractere). De
exemplu, c#utarea tiparului spatiu n liniile
In spatiu este loc
Aici nu este spatiu
Deci aici nu este loc
va afi!a la ie!ire textul
In spatiu este loc
Aici nu este spatiu
Programul poate fi mp#r"it n trei p#r"i, conform schemei de mai jos.

49
ct timp mai avem linii
dac! linia con"ine tiparul
tip!re#te linia
Aceste p#r"i pot fi incluse n func"ia main, dar pentru a face programul mai explicit vom
scrie pentru fiecare parte cte o func"ie. Observ#m c# tip#rirea liniei este deja rezolvat# prin
utilizarea func"iei printf, deci ne mai r#mn numai dou# func"ii de scris.
Testul dac# mai avem alte linii este dat de func"ia getline, pe care am scris-o deja n
Ini"iere n C, iar func"ia strindex(s,t) ne va returna pozi"ia din !irul s unde ncepe !irul t, sau
1 dac# s nu con"ine pe t. Valoarea 1 este utilizat# pentru a semnifica e!ecul opera"iei de
c#utare, deoarece n C tablourile ncep cu pozi"ia zero. De men"ionat faptul c# majoritatea
func"iilor standard care lucreaz# cu !iruri returneaz# o valoare negativ# (de obicei 1) pentru a
semnala e!ecul opera"iei.
Programul care realizeaz# prelucrarea dorit# este dat n continuare.
#include <stdio.h>
#define MAX $000 /lungimea maxima a unei linii/
int getline(char [],int);
int strindex(char [],char []);
char pattern[]=spatiu; /sirul care se cauta/
main()
{
char linie[MAX];
while(getline(linie,MAX)>0)
if(strindex(linie,pattern)>=0)
printf(%s\n,linie);
return 0;
}
int getline(char s[],int lim)
{
int c,i=0;
while(lim>0 && (c=getchar())!=EOF && c!=\n)
s[i++]=c;
if(c==\n)
s[i++]=c;
s[i]=\0;
return i;
}
int strindex(char s[],char t[])
{
int i,j,k;
for(i=0;s[i]!=\0;i++)
{
for(j=i,k=0;t[k]!=\0 && s[j]==t[k];j++,k++)
;
if(k>0 && t[k]==\0)
return i;
}
return $;
}

50
Defini"ia unei func"ii are forma
tip nume-func"ie(lista parametri)
{
declara"ii
instruc"iuni
}
Nu toate aceste p#r"i trebuie s# fie prezente. Cea mai mic# func"ie C care se poate scrie
este
void dummy() { }
care nu face nimic !i nu returneaz# nimic. O astfel de func"ie este util# ntr-un program n
etapa de elaborare a programului. Dac# tipul unei func"ii este omis, se consider# implicit tipul
int. Trebuie remarcat c# dac# o func"ie are un alt tip dect void, atunci ea trebuie s# con"in# cel
pu"in o instruc"iune return n care s# fie precizat# o expresie de acela!i tip cu tipul func"iei, n
caz contrar compilatorul semnalnd eroare.
Un program C este alc#tuit dintr-o mul"ime de defini"ii de variabile !i func"ii, dintre care
trebuie s# existe obligatoriu o (unic!!) func"ie cu numele main. Comunicarea ntre func"ii se
realizeaz# prin argumente, valori returnate de acestea !i prin intermediul variabilelor globale.
Func"iile pot apare n fi!ierul surs# n orice ordine, iar programul surs# poate fi mp#r"it n mai
multe fi!iere surs#, cu observa"ia c# o func"ie nu poate apare dect ntr-un singur fi#ier surs!.
Deoarece mp#r"irea unui program surs# n mai multe fi!iere este dependent# de
implementarea limbajului C, n carte nu am f#cut nici o referire la modul n care se realizeaz#
acest lucru, mp#r"irea f#cndu-se n func"ie de mediul C particular cu care se lucreaz#.
Instruc"iunea return constituie mecanismul prin care func"ia apelat# returneaz# o
valoare func"iei apelante. Sintaxa acestei instruc"iuni este
return expresie;
unde expresie poate fi de orice tip. Dac# este necesar, ea va fi convertit# la tipul func"iei.
Deseori snt utilizate paranteze pentru a ncadra expresia, dar ele snt op"ionale.
Dac# utiliz#m return n func"ia main, valoarea este returnat# sistemului de operare.
Func"ia main este singura func"ie care nu poate fi apelat! de nici o alt! func"ie din
program, nici chiar de ea ns!#i.
Exerci#iul 33. Scrie"i func"ia strrindex(s,t) care s# returneze ultima apari"ie a !irului t n
!irul s, sau 1 n cazul n care t nu este inclus n s.

Exerci#iul 34. Scrie"i func"ia strnindex(s,t,n) care s# returneze primele n apari"ii ale
!irului t n !irul s, sau 1 n cazul n care t nu este inclus n s.

Exerci#iul 35. Scrie"i func"ia strrnindex(s,t,n) care s# returneze ultimele n apari"ii ale
!irului t n !irul s, sau 1 n cazul n care t nu este inclus n s.


51
Exerci#iul 36. Scrie"i func"ia strmindex(s,t,m,n) care s# returneze urm#toarele n apari"ii
ale !irului t n !irul s, ncepnd cu a m-a apari"ie, sau 1 n cazul n care t nu este inclus n s.

Exerci#iul 37. Scrie"i func"ia strcount(s,t) care s# numere toate apari"iile !irului t n !irul
s.
4.1.2 Func#ii care nu returneaz% ntregi
Pn# acum am v#zut doar func"ii care returnau un int sau nu returnau nimic (void).
Exist# multe func"ii (de exemplu func"ii matematice ca sqrt, sin, cos etc.) care returneaz# alt
tip de date (aceste func"ii matematice returneaz# double). Pentru a ilustra cum se scriu
asemenea func"ii, vom extinde func"ia atoi pe care am v#zut-o n capitolele anterioare, scriind
func"ia atof, care converte!te un !ir n valoarea sa n dubl# precizie (deci va returna un
double). Aceast# func"ie nu este versiunea cea mai complet#, ci doar una simplificat#. O
versiune complet# a acestei func"ii exist# n biblioteca standard, iar prototipul ei este declarat
n <math.h>.
Deoarece atof nu returneaz# un int, ea trebuie s# declare obligatoriu tipul rezultatului.
Func"ia atof este descris# mai jos.
double atof(char s[])
{
double val,power;
int i,sign;
for(i=0;isspace(s[i]);i++) /sare spatiile/
;
sign=(s[i]==) ? $ : $;
if(s[i]==+ || s[i]==)
i++;
for(val=0.0;isdigit(s[i]);i++)
val=$0.0val+(s[i]0);
if(s[i]==.)
i++;
for(power=$.0;isdigit(s[i]);i++)
{
val=$0.0val+(s[i]0);
power=$0.0;
}
return signval/power;
}
Func"ia atof trebuie declarat# a!a cum este definit#. Dac# este omis# declara"ia acestei
func"ii sau dac# nu este declarat# conform cu defini"ia, pot apare dou# situa"ii. O prim#
situa"ie (!i cea mai fericit#) este aceea cnd atof !i main (sau func"ia care o apeleaz#) se
g#sesc n acela!i fi!ier surs#. n acest caz compilatorul va detecta eroarea. O a doua situa"ie (!i
cea mai nefericit#) este aceea cnd cele dou# func"ii se g#sesc n fi!iere surs# diferite. n
acest caz eroarea nu mai poate fi detectat# de compilator, func"ia atof returneaz# un double,
iar main (sau func"ia apelant#) va trata aceast# valoare ca un int, iar rezultatul va fi
imprevizibil.
De ce trebuie s# declar#m o func"ie n cazul n care ea returneaz# altceva dect un int?
R#spunsul la aceast# ntrebare l g#sim dac# vom privi mai ndeaproape ce se ntmpl# ntr-o

52
astfel de situa"ie. n cazul n care lipse!te declara"ia unei func"ii, aceasta este declarat!
implicit de prima sa apari"ie ntr-o expresie, ca de exemplu
sum+=atof(linie);
Dac! un nume care nu a fost declarat anterior apare ntr-o expresie #i este urmat de o
parantez! deschis!, el este declarat prin context ca un nume de func"ie.
O astfel de func"ie se presupune c# returneaz# un int, iar despre argumentele func"iei nu
se presupune nimic. Acest lucru se ntmpl# pentru compatibilitate cu primele versiuni ale
limbajului C, dar compilatoarele noi semnaleaz# ca !i o eroare nedeclararea unei func"ii
(aceast# eroare poate fi inhibat#, dar nu este recomandat). Dac# o func"ie are argumente, este
bine ca s# declar#m tipul acestora, n caz contrar vom utiliza void n lista de argumente.
Dac# avem func"ia atof, putem s# o utiliz#m pentru a scrie func"ia atoi:
int atoi(char s[])
{
double atof(char s[]);
return (int) atof(s);
}
Exerci#iul 38.Scrie"i o func"ie btof(n,s,b) care converte!te un !ir de caractere
reprezentnd un num#r real n n baza b (b36) n valoarea sa numeric# corespunz#toare. n
particular, btof(n,s,10) este func"ia atof prezentat# mai sus. Dac# baza b este mai mare dect
10, utiliza"i literele alfabetului pentru cifrele bazei.

Exerci#iul 39. Extinde"i func"ia atof pentru a trata !i nota"ii !tiin"ifice ale numerelor
reale de forma 123.45e 6, n care num#rul poate fi urmat de litera e sau E !i un exponent cu
semn (semnul poate fi op"ional, dac# lipse!te se consider# c# este +).

Exerci#iul 40. Extinde"i func"ia btof pentru a trata !i nota"ii !tiin"ifice ale numerelor
reale n baza b ca la exerci"iul anterior. Exponentul se consider# c# este un num#r n baza 10.
n particular, btof(n,s,10) este func"ia atof de la exerci"iul anterior. Dac# baza b este mai mare
dect 10, utiliza"i literele alfabetului pentru cifrele bazei.
4.2 Variabile externe, statice "i registru
4.2.1 Variabile externe
Un program C este alc#tuit dintr-o mul"ime de obiecte externe, care snt variabile !i
func"ii. Cuvntul extern folosit pentru variabile este utilizat n contrast cu cuvntul intern,
care descrie argumentele func"iilor !i variabilele definite n interiorul acestora. Aceasta
nseamn# c# variabilele externe snt definite n exteriorul oric#rei func"ii !i snt accesibile (cel
pu"in teoretic) din orice func"ie. n C func"iile snt ntotdeauna externe (spre deosebire de

53
Pascal, unde putem avea !i subprograme -proceduri sau func"ii- interne), adic# nu putem
defini o func"ie n interiorul altei func"ii. n mod implicit, variabilele externe !i func"iile au
proprietatea c# toate referirile la acestea, prin acela!i nume, chiar acele referiri care snt n
fi!iere separate, snt referiri la acela!i obiect.
n Ini"iere n C am v#zut unele aspecte ale utiliz#rii variabilelor externe. Vom vedea n
acest capitol !i cum putem avea variabile externe !i func"ii care pot fi accesate numai dintr-un
singur fi!ier surs#.
Deoarece variabilele externe snt accesibile din orice func"ie, ele ne furnizeaz# o
alternativ# de comunica"ie ntre func"ii, n locul argumentelor !i al valorilor de return. Orice
func"ie poate accesa o variabil# extern# prin referirea la numele ei, dac# acel nume a fost
declarat.
Dac# schimbul de informa"ii ntre func"ii se realizeaz# prin intermediul a numeroase
variabile, este mult mai eficient ca acele variabile s# fie externe, n loc de a utiliza o list#
lung# de argumente.
Variabilele externe snt utile datorit# scopului !i timpului lor de via"# (durata lor de
existen"#). Variabilele automate snt interne unei func"ii. Ele apar (snt create) cnd func"ia
este apelat# !i dispar (snt distruse) cnd execu"ia func"iei se termin#. Pe de alt# parte,
variabilele externe snt permanente, adic# apar (snt create) cnd programul este lansat n
execu"ie !i dispar (snt distruse) cnd programul se termin#. Aceasta nseamn# c# ele p#streaz#
valorile n timpul execu"iei programului.
Vom exemplifica lucrul cu variabile externe printr-un program mai complex. Acest
program cite!te o expresie aritmetic# n nota"ie polonez# invers# !i afi!eaz# rezultatul
evalu#rii expresiei. Pentru simplificare, vom considera c# operanzii snt numere reale, iar
operatorii snt +, , !i /. De asemenea consider#m c# expresia nu con"ine func"ii
(matematice). n capitolele urm#toare vom exemplifica prin alte programe lucrul cu expresii
aritmetice.
n nota"ie normal# (infix) operatorul este ntre cei doi operanzi. n nota"ie polonez#
invers# operatorul se g#se!te dup# operanzii asupra c#rora este aplicat, n a!a fel nct nu mai
este nevoie de paranteze. De exemplu, expresia
$(2+3)4
scris# n nota"ie infix, se transcrie n nota"ie polonez# invers# astfel:
$ 2 3 + 4
Pentru a putea scrie programul avem nevoie de o stiv# pentru evaluarea expresiei.
Modul de evaluare a expresiei este destul de simplu !i se poate deduce u!or din func"ia main,
care este dat# mai jos.
#include <stdio.h>
#include <math.h>
#define NUMBER 0 /semnalul ca a fost gasit un numar/
#define MAXOP $00 /lungimea maxima a unui operator/
int getop(char []);
void push(double);
double pop(void);
main()
{
int type;
double op2;
char s[MAXOP];

54
while((type=getop(s))!=EOF)
{
switch(type)
{
case NUMBER:
push(atof(s));
break;
case +:
push(pop()+pop());
break;
case :
push(pop()pop());
break;
case :
op2=pop();
push(pop()op2);
break;
case /:
op2=pop();
if(op2!=0.0)
push(pop()/op2);
else
printf(\aeroare: impartire cu zero!\n);
break;
case \n:
printf(\t%.8g\n,pop());
break;
default:
printf(\aeroare: comanda necunoscuta: %s\n,s);
break;
}
}
return 0;
}
Deoarece operatorii + !i snt comutativi, ordinea de evaluare din apelul func"iei push
corespunz#toare nu este important#, dar pentru operatorii !i / aceast# ordine este esen"ial#.
Din aceast# cauz# secven"a
push(pop()pop()); / GRESIT! /
este eronat# (de ce?), deoarece ordinea de evaluare nu este corect#. Pentru a asigura ordinea de
evaluare corect#, am utilizat variabila temporar# op2.
#define MAXVAL $00 /marimea maxima a stivei/
int sp=0; /indicele de stiva/
double val[MAXVAL]; /stiva/
void push(double f)
{
if(sp<MAXVAL)
val[sp++]=f;
else
printf(\aeroare: stiva plina!\n);
}
double pop(void)
{

55
if(sp>0)
return val[sp];
else
{
printf(\aeroare: stiva vida!\n);
return 0.0;
}
}
O variabil# este extern! dac# este definit# n exteriorul oric!rei func"ii. Astfel stiva !i
indicele de stiv#, care trebuie utilizate de ambele func"ii push !i pop, snt definite n afara
celor dou# func"ii. Deoarece main nu utilizeaz# stiva direct (dect prin intermediul lui push !i
pop), variabilele sp !i val pot fi ascunse acesteia.
Implementarea func"iei getop, care ne d# urm#torul num#r sau operand, este relativ
simpl#. Snt ignorate spa"iile !i caracterele tab. Dac# urm#torul caracter nu este cifr# sau punct
zecimal, el este returnat. n caz contrar, snt colectate toate cifrele (care pot include !i punctul
zecimal) !i este returnat semnalul c# s-a introdus un num#r (este returnat NUMBER). Dup#
cum se poate observa, se presupune c# numerele nu snt n nota"ia !tiin"ific#.
#include <ctype.h>
int getch(void);
void ungetch(int);
int getop(char s[])
{
int i,c;
while((s[0]=c=getch())== || c==\t)
;
s[$]=\0;
if(!isdigit(c) && c!=.)
return c;
i=0;
if(isdigit(c))
while(isdigit(s[++i]=c=getch()))
;
if(c==.)
while(isdigit(s[++i]=c=getch()))
;
s[i]=\0;
if(c!=EOF)
ungetch(c);
return NUMBER;
}
Dup# cum se poate observa, utiliz#m dou# func"ii noi, getch !i ungetch. La ce snt utile
acestea? Este de multe ori ntlnit# situa"ia n care un program nu poate !ti dac# a citit destul
din intrare dect n momentul n care a citit prea mult. n exemplul nostru, nu !tim dac#
num#rul este complet citit dect atunci cnd am citit un caracter non-cifr#. Dar acest caracter
nu face parte din num#r, ci este nevoie de el ntr-un alt context (de exemplu, caracterul citit
poate fi un operator). Ce e de f#cut ntr-o astfel de situa"ie? O posibilitate ar fi de a lucra cu
mai multe variabile, n care s# p#str#m caracterele citite !i s# semnal#m faptul c# au fost citite
prea multe caractere. O astfel de posibilitate este mai dificil de implementat, deci !ansele de a
gre!i snt mai mari. O a doua modalitate de rezolvare a unei astfel de situa"ii (mult mai

56
simpl#) este de a pune napoi n intrare (un-read) caracterul (sau caracterele) n plus. Din acest
motiv am utilizat func"iile getch !i ungetch.
#define BUFSIZE $00
char buf[BUFSIZE]; /bufferul pentru ungetch/
int bufp=0; /urmatoarea pozitie libera in buffer/
int getch(void)
{
return (bufp>0) ? buf[bufp] : getchar();
}
void ungetch(int c)
{
if(bufp>=BUFSIZE)
printf(\aungetch: prea multe caractere!\n);
else
buf[bufp++]=c;
}
Modul de implementare al celor dou# func"ii de mai sus este destul de simplu. Func"ia
getch cite!te un caracter din bufferul n care l-a depus func"ia ungetch, sau din intrare dac#
bufferul este vid. Func"ia ungetch pune caracterul napoi n buffer, de unde va fi citit la
urm#torul apel al lui getch.
Biblioteca standard include func"ia ungetc. De asemenea sistemele de operare MS DOS,
Windows 95/98/NT !i Unix con"in func"iile getch !i ungetch, a c#ror utilizare este pu"in
diferit# de cea de mai sus !i care snt descrise n <conio.h>. Toate aceste func"ii snt descrise
n Intr#ri !i ie!iri.
Exerci#iul 41. Ad#uga"i la programul de mai sus !i operatorul modulo (%), precum !i
posibilitatea de lucru cu variabile. ntr-o astfel de situa"ie, valorile variabilelor se citesc.
Numele variabilelor va respecta regulile limbajului C.

Exerci#iul 42. Ad#uga"i la programul de mai sus !i posibilitatea de lucru cu numere
reale n nota"ie !tiin"ific#.

Exerci#iul 43. Ad#uga"i la programul de mai sus !i posibilitatea de lucru cu func"ii
matematice ca sin, cos, etc. Aceste func"ii snt descrise n <math.h>.

Exerci#iul 44. O variant# a acestui program este aceea n care utiliz#m func"ia getline
pentru a citi o ntreag# linie. n aceast# situa"ie func"iile getch !i ungetch nu mai snt necesare.
Modifica"i programul de mai sus pentru a respecta aceast# observa"ie.

Exerci#iul 45. Scrie"i un program care transform# o expresie aritmetic# din nota"ie infix
n nota"ie polonez# invers#. Expresia poate avea operatorii +, , , / !i % %% %, variabile care s#
respecte conven"iile din C !i func"ii matematice descrise n <math.h> (o versiune mai simpl#
a acestui program, dar care se poate extinde foarte u!or, este dat# n Pointeri la caractere).

57
4.2.2 Domeniul unui identificator
Func"iile !i variabilele externe care alc#tuiesc un program C nu trebuie compilate n
acela!i timp, iar textul surs# al programului poate fi mp#r"it n mai multe fi!iere. Mai mult,
anumite func"ii pot fi luate din biblioteci (nu numai din biblioteca standard). Din aceste
motive apar mai multe probleme:
Cum se scriu declara"iile pentru ca variabilele s# fie declarate corect n momentul
compil#rii?
Unde trebuie puse declara"iile pentru ca ele s# fie corecte cnd programul este lansat
n execu"ie?
Cum snt organizate declara"iile pentru a nu avea mai multe declara"ii identice?
Cum snt ini"ializate variabilele externe?
Vom discuta acest aspect mp#r"ind programul din paragraful anterior n mai multe
fi!iere surs# (de fapt n dou#, dar lucrurile snt analoage !i dac# un program se g#se!te n mai
multe fi!iere surs#).
Domeniul (sau scopul) unui identificator este partea dintr-un program n care
identificatorul poate fi utilizat n sensul primit la defini"ia sa.
Pentru o variabil# automat# (local#) declarat# la nceputul unei func"ii, scopul este
func"ia n care este declarat#. Nu exist! nici o leg!tur! ntre variabile locale cu acela#i nume
situate n func"ii diferite.
Scopul unei variabile externe sau a unei func"ii este din momentul declar#rii !i pn# la
sfr!itul fi!ierului surs#. De exemplu, dac# main, sp, val, push !i pop snt definite n acela!i
fi!ier, n ordinea
main()
{
...
}
double val[MAXVAL];
int sp=0;
void push(double f)
{
...
}
double pop(void)
{
...
}
atunci variabilele sp !i val pot fi utilizate n push !i pop, f#r# a mai fi nevoie de nici o alt#
declara"ie. Aceste nume nu snt vizibile n main, nici chiar push !i pop.
Pe de alt# parte, dac# o variabil# extern# este referit# nainte de a fi definit#, sau dac#
este definit# n alt fi!ier surs#, atunci este necesar# utilizarea unei declara"ii extern.
Este important s# facem distinc"ie ntre declara"ia unei variabile externe !i defini"ia sa.

58
Declara"ia unei variabile externe informeaz! compilatorul despre propriet!"ile acelei
variabile, dar nu rezerv! memorie. Defini"ia unei variabile externe rezerv! memorie pentru
acea variabil! #i constituie #i o declara"ie a variabilei pentru restul fi#ierului surs!.
Dac# avem liniile
int sp;
double val[MAXVAL];
n exteriorul oric#rei func"ii, atunci ele definesc variabilele externe sp !i val, adic# se rezerv#
memorie pentru ele !i declar# variabilele pentru restul fi!ierului surs# n care apar. Dac#
ntr-un alt fi!ier surs# avem liniile
extern int sp;
extern double val[];
atunci ele declar# variabilele externe sp !i val pentru restul fi!ierului surs# n care apar, dar nu
creaz# variabilele respective (nu se rezerv# memorie pentru ele). ntr-o astfel de situa"ie se
presupune c# acele variabile snt definite undeva, iar n cazul cnd ele nu snt definite n nici
un fi!ier surs# se semnaleaz# eroare la editarea leg#turilor (dup# cum se poate observa, tabloul
val nu are precizat# dimensiunea, ceea ce nseamn# c# dimensiunea tabloului este definit#
undeva).
Pentru fiecare variabil! extern! trebuie s! existe o singur! defini"ie n toate fi#ierele
surs! care alc!tuiesc programul. Toate celelalte fi#iere care utilizeaz! acea variabil!
extern! trebuie s! o declare. Dimensiunea tablourilor trebuie precizat! la defini"ie #i este
op"ional! la declara"ie. O variabil! extern! poate fi declarat! n fi#ierul n care a fost
definit! (n special dac! utiliz!m acea variabil! nainte de a fi definit!). Ini"ializarea unei
variabile externe poate fi f!cut! numai la definirea ei.
Pentru a ilustra cele expuse mai sus, programul din paragraful anterior, cu toate c# nu
reprezint# varianta ideal# de organizare, poate fi mp#r"it n dou# fi!iere surs# astfel:
n primul fi!ier:
extern double val[];
extern int sp=0;
void push(double f)
{
...
}
double pop(void)
{
...
}
n al doilea fi!ier:
main()
{
...
}

59
double val[MAXVAL];
int sp=0;
4.2.3 Fi"iere header
S# presupunem c# programul anterior l mp#r"im n mai multe fi!iere surs# (acest lucru
este chiar indicat pentru programele mai mari). Func"ia main va fi ntr-un fi!ier main.c,
func"iile de lucru cu stiva, push !i pop, vor fi ntr-un alt fi!ier stack.c, func"ia getop o punem
n fi!ierul getop.c, iar func"iile getch !i ungetch le punem n fi!ierul getch.c. Aceste ultime
dou# func"ii le punem ntr-un fi!ier separat, deoarece n alte programe vor fi folosite func"iile
analoage din biblioteca standard.
Pentru a putea realiza cele propuse, trebuie s# "inem cont de faptul c# o parte din
defini"ii !i declara"ii trebuie utilizate n mai multe fi!iere. Pentru aceasta, vom plasa lucrurile
comune ntr-un fi#ier header numit calc.h, care va fi inclus acolo unde este necesar. Directiva
de includere #include va fi discutat# tot n acest capitol. Programul care rezult# poate fi
schi"at astfel:
Fi!ierul <calc.h> va con"ine:
#define NUMBER 0
void push(double);
double pop(void);
int getop(char []);
int getch(void);
void ungetch(int);
Fi!ierul <main.c> va con"ine:
#include <stdio.h>
#include <math.h>
#include calc.h
#define MAXOP $00
main()
{
...
}
Fi!ierul <getop.c> va con"ine:
#include <stdio.h>
#include <ctype.h>
#include calc.h
getop()
{
...
}
Fi!ierul <stack.c> va con"ine:

60
#include <stdio.h>
#include calc.h
#define MAXVAL $00
int sp=0;
double val[MAXVAL];
void push()
{
...
}
double pop(void)
{
...
}
Fi!ierul <getch.c> va con"ine:
#include <stdio.h>
#define BUFSIZE $00
char buf[BUFSIZE];
int bufp=0;
int getch(void)
{
...
}
void ungetch(int)
{
...
}
4.2.4 Variabile statice
Variabilele sp !i val din fi!ierul stack.c, precum !i variabilele buf !i bufp din fi!ierul
getch.c, snt utilizate numai n aceste fi!iere !i numai de func"iile respective !i nu trebuie
accesate de altundeva. Declara"ia static aplicat# unui obiect extern (variabil# sau func"ie)
limiteaz# scopul acelui obiect la restul fi!ierului surs# n care este definit obiectul. Obiectele
externe statice ne furnizeaz# astfel un mod de a ascunde obiectele pentru celelalte fi!iere surs#
!i totodat# posibilitatea utiliz#rii acelor obiecte ca !i obiecte externe n fi!ierul surs# n care
snt definite.
Obiectele statice se precizeaz# prin utilizarea cuvntului static n fa"a declara"iei
obi!nuite, ca de exemplu
static double val[MAXVAL];
static int sp=0;
void push(double f)
{
...
}

61
double pop(void)
{
...
}
Variabilele val !i sp nu pot fi accesate din exteriorul fi!ierului surs# n care snt definite.
n acest mod, n celelalte fi!iere putem defini variabile cu acela!i nume, f#r# ca s# existe nici
un conflict.
Declara"ia static poate fi aplicat# de asemenea !i func"iilor. n mod normal func"iile snt
globale, adic# pot fi accesate din orice parte a programului. Dac# o func"ie este declarat#
static, ea poate fi apelat# numai din fi!ierul n care a fost declarat#, putnd exista n celelalte
fi!iere func"ii cu nume identic (de!i acest lucru nu este indicat).
Declara"ia static poate fi aplicat# !i variabilelor interne. Variabilele statice interne snt
locale func"iei n care snt declarate, ca !i variabilele automate, dar, spre deosebire de
variabilele automate (care snt create la apelul func"iei !i snt distruse cnd func"ia se termin#,
avnd n momentul apel#rii func"iei valori nedefinite), ele r#mn !i dup# ce func"ia care le
con"ine se termin#, iar la un nou apel al acelei func"ii ele vor avea acelea#i valori pe care
le-au avut cnd func"ia s-a terminat. Aceasta nseamn# c# variabilele statice furnizeaz# o
modalitate privat# de memorare a unor date de la un apel la altul al acelei func"ii.
Exerci#iul 46. Modifica"i func"ia getop astfel nct s# nu mai utilizeze func"ia ungetch.
Folosi"i pentru aceasta o variabil# static# intern#.
4.2.5 Variabile registru
O variabil# registru semnific# faptul c# ea va fi utilizat# foarte des ntr-o func"ie.
Declara"ia register aplicat# unei variabile nseamn# c# acea variabil# va fi plasat#, dac# este
posibil, n regi!trii calculatorului, ceea ce conduce la programe mai mici !i mai rapide.
Declara"ia register arat# astfel:
register int x;
!i poate fi aplicat# numai variabilelor automate !i parametrilor formali ai unei func"ii (de ce?),
ca n
f(register unsigned m)
{
...
}
Referitor la variabilele registru exist# anumite restric"ii care reflect# realitatea hardware
a calculatorului. Astfel, doar cteva variabile din fiecare func"ie pot fi p#strate n regi!tri
(num#rul regi!trilor unui calculator este dependent de calculator) !i declara"ia register poate fi
aplicat# numai anumitor tipuri de date (nu poate fi aplicat# tipurilor reale). De asemenea nu
este posibil de a ob"ine adresa unei variabile registru (vezi Pointeri), indiferent dac# acea
variabil# este plasat# ntr-un registru sau nu.

62
4.3 Structura de bloc
Limbajul C nu este un limbaj cu o structur# de bloc ca !i Pascal, deoarece nu putem
defini o func"ie n interiorul altei func"ii. Dar, spre deosebire de alte limbaje (cum ar fi Pascal
!i altele nrudite cu acesta), variabilele pot fi definite ntr-o func"ie, structurate n blocuri.
Declara"iile de variabile (inclusiv ini"ializarea) pot urma acolada stng# de nceput a oric!rei
instruc"iuni compuse, nu doar acolada stng# de nceput a unei func"ii. Variabilele declarate n
acest mod ascund variabilele cu nume identice din blocurile exterioare !i exist# pn# la
ntlnirea acoladei drepte de terminare a acelui bloc. De exemplu, n secven"a
if(n>0)
{
int i;
for(i=0;i<n;i++)
...
}
scopul variabilei i este doar n interioriul instruc"iunii compuse corespunz#toare instruc"iunii
if !i nu exist# n exteriorul acestui bloc. Dac# n exteriorul acestei instruc"iuni compuse avem
!i o alt# variabil# cu acela!i nume, aceasta nu poate fi accesat# din interiorul blocului, excep"ie
f#cnd por"iunea din bloc care precede redefinirea variabilei.
O variabil# automat# declarat# !i ini"ializat# ntr-un bloc este ini"ializat# de fiecare dat!
cnd blocul este executat. O variabil# static# este ini"ializat# numai prima dat! cnd blocul
este executat (de ce?).
Variabilele automate, inclusiv parametrii formali, ascund de asemenea variabilele
externe !i func"iile cu acela!i nume, f#cnd n acest mod imposibil# accesarea lor din func"ia
respectiv#.
Toate declara"iile de variabile din interiorul unui bloc trebuie f!cute la nceputul
blocului, naintea primei instruc"iuni executabile.
4.4 Ini#ializarea
n programele anterioare am utilizat ini"ializarea variabilelor, dar nu ne-am referit la
modurile n care ea poate fi f#cut# !i nici la regulile de care trebuie s# "inem seama.
n absen"a unei ini"ializ#ri explicite, este garantat c# variabilele externe !i statice snt
ini"ializate cu zero, iar variabilele automate !i registru au valori ini"iale nedefinite (garbage
values).
Variabilele scalare (simple) pot fi ini"ializate cnd snt definite, punnd dup# numele lor
semnul egal !i o expresie, ca n
int x=$;
long day=$000L60L60L24L; / milisecunde/zi /
Variabilele externe !i statice trebuie s# fie ini"ializate cu o expresie constant#, care s#
poat# fi evaluat# de compilator. Ini"ializarea acestor variabile este f#cut# o singur# dat#,
conceptual nainte ca programul s# !i nceap# execu"ia.

63
Variabilele automate !i registru snt ini"ializate de fiecare dat# cnd blocul n care snt
definite este executat. Aceste variabile pot fi ini"ializate cu o expresie de orice tip, care poate
con"ine chiar !i apeluri de func"ii. n realitate ini"ializ#rile pentru variabilele automate !i
registru constituie o prescurtare a instruc"iunii de asignare.
Un tablou poate fi ini"ializat punnd dup# declara"ia sa o list# cu ini"ializatori cuprin!i
ntre acolade !i separa"i de virgul#. De exemplu, pentru a ini"ializa tabloul days cu num#rul de
zile din fiecare lun#, putem folosi secven"a
int days[]={3$,28,3$,30,3$,30,3$,3$,30,3$,30,3$};
Cnd dimensiunea tabloului este omis# (ca !i n exemplul de mai sus), compilatorul va
calcula dimensiunea num#rnd ini"ializatorii (n cazul de mai sus fiind 12).
Dac# num#rul de ini"ializatori este mai mic dect dimensiunea tabloului, elementele
neini"ializate vor avea valoarea zero dac# tabloul este extern sau static, !i vor avea valori
nedefinite pentru tablourile automate. Dac# num#rul ini"ializatorilor este mai mare dect
dimensiunea tabloului, avem o eroare. Nu exist# nici o posibilitate pentru a repeta un
ini"ializator !i nici pentru a ini"ializa un element dintr-un tablou, f#r# ca s# ini"ializ#m !i toate
elementele dinaintea lui.
Un caz special de ini"ializare l constituie tablourile de caractere (#irurile). Pentru a
ini"ializa un tablou de caractere putem folosi o constant# !ir, ca n
char luna=ianuarie;
Aceast# form# de ini"ializare este o prescurtare pentru ini"ializarea mai lung#, dar
echivalent#
char luna={i,a,n,u,a,r,i,e,\0};
n acest caz, dimensiunea tabloului este egal# cu 9 (8 caractere plus terminatorul \0).
4.5 Recursivitate
n C o func"ie poate fi apelat# recursiv, direct sau indirect. Vom ilustra modul de lucru
cu func"ii recursive prin dou# exemple de astfel de func"ii.
S# consider#m c# dorim s# tip#rim un num#r ca !ir de caractere. Dup# cum am v#zut
ntr-un exemplu anterior, cifrele snt generate n ordine invers#, dar trebuie tip#rite corect.
Pentru a rezolva aceast# problem#, exist# dou# solu"ii. O prim# solu"ie este de a ob"ine cifrele
ntr-un tablou pe care s# l tip#rim n ordine invers#, a!a cum am f#cut la func"ia itoa descris#
n Instruc"iuni. O a doua solu"ie este de a scrie o func"ie recursiv#, n care func"ia mai nti se
apeleaz# !i dup# aceea tip#re!te cifra care rezult#. n acest caz, ceea ce se va tip#ri vor fi
cifrele num#rului n ordine corect#.
#include <stdio.h>
void printd(int n)
{
if(n<0)
{
putchar();
n=n;
}
if(n/$0)
printd(n/$0);

64
putchar(n%$0+0);
}
Un alt exemplu de func"ie recursiv# este implementarea metodei Quick Sort, dezvoltat#
de C.A.R. Hoare n anul 1962. Func"ia descris# mai jos sorteaz# cresc#tor un tablou de n
ntregi, fiind din punct de vedere func"ional identic# cu metoda de sortare shellsort descris# n
Capitorul 3 (dar mai rapid# dect aceasta).
void swap(int [],int,int);
void qsort(int v[],int left,int right)
{
int i,last;
if(left>=right)
return;
swap(v,left,(left+right)/2);
last=left;
for(i=left+$;i<=right;i++)
if(v[i]<v[left])
swap(v,++last,i);
swap(v,left,last);
qsort(v,left,last$);
qsort(v,last+$,right);
}
void swap(int v[],int i,int j)
{
int temp;
temp=v[i];
v[i]=v[j];
v[j]=temp;
}
O alt# implementare a func"iei qsort este dat# mai jos:
void qsort(int v[],int left,int right)
{
int t,i,j;
if(right>left)
{
t=v[right];
i=left-$;
j=right;
for(;;)
{
while(v[++i]<t) ;
while(v[j]>t) ;
if(i>=j)
break;
swap(v,i,j);
}
swap(v,i,right);
qsort(v,left,i$);
qsort(v,i+$,right);
}
}

65
Biblioteca standard con"ine o versiune a func"iei qsort care poate sorta obiecte de orice
tip.
Recursivitatea este consumatoare de spa"iu de memorie, deoarece este utilizat# stiva
pentru apelul recursiv !i pentru memorarea variabilelor automate !i a parametrilor formali. De
asemenea recursivitatea este mai lent# dect itera"ia. Avantajul utiliz#rii recursivit#"ii l
constituie codul mult mai compact, mai u!or de scris !i de n"eles dect echivalentul
nerecursiv. Recursivitatea este util# n special pentru prelucrarea structurilor de date definite
recursiv, a!a cum vom vedea n Structuri.
Exerci#iul 47. Adapta"i ideile din func"ia printd pentru a scrie o versiune recursiv# a
func"iei itoa, care converte!te un ntreg ntr-un !ir.

Exerci#iul 48. Scrie"i o versiune recursiv# a func"iei reverse(s) care inverseaz#
caracterele !irului s.
4.6 Preprocesorul C
C ne furnizeaz# cteva facilit#"i ale limbajului prin intermediul unui preprocesor, care
este din punct de vedere conceptual un prim pas distinct n procesul compil#rii. Facilit#"ile
cele mai utilizate, pe care le-am folosit !i pn# acum n programe, l constituie directivele
#include !i #define. n afara acestor dou# facilit#"i, exist# !i altele, pe care le vom prezenta n
continuare.
4.6.1 Includerea fi"ierelor
Includerea fi!ierelor face posibil# gruparea defini"iilor #define !i a declara"iilor globale,
precum !i a altor lucruri. Orice linie de forma
#include fisier
sau
#include <fisier>
este nlocuit# de compilator cu con"inutul fi!ierului fisier. Dac# folosim forma fisier, fi!ierul
este c#utat acolo unde se g#se!te !i programul surs#. Dac# nu este g#sit n acel loc, sau dac#
utiliz#m forma <fisier>, fi!ierul este c#utat conform unei reguli de c#utare, regul# dependent#
de implementarea limbajului C !i de sistemul de operare utilizat. n cazul n care fi!ierul nu
este g#sit nici n acel loc se semnaleaz# eroare la compilare.
Un fi!ier inclus cu #include poate la rndul s#u s# con"in# !i alte directive #include.
Deseori, la nceputul programului surs#, snt mai multe linii #include, pentru a fi incluse
defini"iile #define necesare, declara"iile extern sau pentru a accesa func"iile din biblioteca
standard, ale c#ror prototipuri se g#sesc n fi!iere header.
Directiva #include este util# deoarece programul surs# este mai u!or de urm#rit !i
n"eles, el putnd con"ine numai corpul func"iilor. Dac# un fi!ier inclus este modificat, atunci
toate programele care l folosesc trebuie recompilate.

66
4.6.2 Macrosubstitu#ia
O macrodefini"ie are forma
#define nume text
unde text este desp#r"it de nume prin cel pu"in un spa"iu.
Macrosubstitu"ia cea mai simpl# are ca efect nlocuirea tuturor apari"iilor !irului nume
cu !irul text. Numele din #define are aceea!i form# ca !i numele unei variabile, iar textul cu
care este nlocuit este arbitrar. n mod normal textul cu care se face nlocuirea este pn# la
sfr!itul liniei, dar, dac# defini"ia este mai lung#, poate continua !i pe liniile urm#toare, punnd
caracterul \ la sfr!itul fiec#rei linii care se continu#. Scopul numelui definit cu #define este
din acel punct !i pn# la sfr!itul fi!ierului surs#.
O defini"ie poate con"ine o alt# defini"ie. Substitu"ia se face numai pentru unit#"i
sintactice care nu snt cuprinse ntre ghilimele. De exemplu, dac# avem defini"ia
#define TAB \t
atunci !irul TAB nu va fi nlocuit n
printf(TAB);
sau
int TABS[5];
Un nume poate fi nlocuit cu orice text. De exemplu
#define ciclu for(;;)
define!te un nou cuvnt, ciclu, care poate fi folosit pentru un ciclu infinit.
Este posibil s# definim un macro cu argumente, astfel nct textul nlocuitor s# fie diferit
pentru apeluri diferite ale acelui macro. Ca un exemplu, s# consider#m macroul care
calculeaz# maximul a dou# numere:
#define max(A,B) ((A)>(B)?(A):(B))
De!i acest macro arat# ca un apel de func"ie, utilizarea lui are ca efect expandarea n cod
in-line (nlocuirea de c#tre compilator cu secven"a de cod corespunz#toare). Fiecare apari"ie a
unui parametru formal va fi nlocuit# cu argumentul actual corespunz#tor. Astfel, linia
x=max(p+q,r+s);
va fi nlocuit# cu linia
x=((p+q)>(r+s)?(p+q):(r+s));
Dac# nu exist# efecte laterale, acest macro va lucra corect pentru orice tip de date, spre
deosebire de func"ii, la care trebuie s# "inem cont de tipul argumentelor.
Dac# examin#m cu aten"ie defini"ia macroului max, vom observa cteva capcane, de
care trebuie s# ne ferim. Expresiile din max snt evaluate de dou# ori, ceea ce este r#u dac#
aceast# evaluare are efecte laterale. De exemplu,
x=max(p++,r++); / GRESIT /
va incrementa valoarea mai mare de dou# ori (de ce?). Pentru a asigura ordinea de evaluare
corect#, trebuie s# lu#m precau"ii prin utilizarea de paranteze. Ca un exemplu, examina"i ce se
ntmpl# cu macroul
#define patrat(x) xx / GRESIT /
cnd este utilizat sub forma patrat(z+1).

67
Defini"ia unui nume poate fi anulat# (undefine), de obicei pentru a ne asigura c# o
anumit# rutin# este o func"ie !i nu un macro. De exemplu,
#undef patrat
int patrat(int x)
{
...
}
Exerci#iul 49. Scrie"i un macro swap(t,x,y) care schimb# argumentele x !i y de tipul t.
Folosi"i pentru aceasta structura de bloc.
4.6.3 Includeri condi#ionale
Este posibil s# control#m preprocesarea cu ajutorul unor instruc"iuni condi"ionale care
snt evaluate n timpul preproces#rii (acest procedeu se mai nume!te !i compilare
condi"ionat#, deoarece vor fi incluse pentru compilare doar anumite secven"e de cod). Aceasta
ne furnizeaz# un mod de a include cod n mod selectiv, n func"ie de condi"iile evaluate n
timpul compil#rii.
Linia #if (#ifdef sau #ifndef) evalueaz# o expresie ntreag# constant# (care nu poate
con"ine operatorul sizeof, construc"ii cast sau constante enum). Dac# expresia este nenul#, snt
incluse (n vederea compil#rii) toate liniile care urmeaz#, pn# la o directiv# #endif, #elif (care
este analog cu else if) sau #else.
n cele mai multe cazuri expresia de dup# #if are forma
defined identificator
sau
defined (identificator)
Directivele
#ifdef identificator
#ifndef identificator
snt echivalente respectiv cu
#if defined identificator
#if ! !! !defined identificator
De exemplu, pentru a fi siguri c# fi!ierul header hdr.h este inclus o singur# dat# ntr-un
program surs#, con"inutul acestuia poate ar#ta
#if !defined(HDR)
#define HDR
/ continutul fisierului hdr.h /
#endif
sau
#ifndef HDR
#define HDR
/ continutul fisierului hdr.h /
#endif

68
4.7 Rezumat
Acest capitol prezint# modul de organizare !i structurare al unui program C. Un
program C const# din una sau mai multe func"ii, dintre care trebuie s# existe obligatoriu o
singur! func"ie cu numele main. Aceast# func"ie nu poate fi apelat# de nici o alt# func"ie din
program. Func"iile n C pot fi recursive.
Spre deosebire de alte limbaje cum ar fi Pascal, n C o func"ie nu poate fi definit# n
interiorul altei func"ii. Din aceast# cauz# func"iile n C pot fi numai externe. Spre deosebire de
acestea, variabilele pot fi externe sau interne. Prototipul func"iilor, precum !i defini"ii de
constante, variabile globale, pot fi cuprinse n fi!iere header !i incluse n program (dup# cum
s-a putut observa nc# din Ini"iere n C, func"iile standard ale limbajului se utilizeaz# prin
includerea n program a fi!ierelor header n care acestea au prototipul). De asemenea mai este
precizat modul de comunicare ntre func"ii !i modul de lucru cu directive ale preprocesorului.

69
5. POINTERI
Un pointer este o variabil! care con"ine adresa unei variabile.
n C pointerii snt foarte mult utiliza"i, pe de o parte pentru c# uneori snt singurul mod
de a exprima un calcul, iar pe de alt# parte pentru c# ei conduc la un cod mult mai compact !i
mai eficient dect codul care s-ar ob"ine n alt mod.
mpreun# cu instruc"iunea goto (pe care nu am prezentat-o n mod inten"ionat), pointerii
constituie cel mai simplu mod de a crea programe imposibil de n"eles !i de depanat, putnd
conduce foarte u!or la blocarea calculatorului, sau chiar mai r#u De aceea ei trebuie utiliza"i
numai acolo unde este necesar, !i cu un scop bine determinat. Este demn de remarcat faptul c#
prin intermediul pointerilor putem face cu calculatorul aproape orice (deci nu numai lucruri
bune).
Deoarece pointerii snt folosi"i aproape n orice program C, se poate spune c# cine nu
#tie s! lucreze cu pointerii, nu #tie s! lucreze n C.
5.1 Pointeri "i adrese
Pentru a n"elege mai bine pointerii, s# examin#m pu"in mai n detaliu cum este
organizat# memoria calculatorului. Memoria poate fi privit# ca un tablou de celule de
memorie consecutive numite octe"i. Aceste celule snt numerotate ncepnd cu zero, iar
num!rul celulei se mai nume!te adres!. Celulele pot fi prelucrate individual sau grupat. De
exemplu, o celul# oarecare (un octet) poate fi tratat# ca un char, doi octe"i consecutivi pot fi
trata"i ca un ntreg short, iar patru octe"i consecutivi pot fi trata"i ca un long.
Un pointer este un grup de loca"ii de memorie (de obicei dou# sau patru) care pot
con"ine o adres!. Operatorul unar & ne d# adresa unui obiect, astfel c# dac# c este un char !i
p este un pointer la el, instruc"iunea
p=&c;
atribuie adresa lui c variabilei p, !i spunem c# p pointeaz! la c. Operatorul & se poate aplica
numai obiectelor din memorie, variabile !i elemente de tablou. El nu poate fi aplicat
expresiilor, constantelor !i variabilelor registru.
Operatorul unar este operatorul de indirectare. Cnd acest operator este aplicat unui
pointer, acceseaz# obiectul pointat de acel pointer. S# presupunem c# x !i y snt ntregi, iar ip
este un pointer la un int. n secven"a de mai jos este ilustrat modul cum se declar# pointerul !i
cum se utilizeaz# operatorii & !i .
int x=$,y=2,z[$0];
int ip; / ip este un pointer la int /
ip=&x; / ip pointeaza la x /

70
y=ip; / y este acum $ (de ce?) /
ip=0; / x este acum 0 /
ip=&z[0]; / ip pointeaza la z[0] /
Declara"ia pointerului ip,
int ip;
ne spune c# ip este un int. De men"ionat c# fiecare pointer pointeaz# la un tip de date
particular (cu excep"ia pointerilor la void, despre care vom vorbi ntr-un paragraf ulterior).
Dac# ip pointeaz# la ntregul x, atunci ip poate apare n orice context unde poate apare
x, deci instruc"iunea
ip=ip+$0;
incrementeaz# pe ip cu 10.
$innd cont de preceden"a operatorilor (vezi Preceden"a !i ordinea de evaluare), expresia
ip+=$
incrementeaz# variabila pointat# de ip, la fel ca !i expresia
++ip
sau
(ip)++
n acest ultim exemplu parantezele snt necesare (de ce?). Deoarece pointerii snt
variabile, ei pot fi utiliza"i f#r# operatorul de indirectare. Astfel, dac# iq este un alt pointer la
int, instruc"iunea
iq=ip;
copiaz# con"inutul lui ip n iq, astfel nct iq !i ip vor pointa n acela!i loc.
5.2 Pointeri "i argumentele func#iilor
n C argumentele func"iilor se transmit numai prin valoare.
Din aceast# cauz# nu exist# un mod direct pentru func"ia apelat# ca s# modifice o
variabil# din functia apelant#. De exemplu, s# presupunem c# o func"ie de sortare trebuie s#
schimbe dou# elemente care nu snt n ordine cu ajutorul unei func"ii numit# swap. Dup#
cuno!tiin"ele dobndite pn# acum, func"ia swap este apelat# prin instruc"iunea
swap(a,b);
iar func"ia ns#!i este definit# ca mai jos
void swap(int x,int y) / GRESIT /
{
int temp;
temp=x;
x=y;
y=temp;
}

71
Deoarece apelul se face prin valoare, func"ia swap, a!a cum este ea scris# mai sus, nu
poate modifica argumentele a !i b cu care este apelat#, ci schimb# doar parametrii s#i x !i y,
care snt cpii ale argumentelor de apel a !i b. Dup# ce func"ia se termin#, schimbul, care se
realizeaz# corect n func"ie, este f#r# efect, ca !i cum nu ar fi existat.
Dac# lucrurile stau a!a, cum putem totu!i s# realiz#m schimbarea argumentelor a !i b?
Este clar c# nu putem transmite valorile lui a !i b. Ce se ntmpl# ns# dac# n locul
variabilelor a !i b transmitem pointeri la cele dou# variabile (adic# adresele variabilelor)?
Instruc"iunea de apel a func"iei swap va arata n acest caz astfel
swap(&a,&b);
Deoarece operatorul & ne d# adresa unei variabile, &a este un pointer la a. ntr-o astfel
de situa"ie, parametrii func"iei swap trebuie s# fie pointeri, iar func"ia se scrie astfel
void swap(int px,int py)
{
int temp;
temp=px;
px=py;
py=temp;
}
Func"ia swap cu modific#rile de mai sus face acum ceea ce dorim? Observ#m c#
parametrii px !i py con"in adresele lui a !i b. Func"ia swap schimb# acum valorile pointate de
px !i py, adic# schimb# valorile lui a !i b, exact ceea ce doream. Schimbarea va r#mne !i
dup# ce func"ia se termin#.
n concluzie, pointerii permit unei func"ii s# acceseze !i s# modifice obiectele din
func"ia apelant#. Pointerii snt utiliza"i n special n func"iile care trebuie s# returneze mai mult
de o valoare (func"ia swap de mai sus returneaz# dou# valori).
Referitor la transmiterea pointerilor ca argumente func"iilor, pot apare unele capcane.
Pentru a ilustra una din acestea, s# consider#m secven"a de program de mai jos, n care
presupunem c# variabilele x !i y snt externe.
...
int x,y;
...
int ip=&x;
f(ip);
...
void f(int ip)
{
ip=&y;
}
ntrebarea care se pune este ce valoare va avea pointerul ip dup# apelul func"iei f? La o
prim# analiz# a secven"ei !i "innd cont de cele discutate mai sus referitor la func"ia swap,
r#spunsul este pointerul ip va avea ca valoare adresa lui y. Oare este corect r#spunsul? S#
examin#m secven"a de mai sus mai cu aten"ie. nainte de apelul func"iei f, este declarat
pointerul ip !i este ini"ializat cu adresa lui x. Aceast# valoare este transmis# func"iei f.
Parametrul func"iei (care se nume!te tot ip) este un pointer, ceea ce nseamn# c# din func"ie
putem modifica valoarea loca"iei pointat# de acesta (con"inutul lui x). Dar, deoarece n C to"i

72
parametrii snt transmi!i prin valoare, pointerul nsu!i este transmis prin valoare, adic#
pointerul ip declarat ca parametru este o copie a pointerului ip transmis ca argument.
Modificarea lui ip n func"ie nu are nici un efect dup! ce func"ia se termin!. n concluzie, n
urma apelului func"iei f din secven"a de mai sus, pointerul ip nu se modific#.
Re"inem deci c#
n cazul n care transmitem unei func"ii un pointer ca argument, pointerul nsu#i este
transmis prin valoare, ceea ce nseamn! c! din func"ie putem modifica valoarea pointat!
de acel pointer, dar nu putem modifica pointerul. Pentru a modifica pointerul, trebuie s!
transmitem func"iei adresa pointerului, adic! un pointer la pointerul pe care dorim s! l
modific!m.
5.3 Calificatori de tip. Expresii r-valoare "i l-valoare
5.3.1 Calificatori de tip
Calificatorii de tip dau unui identificator una sau dou# propriet#"i. Calificatorul const
declar# un obiect ca fiind nemodificabil. Calificatorul volatile declar# o entitate a c#rei valoare
poate fi schimbat# de un eveniment, chiar extern programului, n orice moment. Calificatorii
de tip pot apare doar o dat# ntr-o declara"ie !i trebuie s# fie plasa"i dup! tipul pe care l
calific#. Ei pot apare cu orice specificator de tip !i snt relevan"i numai cnd acces#m
identificatori de tip ca l-valori n expresii.
Urm#toarele declara"ii snt legale:
int const p_ci; / pointer la o constanta int /
int const cp_i; / pointer constant la un int /
int volatile vint; / intreg volatile /
Dac# specificatorul unui tip tablou include calificatori de tip, ei snt aplica"i elementelor
tabloului !i nu tipului tablou. Dac# un specificator de func"ie include calificatori, comportarea
func"iei este nedefinit#. Nici unul din cei doi calificatori nu afecteaz# domeniul valorilor sau
propriet#"ile aritmetice ale obiectului.
Calificatorul const poate fi folosit pentru a modifica orice tip fundamental sau derivat,
sau un pointer la un obiect de orice tip. Dac# un obiect este declarat doar cu calificatorul
const, el se consider# de tipul const int. O variabil# const poate fi numai ini"ializat#.
Calificatorul const este util pentru a declara pointeri const, ceea ce nseamn# c# func"iile nu
pot schimba ace!ti pointeri. De!i un pointer const nu poate fi modificat, se poate modifica
valoarea obiectului spre care pointeaz#.
De exemplu, dac# avem declara"iile
const zile=7;
char const sir=Duminica;
atunci instruc"iunile
zile=3;
sir=Luni;

73
snt interzise, dar apelul
strcpy(sir,Marti);
este permis (de ce?).
Compilatorul presupune c# o variabil# volatile poate fi accesat# n orice punct din
program !i con"inutul ei poate fi modificat. Un obiect poate fi n acela!i timp const !i volatile.
Aceasta nseamn# c# acel obiect nu poate fi modificat din program, dar poate fi modificat de
un eveniment extern programului.
5.3.2 Expresii l-valoare "i r-valoare
Expresiile care se refer# la loca"ii de memorie snt numite expresii l-valoare. O
l-valoare reprezint# o zon# de memorie sau o valoare stng# (nota"ia de l vine de la left),
datorit# faptului c# ea poate apare n stnga semnului =. O expresie care nu poate fi l-valoare
este numit# expresie r-valoare.
Expresiile referitoare la loca"ii modificabile snt numite l-valori modificabile. O
l-valoare modificabil! nu poate fi de tip tablou sau un tip cu atributul const.
Un identificator este o l-valoare modificabil# dac# el refer# o loca"ie de memorie !i tipul
s#u este aritmetic, pointer, structur# sau uniune. De exemplu, dac# ptr este un pointer, atunci
ptr este o l-valoare modificabil# care desemneaz# zona de memorie la care pointeaz# ptr.
Urm#toarele expresii C pot fi l-valori:
un identificator al unui tip ntreg, real, pointer, structur# sau uniune;
o expresie cu indici care nu este evaluat# la un tablou;
o expresie selector dat# de > sau . (vezi Structuri);
o expresie de indirectare unar# care nu se refer# la un tablou;
o expresie l-valoare n paranteze;
un obiect const (o l-valoare nemodificabil#).
5.4 Pointeri "i tablouri
n C exist# o leg#tur# strns# ntre pointeri !i tablouri, astfel nct tablourile pot fi tratate
tot n acest capitol. Orice opera"ie care poate fi f#cut# cu un indice de tablou poate fi f#cut# !i
cu ajutorul pointerilor. Opera"iile cu pointeri snt n general mai rapide, dar, mai ales pentru
ncep#tori, mai greu de n"eles.
5.4.1 No#iuni de baz%
Declara"ia
int a[$0];
define!te un tablou de 10 ntregi, adic# un bloc de 10 ntregi consecutivi, numi"i a[0], a[$],,
a[9].
Dimensiunea unui tablou trebuie cunoscut! la compilare, deoarece pentru un tablou
compilatorul aloc! memorie.

74
De aici rezult# c# secven"a
#define N 20
int a[N];
este corect#, deoarece valoarea lui N este cunoscut# la compilare !i deci compilatorul poate
aloca memorie pentru tablou. Secven"a
const int n=20;
int a[n]; / GRESIT /
nu este corect#, deoarece calificatorul const nseamn# read-only. Un obiect calificat n acest
mod este un obiect care este creat la execu"ia programului (este un obiect run-time) !i c#ruia
nu i se poate atribui (n mod normal) o valoare. Valoarea unui obiect calificat cu const nu este
o expresie constant# care s# fie cunoscut# la compilare !i deci nu poate fi utilizat n declararea
unui tablou (spre deosebire de C, n C++ folosirea calificatorului const face ca acel obiect s#
fie o constant# cunoscut# la compilare, care poate fi deci utilizat# n declara"ii de tablouri,
astfel nct secven"a anterioar# n C++ este corect#).
Nota"ia a[i] se refer# la elementul de pe pozi"ia i din tablou. Dac# pa este un pointer la
un ntreg, declarat
int pa;
atunci instruc"iunea
pa=&a[0];
atribuie lui pa adresa primului element din tablou, adic# adresa lui a[0]. Deci instruc"iunea
x=pa;
copiaz# con"inutul lui a[0] n x.
Dac# pa pointeaz# la un element particular din tablou, atunci, prin defini"ie, pa+1
pointeaz# la urm#torul element, pa+i pointeaz# cu i elemente dup# pa, iar pa-i pointeaz# cu i
elemente naintea lui pa. Astfel, dac# pa pointeaz# la a[0], atunci
(pa+$)
se refer# la con"inutul lui a[$],
pa+i
este adresa lui a[i], iar
(pa+i)
este con"inutul lui a[i].
Aceste observa"ii snt valabile indiferent de tipul elementelor din tabloul a. Expresia
adun# 1 la un pointer !i, prin extensie, toate opera"iile cu pointeri, semnific# faptul c# pa+1
pointeaz# la urm#torul obiect, iar pa+i pointeaz# la al i-lea obiect dup# pa.
Leg#tura dintre indicele unui tablou !i pointeri este foarte apropiat#. Prin defini"ie,
valoarea unei variabile sau expresii de tip tablou este adresa primului element din tablou
(elementul zero). Astfel, n urma execu"iei instruc"iunii
pa=&a[0];
pa !i a au valori identice. Deoarece numele unui tablou este un sinonim pentru loca"ia
primului element, asignarea pa=&a[0] poate fi scris# !i sub forma
pa=a;

75
De asemenea, o referin"# de forma a[i] poate fi scris# sub forma (a+i). Acest lucru este
adev#rat, deoarece n realitate cnd se evalueaz# expresia a[i], ea este transformat# imediat n
expresia (a+i). Aplicnd operatorul & celor dou# forme, rezult# c# &a[i] !i a+i snt de
asemenea identice, adic# a+i este adresa celui de-al i-lea de dup# a.
Pe de alt# parte, dac# pa este un pointer, el poate fi utilizat n expresii cu un indice: pa[i]
este ientic cu (pa+i). Pe scurt, o expresie format# dintr-un tablou !i un indice este identic# cu
acea expresie scris# ca un pointer !i un offset.
Exist# o diferen"# ntre numele unui tablou !i un pointer, de care trebuie s# "inem seama.
Un pointer este o variabil#, deci expresii ca pa=a !i pa++ snt legale. Numele unui tablou nu
este o variabil#, deci construc"ii ca a=pa !i a++ snt ilegale.
Cnd numele unui tablou este transmis unei func"ii, ceea ce se transmite este loca"ia
primului element. n func"ia apelat# acest argument este o variabil# local#, deci numele unui
tablou transmis ca parametru este un pointer. Utiliznd acest lucru, putem rescrie func"ia
strlen care calculeaz# lungimea unui !ir de caractere.
int strlen(char s)
{
int n;
for(n=0;s!=\0;s++)
n++;
return n;
}
Deoarece s este un pointer, incrementarea sa este legal#, iar s++ nu are nici un efect
asupra !irului de caractere cu care este apelat# func"ia.
Ca !i parametri formali n defini"ia func"iei, expresiile
char s[]
!i
char s
snt echivalente. A doua form# este de preferat primeia, deoarece pune n eviden"# n mod
explicit faptul c# argumentul func"iei este un pointer.
Este posibil de asemenea s# transmitem unei func"ii doar o parte dintr-un tablou
(ncerca"i s# face"i acest lucru din Pascal), transmi"nd un pointer la nceputul subtabloului.
De exemplu, dac# a este un tablou de ntregi,
f(&a[2])
!i
f(a+2)
transmit func"iei f adresa subtabloului care ncepe cu a[2]. n func"ia f parametrul poate fi
declarat sub forma
f(int arr[])
{
...
}
sau

76
f(int arr)
{
...
}
Dac# sntem siguri c# ne ncadr#m n limitele unui tablou, este posibil s# folosim indici
negativi. De exemplu p[ $], p[ 2] etc. snt expresii legale !i se refer# la elementele care
preced pe p[0]. Este ilegal s# referim obiecte care nu snt ntre limitele tabloului (mai ales s#
modific#m astfel de obiecte!).
5.4.2 Gre"eli tipice n utilizarea pointerilor "i a tablourilor
De ce acest titlu? De!i pare surprinz#tor, exist# numeroase situa"ii n care, din cauza
confuziei f#cute ntre pointeri !i tablouri, programul s# nu func"ioneze, sau, mai r#u,
calculatorul s# se comporte ntr-un mod destul de ciudat, imprevizibil, care nu l putem
controla sau prevedea. S# vedem cam ce gre!eli se pot face !i cum ne putem feri de ele.
S# consider#m o prim# situa"ie care poate apare. Presupunem c# programul nostru are
cel pu"in dou# fi!iere surs#. n unul din aceste fi!iere avem declara"ia
char a[6];
iar n alt fi!ier avem declara"ia
extern char a;
De ce nu este corect# aceast# declara"ie? Ger!eala const# n faptul c# declara"ia
anterioar# nu este identic# cu defini"ia lui a, deoarece tipul pointer la tipul T nu este acela!i
cu tipul tablou de tipul T.
Declara"ia
char a[6];
are ca efect rezervarea unui spa"iu pentru !ase caractere, iar spa"iul este cunoscut sub numele
a. Declara"ia
char p;
are ca efect rezervarea unui spa"iu n care s# poat# fi memorat un pointer (o adres#). Spa"iul
este cunoscut sub numele p, iar pointerul poate con"ine adresa oric!rui caracter. S#
consider#m instruc"iunile
char a[]=luni;
char p=marti;
Memoria care se aloc# n urma execu"iei acestor instruc"iuni arat# astfel (fiecare celul#
de memorie ocupat# este ncadrat# ntr-un dreptunghi):
a: l u n i \0

p: m a r t i \0
Trebuie s# "inem seama c# o referin"# de tipul x[3] genereaz# cod care depinde de faptul
c# x este un pointer sau un tablou. De exemplu, avnd declara"iile de mai sus, cnd
compilatorul ntlne!te expresia a[3], genereaz# cod care face urm#toarele: sare la loca"ia a, se
mut# cu trei loca"ii dup# !i ia caracterul g#sit acolo. Dac# este ntlnit# expresia p[3],

77
compilatorul genereaz# cod care face urm#toarele: sare la loca"ia p, ia de acolo valoare g#sit#
(adresa), adun# trei la aceast# valoare !i ia caracterul de la adresa dat# de valoarea final#.
Dac# lucrurile stau a!a, s# vedem ce nseamn# echivalen"a pointerilor !i a tablourilor.
Cnd spunem c# pointerii snt echivalen"i cu tablourile, aceasta nu nseamn# nici c# pointerii
snt identici cu tablourile, nici c# snt interschimbabili. Echivalen"a se refer# la urm#toarea
defini"ie:
O l-valoare de tipul tablou-de-T care apare ntr-o expresie este transformat! (cu trei
excep"ii) ntr-un pointer la primul s!u element, iar tipul pointerului rezultat este
pointer-la-T .
Cele trei excep"ii snt:
cnd tabloul este operandul operatorului sizeof;
cnd tabloul este operandul operatorului &;
cnd este un !ir de caractere constant (cuprins ntre ghilimele) care ini"ializeaz# un
tablou de caractere.
Ca o consecin"# a acestei defini"ii, aparent nu exist# nici o diferen"# ntre comportarea
operatorului [ ] cnd este aplicat unui tablou sau pointer. O expresie de tipul x[i] (unde x este
un tablou sau pointer) este, prin defini"ie, identic# cu ((x)+(i)), chiar dac# accesul memoriei
se face diferit (a!a cum am v#zut mai sus).
Dup# cum a"i observat ceva mai sus, am f#cut afirma"ia c# expresiile char s[ ] !i
char s snt identice. Oare de ce ntr-o astfel de situa"ie putem interschimba pointerii cu
tablourile? Deoarece tablourile snt transformate imediat n pointeri, un tablou nu este
transmis niciodat! ca parametru unei func"ii. Orice declara"ie de parametru care arat# ca un
tablou, ca n
int f(char a[ ]);
este tratat# de compilator ca !i cum ar fi un pointer. Aceast# conversie are loc numai n
declara"ia unui parametru formal al unei func"ii.
Trebuie s# men"ion#m faptul c#, de!i un tablou este o l-valoare, nu i putem atribui
valori. Acest lucru este adev#rat deoarece l-valorile se mpart n dou# categorii, modificabile
!i nemodificabile, iar tablourile fac parte din a doua categorie. De asemenea tablourile nu snt
nici pointeri constan"i, de!i numele unui tablou este constant pentru c# nu i putem asigna
valori.
Pn# acum am v#zut care snt asem#n#rile dintre un pointer !i un tablou. S# vedem !i
care snt deosebirile dintre acestea. Tablourile aloc# automat o zon# de memorie, iar acest#
zon# nu poate fi realocat# sau modificat# (m#rit# sau mic!orat#). Pointerilor trebuie s# le
atribuim n mod explicit valori la o zon# de memorie alocat# (de exemplu utiliznd func"ia
malloc, despre care o s# vorbim n Intr#ri !i ie!iri), dar ei pot primi alte valori (pot pointa spre
o alt# zon# de memorie) !i pot avea multe alte utiliz#ri n afar# de a servi ca baz# pentru un
bloc de memorie. n particular, un pointer la o zon# de memorie este deseori tratat ca !i cum ar
fi un tablou adev#rat, utiliznd operatorul [ ].
De!i pare ciudat, n C o expresie de tipul
5[abcdefgh]
este perfect legal#. Cum se poate ntmpla acest lucru? Ciud#"enia de mai sus este adev#rat#,
deoarece n C tabloul comut# cu indicele. S# ne reamintim c# ceva mai nainte am v#zut c# o
expresie de tipul a[e] este identic! cu ((a)+(e)), pentru orice expresie e !i orice expresie a,

78
atta timp ct una din ele este o expresie pointer, iar cealalt# este o expresie ntreag!.
Binen"eles c# utilizarea unor asemenea expresii ca cea de mai sus nu este indicat#, deoarece
programele devin foarte greu de n"eles, chiar !i de autorul lor (dup# o perioad# de timp).
5.5 Aritmetica adreselor
Dac# p este un pointer la un element dintr-un tablou, atunci p++ incrementeaz# pointerul
p astfel nct s# pointeze la urm#torul element, iar p+=i l incrementeaz# pe p astfel nct s#
pointeze cu i elemente dup# elementul curent. Astfel de construc"ii snt cele mai simple forme
n care este utilizat# aritmetica adreselor (opera"ii care pot fi f#cute cu adrese).
Unul din punctele forte ale limbajului C l constituie integrarea pointerilor, tablourilor,
precum !i aritmetica adreselor. Pentru a ilustra acest lucru, vom scrie un alocator rudimentar
de memorie. Acest alocator are dou# func"ii. Prima func"ie, pe care o vom numi alloc(n),
returneaz# un pointer la n pozi"ii caracter consecutive, care poate fi folosit pentru a memora
caractere. A doua func"ie, pe care o vom numi afree(p), elibereaz# zona de memorie alocat#
de alloc, pentru a putea fi folosit# n alte scopuri. Alocatorul este rudimentar deoarece
spa"iul ntre"inut de cele dou# func"ii este utilizat ca o stiv#, (last in, first out), adic# apelul la
afree trebuie f#cut pentru ultima zon# alocat# cu alloc. Biblioteca standard are dou# func"ii
analoage, malloc !i free, care nu au aceste restric"ii n utilizare. Despre aceste dou# func"ii
vom vorbi n Intr#ri !i ie!iri.
Implementarea cea mai simpl# este aceea n care cele dou# func"ii utilizeaz# un tablou
de caractere mare, pe care l vom numi allocbuf. Acest tablou este local acestor func"ii, !i
deoarece cele dou# func"ii lucreaz# cu pointeri, numele tabloului nu trebuie cunoscut de alte
func"ii. De aici rezult# c# tabloul poate fi declarat static, iar cele dou# func"ii mpreun# cu
tabloul pot fi ntr-un fi!ier surs# separat.
O alt# informa"ie de care avem nevoie este ct din tablou am utilizat. Pentru aceasta vom
folosi un pointer numit allocp, care va pointa la urm#torul element liber din tablou. Dac# alloc
nu poate aloca num#rul de caractere cerut, va returna zero, n caz contrar va returna valoarea
curent# a lui allocp, dup# care allocp va fi incrementat corespunz#tor.
#define ALLOCSIZE $0000
static char allocbuf[ALLOCSIZE];
static char allocp=allocbuf;
char alloc(int n)
{
if(allocbuf+ALLOCSIZEallocp>=n)
{
allocp+=n;
return allocpn;
}
else
return 0;
}
void afree(char p)
{
if(p>=allocbuf && p<allocbuf+ALLOCSIZE)
allocp=p;
}

79
n general un pointer poate fi ini"ializat ca orice alt# variabil#. Declara"ia
static char allocp=allocbuf;
define!te allocp ca un pointer la caracter !i l ini"ializeaz# la nceputul lui allocbuf. Aceast#
declara"ie poate fi scris# !i sub forma
static char allocp=&allocbuf[0];
datorit# faptului c# numele tabloului este adresa elementului zero.
Testul
if(allocbuf+ALLOCSIZEallocp>=n)
testeaz# dac# exist# loc suficient pentru cele n caractere. Dac# nu exist# spa"iul necesar este
returnat# valoarea zero. Limbajul C garanteaz# c# zero nu este niciodat! o adres# valid#
pentru date, deci zero poate fi folosit ca un semnal al unui eveniment anormal, n cazul acesta
lipsa de spa"iu.
Aici putem face o parantez# referitor la valoarea zero utilizat# pentru a semnala o
situa"ie anormal# n cazul lucrului cu pointeri.
Defini"ia limbajului C stabile!te c# pentru fiecare tip de pointer exist# o valoare
special#, numit# pointerul nul, care este distinct# de toate celelalte valori pe care le poate lua
un pointer !i care nu este adresa nici unui obiect C (variabil# sau func"ie). Aceasta nseamn#
c# operatorul de adres# & nu va returna niciodat# un pointer nul !i nici un apel cu succes la
func"iile care returneaz# pointeri. Astfel de func"ii returneaz# pointerul nul pentru a semnifica
faptul c# func"ia respectiv# a e!uat.
Pointerul nul este conceptual diferit de un pointer neini"ializat, deoarece el nu pointeaz#
la nici un obiect, pe cnd un pointer neini"ializat pointeaz# undeva (nu se !tie unde, dar
pointeaz#).
Pointerul nul este definit n biblioteca standard ca !i constanta NULL !i este de preferat
utilizarea acestei constante pentru a reprezenta un pointer nul, chiar dac# pe majoritatea
calculatoarelor aceast# constant# este definit# ca fiind zero.
Pointerii !i ntregii nu snt interschimbabili, deoarece un pointer nu este un ntreg.
Singura excep"ie de la aceast# regul# este valoarea zero. Constanta zero poate fi atribuit# unui
pointer !i un pointer poate fi comparat cu zero.
n exemplul de mai sus am folosil valoarea zero pentru a ilustra c# aceast# valoare nu
este valid# pentru un pointer propriu. n exemplele urm#toare vom folosi constanta NULL
pentru a indica faptul c# avem un pointer nul.
Teste ca
if(allocbuf+ALLOCSIZEallocp>=n)
sau
if(p>=allocbuf && p<allocbuf+ALLOCSIZE)
ne arat# unele aspecte ale aritmeticii adreselor. n primul rnd pointerii pot fi compara"i n
unele situa"ii. Dac# p !i q pointeaz# la membrii aceluia!i tablou, compara"iile ==, !=, <, <= etc.
au sens. De exemplu,
p<q
este adev#rat dac# p pointeaz# la un element al tabloului situat naintea elementului pointat de
q. Compara"ia unui pointer cu zero are sens numai n cazul egalit#"ii sau inegalit#"ii.
Nu are sens comparea a doi pointeri care nu pointeaz! la membrii aceluia#i tablou.

80
n al doilea rnd, se observ# c# un pointer !i un ntreg pot fi aduna"i sau sc#zu"i. Expresia
p+n
nseamn# adresa celui de-al n-lea element dup# elementul pointat de p. Acest lucru este
adev#rat indiferent de tipul elementelor pointate de p, adic# n este nmul"it, nainte de a fi
adunat la p, cu dimensiunea n octe"i a obiectelor pointate de p, dimensiune determinat# de
declara"ia lui p.
Sc#derea pointerilor este de asemenea valabil#, n acelea!i situa"ii ca !i compararea.
Dac# p !i q pointeaz# la membrii aceluia!i tablou !i p<q, atunci q p+1 este num#rul de
elemente dintre p !i q inclusiv. Folosind aceast# observa"ie, putem scrie o alt# versiune a
func"iei strlen:
int strlen(char s)
{
char p=s;
while(p!=\0)
p++;
return ps;
}
Deoarece num#rul de caractere din !ir poate s# fie prea mare pentru a ncape ntr-un int,
n headerul <stddef.h> este definit tipul ptrdiff_t care este suficient de mare pentru a putea
cuprinde diferen"a a doi pointeri. Putem de asemenea utiliza size_t pentru tipul returnat de
strlen (func"ia strlen din biblioteca standard utilizeaz# acest tip).
Compara"ia cu \0 din ciclul while fiind redundant#, putem scrie o versiune mai scurt# a
func"iei strlen:
int strlen(char s)
{
char p=s;
while(p)
p++;
return ps;
}
Aritmetica pointerilor se realizeaz# corect !i dac# lucr#m cu alte tipuri. De exemplu,
dac# p este un pointer la un tablou de float (care ocup# mai mult# memorie dect un char),
p++ va avansa la urm#torul element de tablou.
Opera"iile cu pointeri valide snt asignarea pointerilor de acela!i tip, adunarea sau
sc#derea unui ntreg la un pointer, sc#derea sau compararea a doi pointeri la membrii unui
aceluia!i tablou !i asignarea sau compararea cu zero.
Nu este legal! adunarea a doi pointeri, nmul"irea, mp!r"irea sau opera"iile pe bi"i,
precum #i adunarea cu un num!r real (float sau double).
De asemenea este ilegal s# asign#m un pointer de un tip (cu excep"ia tipului void) la un
pointer de un alt tip f#r# o construc"ie cast. Nu putem s# facem opera"ii aritmetice (ca de
exemplu incrementare sau decrementare) cu pointeri de tipul void, deoarece compilatorul nu
cunoa!te m#rimea obiectelor pointate.

81
5.6 $iruri de caractere
Prin #ir de caractere (sau pe scurt #ir) n"elegem un tablou de caractere terminat cu
caracterul nul \0 (un asemenea !ir mai este cunoscut !i sub denumirea de #ir C sau string).
5.6.1 Pointeri la caractere
Un #ir constant, ca de exemplu
Eu sint un sir
este un tablou de caractere, care n reprezentarea intern# este terminat cu caracterul nul \0,
astfel nct programele pot determina sfr!itul !irului. Deci spa"iul de memorie necesar este cu
1 mai mult dect num#rul de caractere cuprinse ntre ghilimele.
Una din utiliz#rile cele mai dese ale !irurilor constante este ca argumente n func"ii, ca
de exemplu n
printf(hello, world\n);
Cnd un !ir ca cel de mai sus apare ntr-un program, accesul la el se face printr-un
pointer la primul s#u caracter, deci func"ia printf recep"ioneaz# un pointer la nceputul
tabloului de caractere.
%irurile constante nu trebuie s# fie argumente ale func"iilor. Dac# avem declara"ia
char msg;
atunci instruc"iunea
msg=eu sint un sir;
atribuie variabilei msg un pointer la tabloul de caractere. Aceast# instruc"iune nu este o
copiere de !iruri, ci snt copia"i numai pointerii. Limbajul C nu furnizeaz# nici un operator
pentru a prelucra un !ir ntreg ca !i o unitate.
Trebuie s# facem distinc"ie ntre urm#toarele defini"ii:
char amsg[]=eu sint un sir;
char pmsg=eu sint un sir;
n defini"iile de mai sus, amsg este un tablou suficient de mare astfel nct s# cuprind#
toate caracterele !irului plus terminatorul \0. Caracterele din interiorul tabloului pot fi
schimbate, dar amsg va referi ntotdeauna aceea!i zon# de memorie. Pe de alt# parte, pmsg
este un pointer, ini"ializat ca s# pointeze la un !ir constant. Pointerul poate fi modificat astfel
nct s# pointeze n alt# parte, dar rezultatul este nedefinit dac# ncerc#m s# modific#m
con"inutul !irului. Trebuie s# remarc#m faptul c# dac# modific#m pointerul pmsg, !irul pointat
ini"ial nu va mai putea fi accesat, doar dac# adresa sa mai este salvat# ntr-o alt# variabil#
(pointer).
Pentru a ilustra mai multe aspecte referitoare la pointeri !i tablouri, vom studia mai
multe versiuni a dou# func"ii din biblioteca standard.
Prima func"ie este strcpy(s,t), care copiaz# !irul t n !irul s. Nu este suficient s# scriem
s=t, deoarece aceasta ar nsemna doar copierea pointerilor, nu a caracterelor. Pentru a putea
copia caracterele avem nevoie de un ciclu. Prima versiune a acestei func"ii, utiliznd tablouri,
este dat# mai jos.
void strcpy(char s,char t)
{

82
int i;
i=0;
while((s[i]=t[i])!=\0)
i++;
}
Versiunea cu pointeri a acestei func"ii este:
void strcpy(char s,char t)
{
while((s=t)!=\0)
{
s++;
t++;
}
}
Deoarece argumentele snt transmise prin valoare, putem utiliza parametrii s !i t n orice
mod dorim, n particular ca mai sus.
n practic# func"ia strcpy nu este scris# ca mai sus, ci sub o form# care seam#n# cu cea
de mai jos, form# care este mai utilizat# de programatorii C cu experien"#.
void strcpy(char s,char t)
{
while((s++=t++)!=\0)
;
}
Dup# cum se observ#, incrementarea a fost mutat# n partea de test a ciclului. Valoarea
t++ este caracterul la care pointeaz# t nainte de a fi incrementat. Operatorul postfix ++ nu l
modific# pe t dect dup! ce utilizeaz# valoarea t. Acela!i lucru se ntmpl# !i cu s++.
Deoarece compararea cu \0 este redundant#, vom ob"ine versiunea final# a func"iei
strcpy, a!a cum este !i implementat# n biblioteca standard.
void strcpy(char s,char t)
{
while(s++=t++)
;
}
Func"ia strcpy din biblioteca standard returneaz# !irul destina"ie !i are prototipul n
<string.h>.
A doua func"ie pe care o vom examina este strcmp(s,t), care compar# !irurile s !i t !i
returneaz# o valoare negativ#, pozitiv# sau zero, dup# cum s este lexicografic mai mic, egal
sau mai mare dect t. Ca !i la func"ia precedent#, prima versiune este cu tablouri.
int strcmp(char s,char t)
{
int i;
for(i=0;s[i]==t[i];i++)
if(s[i]==\0)
return 0;
return s[i]t[i];
}

83
Versiunea cu pointeri a func"iei este:
int strcmp(char s,char t)
{
for(;s==t;s++,t++)
if(s==\0)
return 0;
return st;
}
Se pot utiliza !i alte combina"ii ale operatorilor de incrementare !i decrementare aplica"i
unui pointer. Astfel, perechea de expresii
p++=val; / push /
val=p; / pop /
reprezint# secven"a standard pentru a pune o valoare n stiv# sau pentru a o scoate din stiv#.
n afara celor dou# func"ii de mai sus, n fi!ierul header <string.h> se mai g#sesc !i alte
func"ii utile pentru lucrul cu !iruri.
Exerci#iul 50. Scrie"i o func"ie strend(s,t) care returneaz# 1 dac# !irul t apare la sfr!itul
!irului s !i 0 n caz contrar.
n Variabile externe, statice !i registru am v#zut cum se evalueaz# o expresie aritmetic#
scris# n nota"ie polonez# invers#. n continuare vom scrie un program care transform# o
expresie aritmetic# din nota"ie infix n nota"ie polonez# invers#. Pentru simplificare se
presupune c# operanzii snt forma"i dintr-un singur caracter (liter# sau cifr#), iar operatorii snt
cei patru operatori aritmetici binari +, , !i /.
#define STMAX 200
#include <stdio.h>
#include <process.h>
char sir[STMAX],polonez[STMAX],stack[STMAX];
char pol=polonez,st=stack,psir=sir;
char srch_nxt_chr(void)
{
while(psir && psir++== )
;
return (psir$);
}
char pop(void)
{
if(st==stack)
{
puts(Stiva vida!\n);
exit($);
}
return st;
}
void push(char c)
{
if(st==stack+STMAX)

84
{
puts(Stiva plina!\n);
exit(2);
}
++st=c;
}
pondere(char ch)
{
if(ch==+ || ch==)
return $;
else if(ch==()
return 0;
else
return 2;
}
pot_introduce(char ch)
{
if(st>stack)
if(pondere(ch) > pondere(st))
return $;
else
return 0;
else
return $;
}
operator(char ch)
{
if(ch==+ || ch== || ch== || ch==/)
return $;
else
return 0;
}
main()
{
char ch,p;
puts(Introduteti o expresie aritmetica:);
fgets(sir,sizeof(sir),stdin);
if((p=strchr(sir,\n))!=NULL)
p=\0;
do {
ch=srch_nxt_chr();
if(operator(ch))
if(pot_introduce(ch))
push(ch);
else
{
do
{
pol++=pop();
} while(st>stack&&pondere(ch)<=pondere(st)&&st!=();
push(ch);
}
else
if(ch==))

85
{
char golire;
while(st!=( && st>stack)
pol++=pop();
golire=pop();
}
else
if(ch==()
push(ch);
else
pol++=ch;
} while(psir);
while(st>stack && st!=()
pol++=pop();
pol=\0;
puts(polonez);
return 0;
}
Exerci#iul 51. Scrie"i o variant# recursiv# a programului anterior.
5.6.2 C%utare n "iruri
De cele mai multe ori datele care se prelucreaz# se caracterizeaz# numai prin faptul c#
ele se reprezint# sub form# de !ir, adic# o secven"# liniar# (de obicei foarte lung#) de
caractere.
Este evident c# !irurile ocup# un loc central n editoarele de texte (word-processing
systems), care furnizeaz# o serie de facilit#"i pentru manipularea textelor. Un text este alc#tuit
din litere, cifre !i caractere speciale, toate numite pe scurt caractere. Textele pot fi foarte lungi
(de exmplu cartea aceasta con"ine peste 340,000 de caractere), deci pentru manipularea lor
este nevoie de algoritmi eficien"i.
Un alt tip de !ir este #irul binar, care const# dintr-o secven"# de cifre 0 !i 1. Astfel de
!iruri apar n mod natural n multe aplica"ii. De exemplu, imaginile snt reprezentate deseori
ca !iruri binare.
Din multe puncte de vedere un !ir de caractere !i un !ir binar pot fi tratate asem#n#tor.
De exemplu, n ceea ce prive!te c!utarea unui !ir ntr-un alt !ir, algoritmii care se utilizeaz#
snt aceea!i pentru ambele tipuri de !iruri.
Una din opera"iile fundamentale care este f#cut# cu !irurile este cea de c#utare, adic#
determinarea primei apari"ii a unui !ir ntr-un alt !ir. Algoritmii de c#utare pot fi extin!i
pentru a deterimna toate apari"iile unui !ir ntr-un alt !ir, datorit# faptului c# opera"ia de
c#utare este secven"ial#.
n Func"ii am scris func"ia strindex(s,t) care determin# prima apari"ie a !irului s n !irul
t. Vom rescrie aceast# func"ie utiliznd pointeri:
int strindex(char s,char t)
{
char i,j,k;
for(i=s;*i!='\0';i++)
{
for(j=i,k=t;k!='\0' && j==k;j++,k++)
;

86
f(k>t && k=='\0')
return is;
}
return $;
}
O alt# variant# a acestei func"ii este dat# mai jos.
int strindex(char s,char t)
{
char i,j;
for(i=s,j=t;j!='\0' && i!='\0';i++,j++)
while(i!=j)
{
i=j-t$;
j=t;
}
if(j=='\0')
return is(jt);
else
return $;
}
ntr-o aplica"ie de editare de texte, ciclul mai interior din aceast# func"ie este parcurs
destul de rar, iar timpul de execu"ie este aproape egal cu num#rul de caractere din text
examinate.
Un algoritm de c#utare mai performant este algoritmul Knuth Moris Pratt, care este dat
n cele ce urmeaz#. Deoarece scopul principal al c#r"ii nu l constituie descrierea algoritmilor,
ci prezentarea limbajului C !i a unor tehnici de scriere de programe eficiente, nu d#m aici
descrierea acestui algoritm. Algoritmul poate fi g#sit n bibliografia indicat#.
void initnext(char p,char next)
{
int i,j,M=strlen(p);
next[0]=$;
for(i=0,j=$;i<M;i++,j++,next[i]=(p[i]==p[j]) ? next[j] : j)
while((j>=0) && (p[i]!=p[j]))
j=next[j];
}
int strindex(char s,char t)
{
char next;
int i,j,M=strlen(t),N=strlen(s);
if((next=malloc(M+$))==NULL)
return $;
initnext(t,next);
for(i=0,j=0;j<M && i<N;i++,j++)
while((j>=0) && (s[i]!=t[j]))
j=next[j];
if(j==M)
return iM;
else
return $;

87
}
Exerci#iul 52. Rescrie"i func"ia strindex(s,t) folosind algoritmul Knuth Moris Pratt
descris mai sus, astfel nct s# utiliza"i pointeri n locul tablourilor.

Exerci#iul 53. Scrie"i func"ia strrindex(s,t) care s# returneze ultima apari"ie a !irului t n
!irul s, sau 1 n cazul n care t nu este inclus n s, folosind algoritmul Knuth Moris Pratt
descris mai sus. Scrie"i o versiune cu tablouri !i o versiune cu pointeri.
5.6.3 Gre"eli tipice n utilizarea "irurilor C
A!a cum a"i observat, lucrul cu pointeri nu este tocmai u!or. Dac# ntr-un program
utiliz#m pointeri ntr-un mod eronat, acel program se va comporta ciudat !i astfel de gre!eli
vor fi totodat# greu de detectat.
Pentru a preveni utilizarea eronat# a !irurilor sau pentru a detecta mai u!or unele gre!eli
care pot apare, n continuare vom prezenta tipuri de gre!eli mai frecvente !i cum s# le evit#m.
Men"ion#m faptul c# func"iile utilizate n exemplele care urmeaz# snt func"ii din biblioteca
standard !i snt descrise n Intr#ri !i ie!iri.
S# consider#m secven"a de mai jos, care este eronat#:
char answer;
printf(Introduceti ceva:\n);
gets(answer);
printf(Ati introdus \%s\\n,answer);
Pointerul answer, care este transmis func"iei gets ca parametru !i unde trebuie memorate
caracterele citite, nu pointeaz# la nici o zon# de memorie. Aceasta nseamn# c# func"ia gets nu
are unde depune rezultatul. Pentru ca secven"a de mai sus s# fie corect#, ea poate fi scris#
ntr-un mod asem#n#tor cu cel de mai jos:
char answer[$00],p;
printf(Introduceti ceva:\n);
fgets(answer,sizeof(answer),stdin);
if((p=strchr(answer,\n))!=NULL)
p=\0;
printf(Ati introdus \%s\\n,answer);
Not#m c# n exemplul de mai sus am utilizat fgets n locul lui gets, deoarece func"ia gets
nu controleaz# dep#!irea bufferului n care se face citirea (nu verific# dac# au fost introduse
prea multe caractere). Dar, spre deosebire de gets, func"ia fgets nu !terge automat caracterul
\n, a!a c# a trebuit s# facem acest lucru manual (testul if)
Secven"a care urmeaz# este de asemenea eronat#.
char s$=Hello,;
char s2=world!;
char s3=strcat(s$,s2);
Gre!eala din secven"a de mai sus este dat# de faptul c# !irul care rezult# n urma
concaten#rii nu este alocat corespunz#tor. n C !irurile nu snt redimensionate automat,
compilatorul alocnd spa"iu numai pentru obiectele pentru care se cere explicit acest lucru.

88
Programatorul trebuie s# aloce explicit spa"iu suficient pentru rezultatele opera"iilor de la
execu"ie, cum ar fi concatenarea !irurilor, citirea !irurilor etc. Func"ia strcat nu aloc#
memorie. Al doilea !ir este ad#ugat la sfr!itul primului !ir, n zona de memorie alocat#
acestuia. Astfel, dac# dorim s# corect#m secven"a de mai sus, putem proceda astfel:
char s$[20]=Hello,;
Ca o observa"ie, toate func"iile care lucreaz# cu !iruri au declara"i ca parametri n
prototipul lor pointeri la char !i nu tablouri. n general, cnd lucr#m cu pointeri, trebuie s#
"inem cont ntotdeauna de alocarea memoriei, pentru a nu avea surprize nepl#cute la execu"ia
programului.
Urm#toarea secven"# con"ine o eroare ceva mai greu de detectat.
char f()
{
char buf[$0];
/.../
return buf;
}
Gre!eala din aceast# secven"# const# n faptul c# zona de memorie la care func"ia
returneaz# un pointer nu este corect alocat#. Tabloul buf este local func"iei, ceea ce nseamn#
c# el apare la intrarea n func"ie !i dispare cnd func"ia se termin#. Deci zona alocat# variabilei
buf nu va mai exista dup# ce func"ia se termin# !i de aici rezult# c# pointerul returnat va avea
o valoare nedefinit#. Mai mult, pointerul returnat de o func"ie trebuie s# fie un pointer la o
zon# static#, la o zon# transmis# ca parametru func"iei, sau la o zon# ob"inut# prin apelul
func"iei malloc !i nu poate fi un pointer la o zon# local# func"iei (un tablou local ca n
exemplul de mai sus).
O alt# gre!eal# care poate apare destul de frecvent este n secven"a
char a[3]=abc;
Unde poate fi gre!eala !i n ce const# ea? Dac# examin#m secven"a cu aten"ie, vedem c#
a este un tablou de trei caractere, ini"ializat cu caracterele a, b !i c. Acest tablou nu
con"ine terminatorul nul \0, deoarece dimensiunea lui este trei !i este ini"ializat cu exact trei
caractere. Caracterul \0 ar fi fost al patrulea, dar, nemaiavnd loc, nu a fost inclus n tablou.
Astfel a nu este un #ir C (nu este un tablou de caractere care s# se termine cu \0) !i deci nu
poate fi folosit n func"ii ca strlen, strcpy, printf cu argumentul %s etc. (utilizarea tabloului n
astfel de func"ii poate avea efecte neprev#zute).
5.7 Tablouri de pointeri. Pointeri la pointeri
Deoarece pointerii snt la rndul lor ni!te variabile, ei pot fi p#stra"i n tablouri, la fel ca
orice alt# variabil#. Vom ilustra acest lucru scriind un program care va sorta un set de linii din
intrare n ordine alfabetic#.
n Instruc"iuni repetitive am prezentat func"ia shellsort care sorteaz# un tablou de
ntregi, iar n Recursivitate am mbun#t#"it func"ia de sortare, prezentnd func"ia qsort. Pentru
a sorta linii de text putem folosi acela!i algoritm de sortare. Spre deosebire de ntregi, liniile
de text au lungime variabil# !i nu pot fi comparate sau mutate la fel ca ntregii. Pentru a putea

89
realiza sortarea, avem nevoie de o reprezentare a datelor care s# ne permit# s# facem acest
lucru ct mai eficient !i convenabil. Aceast# reprezentare este dat# de tablourile de pointeri.
Dac# liniile care dorim s# le sort#m snt memorate una dup# alta ntr-un tablou de
caractere lung, atunci fiecare linie poate fi accesat# printr-un pointer la primul s#u caracter.
Ace!ti pointeri pot fi la rndul lor memora"i ntr-un tablou. Putem compara dou# linii
transmi"nd pointerii lor func"iei strcmp. Dac# dou# linii nu snt ordonate, putem s# le
schimb#m prin simpla schimbare a pointerilor lor. Acest mod de reprezentare a datelor
elimin# problema complicat# a schimb#rii liniilor (ncerca"i s# scrie"i programul care s#
schimbe liniile de lungime variabil# memorate ntr-un tablou lung de caractere).
Programul poate fi mp#r"it n trei pa!i:
cite#te toate liniile din intrare
le sorteaz!
le tip!re#te
Ca de obicei, cel mai bine este s# mp#r"im programul n func"ii care s# realizeze
opera"iile de mai sus. Pentru moment vom l#sa deoparte problema sort#rii !i ne vom ocupa de
organizarea datelor, de introducerea !i extragerea lor.
Func"ia de introducere trebuie s# citeasc# !i s# salveze caracterele din fiecare linie !i s#
construiasc# tabloul de pointeri. De asemenea func"ia trebuie s# numere liniile citite, deoarece
num#rul lor este necesar la sortare !i tip#rire. Func"ia de intrare poate lucra cu un num#r finit
de linii, deci ea trebuie s# returneze un num#r de linii ilegal, de exemplu 1, dac# au fost
introduse prea multe linii.
Func"ia de tip#rire trebuie s# tip#reasc# liniile n ordinea n care ele apar n tabloul de
pointeri.
Aceste dou# func"ii, precum !i func"ia main, snt date mai jos.
#include <stdio.h>
#include <string.h>
#define MAXLINES 5000
char lineptr[MAXLINES];
int readlines(char lineptr[],int nlines);
void writelines(char lineptr[],int nlines);
void qsort(char lineptr[],int left,int right);
main()
{
int nlines;
if((nlines=readlines(lineptr,MAXLINES))>=0)
{
qsort(lineptr,0,nlines$);
writelines(lineptr,nlines);
return 0;
}
else
{
printf(error: prea multe linii pentru sortare\n);
return $;
}
}

90
#define MAXLEN $000
int getline(char ,int);
char alloc(int);
int readlines(char lineptr[],int maxlines)
{
int len,nlines;
char p,line[MAXLEN];
nlines=0;
while((len=getline(line,MAXLEN))>0)
if(nlines>=maxlines || (p=alloc(len)==NULL)
return $;
else
{
line[len$]=\0;
strcpy(p,line);
lineptr[nlines++]=p;
}
return nlines;
}
void writelines(char lineptr[],int nlines)
{
int i;
for(i=0;i<nlines;i++)
printf(%s\n,lineptr[i]);
}
Func"ia getline este dat# n Ini"iere n C. Principala noutate din acest program este
declara"ia
char lineptr[MAXLINES];
Aceast# declara"ie ne spune c# lineptr este un tablou de MAXLINES elemente, fiecare
element fiind un pointer la char. Deci lineptr[i] este un pointer la char, iar lineptr[i]este
caracterul la care pointeaz#.
Deoarece lineptr este numele unui tablou, el poate fi tratat ca un pointer !i deci func"ia
writelines poate fi scris# sub forma de mai jos.
void writelines(char lineptr[],int nlines)
{
while(nlines>0)
printf(%s\n,lineptr++);
}
Avnd func"iile de intrare !i de ie!ire, putem s# ne ocup#m !i de func"ia de sortare.
Func"ia qsort din Recursivitate necesit# pu"ine schimb#ri, printre care amintim faptul c#
pentru comparare trebuie s# utiliz#m func"ia strcmp.
void swap(char v[],int i,int j);
void qsort(char v[],int left,int right)
{
int i,last;
if(left>=right)
return;
swap(v,left,(left+right)/2);

91
last=left;
for(i=left+$;i<=right;i++)
if(strcmp(v[i],v[left])<0)
swap(v,++last,i);
swap(v,left,last);
qsort(v,left,last$);
qsort(v,last+$,right);
}
void swap(char v[],int i,int j)
{
char temp;
temp=v[i];
v[i]=v[j];
v[j]=temp;
}
Exerci#iul 54. Modifica"i func"ia readlines pentru a memora liniile ntr-un tablou
furnizat de main, n loc de a utiliza alloc.
Pentru a ilustra utilizarea tablourilor de pointeri prezent#m n continuare un program
pu"in mai complex, care transform# o expresie aritmetic# din nota"ie polonez# invers# n
nota"ie infix, f!r! s# introducem paranteze suplimentare. Pentru simplificare, se presupune c#
operanzii snt forma"i dintr-un caracter (liter# sau cifr#), iar operatorii snt +, , !i /.
Ce nseamn# f#r# introducerea de paranteze suplimentare? S# consider#m expresia
a+(bc)
Se observ# c# paranteza din expresie nu este necesar#. Forma polonez# invers# a acestei
expresii este
abc+
Dac# vom considera acum aceea!i expresie, dar scris# f#r# parantezele suplimentare,
a+bc
forma polonez# invers# a acestei expresii este
ab+c
care, dup# cum se poate observa u!or, este diferit# de cea a expresiei scris# cu parantezele
suplimentare.
Programul prezentat mai jos cite!te expresia n nota"ie polonez# invers# !i ne va furniza
(n cazul exemplului de mai sus) cea de-a doua form# a expresiei n nota"ie infix (forma f#r#
parantezele suplimentare), indiferent de nota"ia polonez# utilizat#.
#define STMAX 2000
#define OPERAND 0
#define ADUNARE $
#define SCADERE 2
#define INMULTIRE 3
#define IMPARTIRE 4
#define PESTE $0
#define DUPA $$
#include <stdio.h>
#include <stdlib.h>

92
#include <string.h>
char sir[STMAX],polonez[STMAX],stack[STMAX];
char unu[STMAX],doi[STMAX];
char pol=polonez,st=stack;
char next_char(void)
{
while(pol && (pol== || pol==\t))
pol++;
return pol++;
}
void pun_in_paranteza(char c, int cum)
{
if(cum==PESTE)
strcpy(sir,();
else
strcat(sir,();
strcat(sir,c);
strcat(sir,));
}
char pop(void)
{
if(st==stack)
{
puts(\aStiva vida!\n);
exit($);
}
return st;
}
void push(char c)
{
if(st==stack+STMAX)
{
puts(\aStiva plina!\n);
exit(2);
}
st=malloc(strlen(c)+$);
if(st==NULL)
{
puts(\aMemorie insuficienta!\n);
exit(3);
}
strcpy(st++,c);
}
am_gasit_plus_minus(char s)
{
char p=s;
while(p)
if(p==+ || p==)
return $;
else if(p==()
{
char q=p;
int n=$;
do {

93
if(q==()
n++;
else if(q==))
n;
q++;
} while(n!=$);
p=q;
}
else
p++;
return 0;
}
op(char ch)
{
if(ch==+)
return ADUNARE;
else if(ch==)
return SCADERE;
else if(ch==)
return INMULTIRE;
else if(ch==/)
return IMPARTIRE;
else
return OPERAND;
}
main()
{
char ch,buf[2]= ,p;
int tip;
puts(Introduceti o expresie aritmetica );
printf(in notatie poloneza inversa:\n);
fgets(polonez,sizeof(polonez),stdin);
if((p=strchr(polonez,\n))!=NULL)
p=\0;
do {
buf=ch=next_char();
if((tip=op(ch))!=OPERAND)
{
strcpy(doi,pop());
strcpy(unu,pop());
if(tip==ADUNARE)
{
strcpy(sir,unu);
strcat(sir,buf);
strcat(sir,doi);
}
else if(tip==SCADERE)
{
strcpy(sir,unu);
strcat(sir,buf);
if(am_gasit_plus_minus(doi))
pun_in_paranteza(doi,DUPA);
else
strcat(sir,doi);
}
else
{

94
if(am_gasit_plus_minus(unu))
pun_in_paranteza(unu,PESTE);
else
strcpy(sir,unu);
strcat(sir,buf);
if(am_gasit_plus_minus(doi) ||
tip==IMPARTIRE && strlen(doi)>$)
pun_in_paranteza(doi,DUPA);
else
strcat(sir,doi);
}
push(sir);
}
else
push(buf);
} while (pol);
strcpy(sir,pop());
if(st!=stack)
{
puts(\aExpresie eronata!\n);
return 4;
}
puts(sir);
return 0;
}
Exerci#iul 55. Modifica"i programul anterior astfel nct s# accepte operanzi care
respect# regulile din C (identificatorii pot avea mai mult de un caracter, iar numerele pot fi
ntregi sau reale, caz n care poate fi folosit# !i nota"ia !tiin"ific#).

Exerci#iul 56. Scrie"i o variant# recursiv# a programului anterior.

Exerci#iul 57. Scrie"i un program C care elimin# parantezele suplimentare dintr-o
expresie aritmetic# scris# n C. Expresia poate con"ine !i func"ii matematice ca sin, cos, exp
etc., descrise n <math.h>, iar operanzii pot fi numere (ntregi sau reale, caz n care poate fi
folosit# !i nota"ia !tiin"ific#) sau identificatori C.

Exerci#iul 58. Scrie"i un program C care simuleaz# func"ionarea unui calculator
!tiin"ific de buzunar, care poate con"ine !i func"ii matematice ca sin, cos, exp etc., descrise n
<math.h>, iar operanzii pot fi numere ntregi sau reale (care pot fi !i n nota"ie !tiin"ific#).

Exerci#iul 59. Scrie"i un program C care simuleaz# func"ionarea unui calculator
!tiin"ific de buzunar care lucreaz# cu numere mari (reprezentate ca !ir de caractere). Operanzii
pot fi numere ntregi, iar operatorii care pot apare n expresie snt +, , , / !i %. Opera"iile se
vor face utiliznd reprezentarea numerelor ca !ir de caractere.


95
Exerci#iul 60. Scrie"i un program C care cite!te o expresie aritmetic# scris# n C,
calculeaz# valoarea ei !i afi!eaz# rezultatul. Expresia poate con"ine !i func"ii matematice ca
sin, cos, exp etc., descrise n <math.h>, iar operanzii pot fi numere (ntregi sau reale, caz n
care poate fi folosit# !i nota"ia !tiin"ific#) sau identificatori C. n cazul n care expresia con"ine
variabile, pentru fiecare variabil# se va cere introducerea valorii sale. Operatorii care pot apare
n expresie snt +, , , / !i %.
5.7.1 Ini#ializarea tablourilor de pointeri
S# consider#m problema scrierii unei func"ii month_name(n) care ne returneaz# un
pointer la un !ir de caractere care con"ine numele celei de-a n-a lun# din an. Numele lunilor le
putem p#stra ntr-un tablou static intern de !iruri de caractere (n realitate un tablou de pointeri
la !iruri de caractere), pe care l vom ini"ializa. Deoarece nu vom preciza dimensiunea
tabloului, compilatorul va calcula dimensiunea num#rnd ini"ializatorii. De remarcat c#
dimensiunea este egal# cu 13, primul element fiind folosit n cazul n care num#rul lunii este
incorect.
char month_name(int n)
{
static char nume[]=
{
luna ilegala,
ianuarie, februarie, martie,
aprilie, mai, iunie,
iulie, august, septembrie,
octombrie, noiembrie, decembrie
}
return (n<$ || n>$2)? nume[0]:nume[n];
}
5.8 Tablouri multidimensionale
Putem utiliza tablouri multidimensionale n C, de!i acestea snt mult mai pu"in utilizate
dect tablourile de pointeri. n acest paragraf vom vedea cteva din propriet#"ile lor !i modul n
care se utilizeaz#.
5.8.1 No#iuni generale
S# consider#m problema conversiei datei, din zi din lun# n zi din an !i invers, din zi din
an n zi din lun#. De exemplu, 1 Martie este a 60-a zi dintr-un an nebisect !i a 61-a zi dintr-un
an bisect.
Pentru conversie vom utiliza dou# func"ii. Prima func"ie, day_of_year, converte!te ziua
!i luna n zi din an, iar a doua func"ie, month_day, face conversia zilei din an n zi din lun#.
Aceast# func"ie returneaz# dou# rezultate, luna !i ziua. De aceea aceste dou# argumente vor fi
transmise prin intermediul pointerilor, iar func"ia va avea tipul void.
Cele dou# func"ii au nevoie de acelea!i informa"ii, !i anume o tabel# cu num#rul de zile
din fiecare lun#. Deoarece num#rul de zile din luna februarie este diferit pentru anii bisec"i

96
fa"# de anii nebisec"i, vom utiliza un tablou bidimensional n care vom p#stra zilele din fiecare
lun#.
static char daytab[2][$3]=
{
{0,3$,28,3$,30,3$,30,3$,3$,30,3$,30,3$},
{0,3$,29,3$,30,3$,30,3$,3$,30,3$,30,3$}
};
int day_of_year(int an,int luna,int zi)
{
int i,bisect;
bisect=an%4==0 && an%$00!=0 || an%400==0;
for(i=$;i<luna;i++)
zi+=daytab[bisect][i];
return zi;
}
void month_day(int an,int zi,int pluna,int pzi)
{
int i,bisect;
bisect=an%4==0 && an%$00!=0 || an%400==0;
for(i=$;zi>daytab[bisect][i];i++)
zi=daytab[bisect][i];
pluna=i;
pzi=zi;
}
Deoarece valoarea aritmetic# a unei expresii logice este n C zero sau unu, putem utiliza
aceast# valoare ca indice de tablou.
Tabloul daytab trebuie s# fie extern ambelor func"ii, pentru a putea fi utilizat de
amndou#. Am declarat elementele tabloului de tip char pentru a ilustra cum putem utiliza
tipul char pentru a lucra cu numere ntregi mici.
Dup# cum se observ#, daytab este un tablou bidimensional, primul pe care l-am ntlnit
pn# acum. n C un tablou bidimensional este n realitate un tablou unidimensional, fiecare
element al s#u fiind un tablou unidimensional. Spre deosebire de alte limbaje ca Pascal, indicii
de tablou trebuie scri!i separat, adic# sub forma
daytab[linie][coloana]
Un tablou se ini"ializeaz# punnd lista ini"ializatorilor n acolade. Fiecare linie a unui
tablou bidimensional este ini"ializat# de o list# corespunz#toare.
Dac# transmitem ca parametru un tablou bidimensional, declara"ia parametrului n
func"ie trebuie s# includ# num!rul de coloane. Num#rul de linii nu este semnificativ, deoarece
este transmis un pointer la un tablou de linii n care fiecare linie este un tablou. n cazul
particular n care transmitem ca parametru tabloul daytab, ceea ce se transmite este un pointer
la obiecte care snt tablouri de 13 ntregi. Deci, dac# transmitem ca parametru tabloul daytab
func"iei f, declara"ia func"iei poate ar#ta astfel:
f(int daytab[2][$3])
{
...
}

97
Deoarece num#rul liniilor nu are importan"#, declara"ia func"iei poate avea !i forma
f(int daytab[][$3])
{
...
}
sau
f(int (daytab)[$3])
{
...
}
Aceast# ultim# form# ne spune c# daytab este un pointer la un tablou de 13 ntregi.
Observ#m c# parantezele snt necesare, deoarece, f#r# ele, declara"ia
int daytab[$3]
este un tablou de 13 pointeri la ntregi, ceea ce este altceva.
5.8.2 Pointeri "i tablouri multidimensionale
Cei care nva"# C snt deseori deruta"i de diferen"a dintre un tablou de pointeri !i un
tablou bidimensional. S# consider#m defini"iile
int a[$0][20];
int b[$0];
ntr-o astfel de situa"ie, a[3][4] !i b[3][4] snt referiri corecte la un int. Observ#m c# a
este un tablou bidimensional real, adic# se aloc# memorie pentru 200 de ntregi pentru
elementele sale. Defini"ia lui b aloc# numai un tablou de 10 pointeri, care trebuie ini"ializa"i
explicit, fie static, fie la execu"ie. Presupunnd c# fiecare element al lui b pointeaz# la un
tablou de 20 de ntregi, memoria necesar# este de 200 de ntregi, plus 10 loca"ii pentru
pointerii tabloului b. Avantajul utiliz#rii tablourilor de pointeri este acela c# liniile tabloului
pot avea lungimi diferite. Deci fiecare element al lui b poate fi un pointer c#tre tablouri de
dimensiuni diferite.
Compara"ia dintre un tablou bidimensional !i un tablou de pointeri am f#cut-o pentru un
tablou de ntregi. Utilizarea mult mai frecvent# a tablourilor de pointeri este memorarea
!irurilor de caractere de lungimi diverse, a!a cum am f#cut n func"ia month_name. ntr-o
astfel de situa"ie, prin utilizarea tablourilor de pointeri se face economie de memorie.
ntr-adev#r, face"i o compara"ie a memoriei utilizat# n cazul defini"iilor de mai jos:
char a[]={unu, doi, trei, patru};
int b[][$0]={unu, doi, trei, patru};
5.8.3 Sfaturi practice n utilizarea tablourilor multidimensionale
Gre!elile care apar cel mai frecvent n utilizarea tablourilor multidimensionale snt
generate de confuzia care se face ntre un tablou !i un pointer.
Una din situa"iile n care se gre!e!te frecvent este atunci cnd este transmis un tablou
bidimensional ca parametru unei func"ii. Func"ia care recep"ioneaz# ca parametru un tablou
bidimensional nu poate s# declare acel parametru ca fiind un pointer la pointer. Gre!eala

98
const# n aplicarea recursiv# a regulii de transformare a unui tablou ntr-un pointer. n cazul
unui tablou bidimensional, acesta se transform# ntr-un pointer la un tablou !i nu ntr-un
pointer la pointer.
Se pot scrie func"ii care s# accepte un tablou bidimensional a c#rui dimensiuni s# nu fie
cunoscute dect n momentul execu"iei. O modalitate este de a transmite func"iei un pointer la
elementul [0][0], mpreun# cu dimensiunile tabloului, iar n func"ie s# simul#m lucrul cu
indici, a!a cum se poate vedea n secven"a de mai jos.
void f(int parr,int lin,int col)
{
...
/ arr[i][j] este in realitate parr[icol+j] /
}
Func"ia de mai sus se apeleaz# cu instruc"iunea
f(&arr[0][0],LIN,COL);
De notat c# func"ia f lucreaz# cu tabloul multidimensional manual, iar accesarea
(&arr[0][0])[x] este nedefinit# dac# x>COL.
Men"ion#m faptul c# n cazul n care lucr#m cu pointeri la tablou, incrementarea
pointerului are ca efect saltul peste ntregul tablou !i este util# dac# lucr#m cu tablouri de
tablouri.
Putem s# aloc#m dinamic un tablou bidimensional n mai multe moduri. Mai jos
prezent#m patru posibilit#"i de alocare dinamic# a unui tablou de ntregi. n aplica"ii practice
n care este nevoie de asemenea aloc#ri, se poate utiliza oricare dintre acestea.
int a=(int )malloc(linsizeof(int ));
for(i=0;i<lin;i++)
a[i]=(int )malloc(colsizeof(int));
ntr-o secven"# real# de program, trebuie analizat# valoarea returnat# de malloc !i, n
caz nesatisf#c#tor (cnd nu mai este memorie suficient#), programul trebuie oprit. Deoarece
dorim numai s# ilustr#m declararea tabloului bidimensional, presupunem c# func"ia malloc nu
ne va da eroare.
O a doua posibilitate, n care tabloul ocup# o zon# contigu# de memorie, ca n cazul
unui tablou static, este
int a=(int )malloc(linsizeof(int ));
a[0]=(int )malloc(lincolsizeof(int));
for(i=0;i<lin;i++)
a[i]=a[0]+icol;
n ambele cazuri de mai sus, elementele tabloului se pot accesa prin a[i][j]. Dac# nu
dorim s# folosim indirectarea dubl#, putem simula un tablou bidimensional ntr-unul singur:
int a=(int )malloc(linsizeof(int ));
n cazul acesta, indirectarea dubl# trebuie s# o facem manual, prin a[icol+j]. Un tablou
bidimensional l putem declara !i prin utilizarea de pointeri la tablouri:
int (a)[COL]=(int ()[COL])malloc(linsizeof(a));

99
n acest caz, toate dimensiunile cu excep"ia primeia trebuie cunoscute la compilare.
5.9 Argumentele liniei de comand%
n sistemele de operare care suport# limbajul C exist# o modalitate de a transmite unui
program argumente (parametri) n linia de comand#. Cnd un program !i ncepe execu"ia,
func"ia main este apelat# cu dou# argumente. Primul argument, de tipul int, numit
conven"ional argc, este num#rul argumentelor din linia de comand# cu care a fost lansat
programul n execu"ie. Al doilea argument, numit conven"ional argv, este un pointer la un
tablou de !iruri de caractere care con"ine argumentele, cte unul pe !ir.
Argumentele din linia de comand! snt desp!r"ite prin spa"iu sau tab. Dac! dorim ca
spa"iul s! fac! parte dintr-un argument, atunci acel argument trebuie inclus n ghilimele.
De exemplu, #irul Acesta este un argument formeaz! un singur argument dac! este scris
n linia de comand! ncadrat de ghilimele (ca mai sus) #i 4 argumente n caz contrar (avem
4 cuvinte).
Caracterul \ dintr-un argument este transformat intern n \\. De ce? S# ilustr#m utilizarea
argumentelor n linia de comand# scriind un program echo, care tip#re!te argumentele cu care
a fost apelat. Argumentele vor fi separate de spa"ii. De exemplu, comanda
echo hello, world
va afi!a
hello, world
Prin conven"ie, argv[0] este numele cu care a fost apelat programul, deci argc este cel
pu"in 1. Dac# argc=1, nseamn# c# nu avem argumente n linia de comand#. n exemplul de
mai sus, argc este 3 !i argv[$] este hello,, iar argv[2] este world (argv[0] este dependent
de sistemul de operare). Primul argument real este argv[$], iar ultimul argument este
argv[argc $]. n plus, standardul limbajului asigur# c# argv[argc] este un pointer nul.
Prima versiune a programului echo trateaz# argumentul argv ca un tablou de pointeri la
caracter.
#include <stdio.h>
main(int argc,char argv[])
{
int i;
for(i=$;i<argc;i++)
printf(%s%s,argv[i],(i<argc$)? : );
printf(\n);
return 0;
}
Deoarece argv este un pointer la un tablou de pointeri, putem lucra cu pointeri n locul
tabloului, a!a cum se poate vedea n a doua versiune a programului.
#include <stdio.h>
main(int argc,char argv[])

100
{
while(argc>0)
printf(%s%s,++argv,(argc>$)? : );
printf(\n);
return 0;
}
Func"ia printf poate fi apelat# !i sub forma
printf((argc>$) ? %s : %s,++argv);
Ca un al doilea exemplu, s# consider#m programul din Structura programelor C care
afi!eaz# toate linile din intrare care con"in un anumit tipar. Dup# cum v# aminti"i, tiparul era
codificat n program (era inclus n program), ceea ce face programul greu de utilizat (mai bine
spus nepractic). Vom modifica acel program astfel nct tiparul s# fie introdus ca !i prim
argument n linia de comand#.
#include <stdio.h>
#include <string.h>
#define MAX $000
int getline(char ,int);
main(int argc,char argv[])
{
char linie[MAX];
if(argc!=2)
printf(\aUtilizare: find tipar\n);
else
while(getline(linie,MAX)>0)
if(strstr(linie,argv[$])!=NULL)
printf(%s\n,linie);
return 0;
}
Func"ia din biblioteca standard strstr(s,t) returneaz# un pointer la prima apari"ie a !irului
t n !irul s, sau NULL n caz contrar. Ea este declarat# n <string.h>.
Acest exemplu l putem folosi n continuare ca model, pentru a ilustra !i alte aspecte
privind argumentele din linia de comand#. S# presupunem c# dorim s# avem dou# argumente
op"ionale. Un argument ne va spune c# vor fi afi!ate toate liniile cu excep"ia celor care con"in
tiparul, iar al doilea argument ne va spune c# fiecare linie va fi precedat# de num#rul de linie.
O conven"ie utilizat# n sisemul de operare Unix !i preluat# !i de alte sisteme de
operare, este aceea c# un argument care ncepe cu semnul minus (sau cu /) introduce un
parametru op"ional. Pentru exemplul nostru, dac# vom folosi nota"ia x pentru primul
argument (cu excep"ia) !i n pentru al doilea argument (num#rul liniei), atunci comanda
find x n tipar
va afi!a toate liniile care nu con"in tiparul, fiecare fiind precedat# de num#rul s#u.
Argumentele op"ionale trebuie s# poat# fi introduse n orice ordine, iar programul s# fie
independent de num#rul argumentelor prezente. Mai mult, argumentele pot fi combinate n
toate modurile posibile, ca de exemplu
find xn tipar
sau
find nx tipar

101
Programul de c#utare este dat mai jos. Situa"ii mai complexe de utilizare a pointerilor
dect n acest program snt rare, iar n astfel de cazuri se recomand# descompunerea n pa!i
intermediari. De remarcat c# n acest program un argument op"ional este introdus de semnul
minus, dar programul poate fi modificat astfel nct argumentele op"ionale s# fie introduse !i
de / sau de alte caractere, sau chiar o combina"ie a acestor caractere.
#include <stdio.h>
#include <string.h>
#define MAX $000
int getline(char ,int);
main(int argc,char argv)
{
char linie[MAX];
int c,except=0,numar=0;
long lineno=0;
while(argc>0 && ++argv==)
while(c=++argv)
switch(c)
{
case x:
except=$;
break;
case n:
numar=$;
break;
default:
printf(\aeroare: argument eronat %c\n,c);
argc=0;
break;
}
if(argc!=$)
printf(\aUtilizare: find x n tipar\n);
else
while(getline(linie,MAX)>0)
{
lineno++
if((strstr(linie,argv)!=NULL)!=except)
{
if(numar)
printf(%ld:,lineno);
printf(%s\n,linie);
}
}
return 0;
}
Exerci#iul 61. Scrie"i un program expr care cite!te o expresie aritmetic# n nota"ie
polonez# invers#, calculeaz# valoarea ei !i afi!eaz# rezultatul. Operanzii pot fi numere, iar
operatorii care pot apare n expresie snt +, , , / !i %. Fiecare operator este introdus ca !i
argument separat n linia de comand#.


102
Exerci#iul 62. Modifica"i programele entab !i detab din Ini"iere n C astfel nct s#
accepte ca argumente o list# cu stopii tab. Dac# lista nu este prezent#, utiliza"i set#ri implicite
pentru caracterele tab.

Exerci#iul 63. Scrie"i un program tail care tip#re!te ultimele n linii din intrare. Implicit
n=10, dar poate fi modificat printr-un argument n linia de comand#. Scrie"i programul pentru
a face ct mai mult# economie de memorie pentru memorarea liniilor, f#r# a folosi tablouri
bidimensionale de dimensiune fix#.

Exerci#iul 64. Scrie"i un program care sparge liniile prea lungi din intrare n dou# sau
mai multe linii mai scurte dup# ultimul caracter non-spa"iu care apare naintea celui de-al
n-lea caracter. Dac# nu este posibil altfel, desp#r"irea se va face dup# cel de-al n-lea caracter,
iar naintea coloanei specificate nu trebuie s# mai fie caractere spa"iu sau tab. Liniile ob"inute
se vor alinia ntre cele dou# margini, prin introducerea propor"ional# de spa"ii ntre cuvinte.
Valoarea implicit# pentru n este 60, dar ea trebuie s# poat# fi schimbat# printr-un argument n
linia de comand#.
5.10 Pointeri la func#ii
n C o func"ie nu este o variabil#, dar este posibil s# definim pointeri la func"ii, care pot
fi asigna"i, plasa"i n tablouri, transmi!i func"iilor, etc. Vom ilustra acest lucru modificnd
programul de sortare din acest capitol astfel nct dac# utiliz#m un argument op"ional n,
sortarea se va face n ordine numeric# !i nu lexicografic# (aceasta nseamn# c# introducem
numere).
O sortare are trei p#r"i: o comparare care determin# ordinea unei perechi de elemente, un
schimb care inverseaz# ordinea elementelor care nu snt n ordine !i un algoritm de sortare
care face compararea !i schimbul elementelor, pn# cnd elementele snt ordonate. Algoritmul
de sortare este independent de opera"iile de comparare !i schimb, astfel c# transmi"nd
algoritmului de sortare diferite func"ii de comparare !i schimb, putem realiza sortarea dup#
diferite criterii.
Compararea lexicografic# este realizat# de func"ia strcmp, iar compararea numeric# de
func"ia numcmp. Pentru a insista mai mult asupra utiliz#rii pointerilor la func"ii, nu am
analizat eventualele erori care pot fi n linia de comand#.
#include <stdio.h>
#include <string.h>
#define MAXLINES 5000
char lineptr[MAXLINES];
int readlines(char lineptr[],int nlines);
void writelines(char lineptr[],int nlines);
void qsort(void , int, int, int ()(const void ,const void ));
int numcmp(const char ,const char );
main(int argc,char argv[])
{

103
int nlines,numeric=0;
if(argc>$ && strcmp(argv[$],n)==0)
numeric=$;
if((nlines=readlines(lineptr,MAXLINES))>=0)
{
qsort((void )lineptr,0,nlines$,
(int ()(void ,void ))(numeric ? numcmp : strcmp));
writelines(lineptr,nlines);
return 0;
}
else
{
printf(error: prea multe linii pentru sortare\n);
return $;
}
}
S# explic#m pe scurt ce face programul. n apelul lui qsort, strcmp !i numcmp snt
adrese de func"ii. Deoarece compilatorul !tie c# ele snt func"ii, nu mai este necesar# utilizarea
operatorului de adres# &.
Func"ia qsort poate prelucra acum orice tip de date, nu numai !iruri de caractere. A!a
cum se observ# din prototipul func"iei, qsort recep"ioneaz# un tablou de pointeri, doi ntregi !i
o func"ie cu dou# argumente pointeri, pentru care am utilizat tipul void . Orice pointer poate
fi convertit printr-o construc"ie cast la tipul void !i invers, f#r# pierderea informa"iilor, deci
am apelat func"ia qsort folosind aceast# observa"ie.
void swap(void , int, int);
void qsort(void v[], int left, int right, int (comp)(const void ,const void ))
{
int i,last;
if(left>=right)
return;
swap(v,left,(left+right)/2);
last=left;
for(i=left+$;i<=right;i++)
if((comp)(v[i],v[left])<0)
swap(v,++last,i);
swap(v,left,last);
qsort(v,left,last$,comp);
qsort(v,last+$,right,comp);
}
void swap(void v[],int i,int j)
{
void temp;
temp=v[i];
v[i]=v[j];
v[j]=temp;
}
S# studiem declara"iile mai n detaliu. Al patrulea parametru al lui qsort este
int ()(const void ,const void )

104
care nseamn# c# el este un pointer la o func"ie care are dou# argumente de tip const void !i
returneaz# un int. Utilizarea acestei func"ii n linia
if((comp)(v[i],v[left])<0)
este n concordan"# cu declara"ia, deoarece comp este un pointer la o func"ie, comp este
func"ia, iar
(comp)(v[i],v[left])
este apelul la ea. Parantezele snt necesare, deoarece n lipsa lor,
int comp(void ,void ) / GRESIT /
nseamn# c# comp este o func"ie care returneaz# un pointer la un int, ceea ce este altceva.
Func"ia numcmp, care compar# dou# !iruri pe baza valorii lor numerice calculat# de
atof, este dat# mai jos.
#include <math.h>
int numcmp(char s$,char s2)
{
double v$,v2;
v$=atof(s$);
v2=atof(s2);
if(v$<v2)
return $;
else if(v$>v2)
return $;
else
return 0;
}
Exerci#iul 65. Modifica"i programul de sortare astfel nct s# admit# !i op"iunea r, care
indic# faptul c# sortarea are loc n ordine invers# (descresc#toare). Fi"i siguri c# op"iunea r
lucreaz# mpreun# cu n. Face"i !i verificarea erorilor din linia de comand#.

Exerci#iul 66. Ad#uga"i la programul anterior !i optiunea c pentru a nu "ine cont de
diferen"a dintre literele mari !i mici. Aceast# op"iune trebuie s# lucreze mpreun# cu r !i n.

Exerci#iul 67. Ad#uga"i la programul anterior !i optiunea d pentru a face compara"ia
numai a literelor, numerelor !i spa"iilor. Aceast# optiune trebuie s# lucreze mpreun# cu c, r
!i n.


105
Exerci#iul 68. Ad#uga"i la programul anterior posibilitatea ca sortarea s# se fac# pe
cmpuri din linie, fiecare cmp avnd asociat un set independent de op"iuni de sortare ( c, r !i
n). Prin cmp al unei linii n"elegem caracterele cuprinse ntre dou# coloane. (O posibilitate
de a defini o op"iune de sortare dup# cmpuri este s# folosim de exemplu -f<n$>:<n2>, unde
<n$> este coloana de nceput a cmpului, iar <n2> este coloana de sfr!it. ntr-o astfel de
situa"ie celalte op"iuni (care urmeaz#) se vor referi la cmpul definit astfel. Dac# avem mai
multe cmpuri dup# care facem sortarea simultan, trebuie s# v# gndi"i la o asociere ntre cmp
!i op"iuni, care poate fi f#cut# ntr-un mod analog celui descris mai sus.)
5.11 Declara#ii complexe
n general, declara"iile n C nu se deosebesc prea mult de declara"iile din alte limbaje de
programare. Dac# snt utiliza"i pointerii, lucrurile se pot complica, ndeosebi dac# intervin
pointeri la func"ii sau tablouri sau dac# utiliz#m paranteze. De exemplu, diferen"a dintre
int f();
!i
int (pf)();
ilustreaz# acest aspect al complexit#"ii declara"iilor. Dac# n primul caz f este o func"ie care
returneaz# un pointer la un int, n cel de-al doilea caz pf este un pointer la o func"ie care
returneaz# un int. Parantezele n acest ultim exemplu snt necesare, deoarece operatorul are
preceden"# mai mic# dect operatorul ().
De!i n practic# declara"iile complexe apar mai rar, este important s# !tim cum s# le
n"elegem !i cum s# le interpret#m, !i, dac# este necesar, cum s# le creem.
5.11.1 Declara#ii abstracte
O declara"ie abstract# este o declara"ie din care lipse!te identificatorul !i con"ine unul
sau mai mul"i modificatori de func"ii, pointeri sau tablouri. Pentru a putea n"elege o declara"ie
abstract#, trebuie s# "inem cont de ni!te observa"ii, care pot fi deduse din cele f#cute pn#
acum.
Modificatorul de pointer () precede ntotdeauna identificatorul ntr-o declara"ie.
Modificatorii de tablou ([ ]) !i de func"ie (( )) snt ntotdeauna dup# identificator. %tiind aceste
lucruri, putem determina unde este locul identificatorului dintr-o declara"ie abstract#.
Declaratorii abstrac"i pot fi complec!i. Parantezele ntr-un declarator abstract complex
specific# o interpretare particular#, identic# cu cea a declara"iilor complexe. De exemplu:
int ; /pointer la int/
int [3]; /tablou de 3 pointeri la int /
int ()[5]; /pointer la un tablou de 5 int /
int (); /functie fara parametri specificati care returneaza un pointer la int /
int ()(void); /pointer la o functie fara argumente care returneaza un int/
int (const [])(int); /tablou cu un numar neprecizat de elemente, fiecare fiind un pointer
constant la functie, fiecare functie avind un parametru de tip int si returnind
un int/

106
Ca !i observa"ie, declaratorul abstract care const# numai dintr-o pereche de paranteze
vide (ntre care nu avem nimic) nu este permis, deoarece este ambiguu (de ce?).
5.11.2 Interpretarea declara#iilor complexe
Un declarator complex este un identificator calificat de mai mult de un modificator de
tablou, pointer sau func"ie.
n interpretarea declaratorilor complec!i, parantezele drepte !i rotunde au preceden"#
mai mare dect . Ele apar n dreapta identificatorului, au aceea!i preceden"# !i se asociaz# de
la stnga spre dreapta. Dup# ce am interpretat declaratorul, ca !i un ultim pas se aplic#
specificatorul de tip. Ordinea de asociere implicit# poate fi modificat# prin utilizarea
parantezelor. Nu se utilizeaz# niciodat! paranteze rotunde n care s# fie numai numele
identificatorului, deoarece acesta poate fi interpretat ca !i o list# de parametri.
Un mod simplu de a interpreta declara"iile complexe este de a le citi din interior spre
exterior, utiliznd urm#torii pa!i:
1. Se ncepe cu identificatorul !i ne uit#m n dreapta lui dac# are paranteze p#trate sau
rotunde;
2. Interpret#m parantezele, apoi ne uit#m dup# asterisc n stnga lui;
3. Dac# ntlnim o parantez# rotund# nchis# la orice nivel, aplic#m pa!ii 1 !i 2 pentru
tot ceea ce este n interiorul parantezelor rotunde;
4. Aplic#m specificatorul de tip.
S# consider#m declara"ia urm#toare:
char ((var)())[$0];
^ ^ ^ ^ ^ ^ ^
7 6 4 2 1 3 5
n acest exemplu pa!ii snt numerota"i n ordinea n care snt interpreta"i !i,
parcurgndu-i, ob"inem:
1. Identificatorul var este
2. pointer la
3. func"ie (f#r# parametri) care returneaz#
4. pointer la
5. tablou de 10 elemente care snt
6. pointeri la
7. char
Pentru a ilustra cele expuse mai sus, vom prezenta un program dcl care realizeaz# aceste
opera"ii, adic# interpreteaz# o declara"ie complex#, dar ntr-o form# simplificat#. Dac#
analiz#m mai cu aten"ie modul de interpretare a unei declara"ii complexe, observ#m c# aceast#
interpretare este recursiv#. Programul fiind doar ilustrativ, nu analizeaz# dect tipuri de date
simple. Nu snt analizate tipul argumentelor ntr-o func"ie !i nici calificatori ca !i const. De
asemenea erorile care pot apare ntr-o declara"ie nu snt tratate n detaliu. Aceste dezvolt#ri ale
programului snt l#sate ca exerci"iu. Men"ion#m faptul c# func"iile care nu au mai fost ntlnite
pn# acum vor fi prezentate n capitolele urm#toare.
Pentru a n"elege mai bine func"ionarea programului, vom prezenta mai jos defini"ia
unui declarator, ntr-o form# simplificat#.

107
declarator:
pointer
opt
declarator-direct
declarator-direct:
identificator
(declarator)
declarator-direct[expresie-constant#
opt
]
declarator-direct(lista-parametri-cu-tip)
declarator-direct(lista-identificatori)
pointer:
list#-calificatori-cu-tip
opt

list#-calificatori-cu-tip
opt
pointer
list#-calificatori-cu-tip:
calificator-cu-tip
list#-calificatori-cu-tip calificator-cu-tip
list#-parametri-cu-tip:
list#-parametri
list#-parametri , . . .
list#-parametri:
declarare-parametru
list#-parametri , declarare-parametru
declarare-parametru:
specificatori-declarare declarator
specificatori-declarare declarator-abstract
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#define MAXTOKEN $00
enum {NAME,PARENS,BRACKETS};
void dirdcl(void);
int tokentype;
char token[MAXTOKEN];
char name[MAXTOKEN];
char datatype[MAXTOKEN];
char out[$000];
int gettoken(void)
{
int c;
char p=token;
while((c=getchar())== || c==\t)
;
if(c==()
{
c=getchar();

108
if(c==))
{
strcpy(token,());
return tokentype=PARENS;
}
else
{
ungetc(c,stdin);
return tokentype=(;
}
}
else if(c==[)
{
for(p++=c;(p++=getchar())!=];)
p=c;
p=\0;
return tokentype=BRACKETS;
}
else if(isalpha(c))
{
for(p++=c;isalnum(c=getchar());)
p++=c;
p=\0;
ungetc(c,stdin);
return tokentype=NAME;
}
else
return tokentype=c;
}
void dcl(void)
{
int ns;
for(ns=0;gettoken()==;)
ns++;
dirdcl();
while(ns>0)
strcat(out, pointer(i) la);
}
void dirdcl(void)
{
int type;
if(tokentype==()
{
dcl();
if(tokentype!=))
printf(\aeroare: lipseste )\n);
}
else if(tokentype==NAME)
strcpy(name,token);
else
printf(\aeroare: nume lipsa sau (nume)\n);
while((type=gettoken())==PARENS || type==BRACKETS)
if(type==PARENS)
strcat(out, functie care returneaza);

109
else
{
strcat(out, tablou de );
strcat(out,token);
}
}
main()
{
while(gettoken()!=EOF)
{
strcpy(datatype,token);
out[0]=\0;
dcl();
if(tokentype!=\n)
printf(\aeroare de sintaxa\n);
printf(%s: %s %s\n,name,out,datatype);
}
return 0;
}
Exerci#iul 69. Modifica"i programul dcl anterior astfel nct s# fie analizate n detaliu
erorile care pot apare ntr-o declara"ie.

Exerci#iul 70. Modifica"i programul dcl anterior astfel nct s# poat# fi analizat# orice
declara"ie despre care am vorbit pn# n acest moment, adic# s# fie analizate !i argumentele
func"iilor, calificatori ca !i const, etc. (astfel nct s# fie respectat# defini"ia dat# mai sus unui
declarator)

Exerci#iul 71. Scrie"i un program C care cite!te o func"ie C !i detecteaz# toate erorile
care pot apare n acea func"ie (realizeaz# o compilare a func"iei, dar f#r# generarea codului
obiect). Se presupune c# func"ia analizat# nu con"ine declara"ii de variabile sau parametri, iar
variabilele utilizate n func"ie pot fi numai din cele prezentate pn# aici. Func"ia poate con"ine
!i expresii C. Un exemplu de func"ie care poate fi analizat# de acest program este func"ia
main de mai sus.

Exerci#iul 72. Extinde"i programul de mai sus astfel nct s# pute"i analiza !i func"ii care
au parametri. Declararea parametrilor se face utiliznd numai stilul nou, cel vechi constituind
eroare.
5.12 Rezumat
Pointerii ocup# locul cel mai important n cadrul limbajului C. Ei au fost utiliza"i nc#
din Ini"iere n C, chiar dac# nu s-a !tiut atunci acest lucru (primul program C utilizeaz#
pointeri). Se poate afirma, f#r# a gre!i prea mult, c# nu exist# program C n care s# nu se
utilizeze cel pu"in un pointer. De aceea limbajul a fost proiectat astfel nct lucrul cu pointeri
s# fie natural. Prin aceast# facilitate, precum !i prin posibilitatea trat#rii ntr-un mod foarte

110
simplu a argumentelor din linia de comand#, limbajul !i dovede!te superioritatea fa"# de alte
limbaje cum ar fi de exemplu Pascal. Datorit# importan"ei pointerilor, acest capitol este cel
mai cuprinz#tor. Problemele din capitol nu snt tocmai simple, unele dintre ele putnd fi
considerate chiar cele mai dificile din ntreaga carte. Pointerii constituie cel mai simplu mijloc
de a face programe pe care s# nu le n"eleag# nimeni (nici m#car calculatorul)
Un subiect mai pu"in tratat n alte c#r"i de C, dar care joac# un rol central n cadrul
lucrului cu pointerii, este legat de pointeri la func"ii !i de tratarea argumentelor n linia de
comand#. Referitor la argumentele din linia de comand#, dificultatea const# n modul de
organizare al acestora, astfel nct programul s# nu fie dependent de ordinea acestora.
Tehnicile prezentate aici snt utile !i necesare pentru studiul capitolului urm#tor,
deoarece nu se poate lucra cu structuri de date recursive f#r# pointeri.

111
6. STRUCTURI
O structur! este o colec"ie de una sau mai multe variabile, n general de tipuri diferite,
grupate mpreun# sub un singur nume. Ele snt folosite pentru organizarea datelor complexe,
deoarece pot fi tratate !i prelucrate ca un ntreg. Structurile din C snt analogul tipului
RECORD din Pascal.
6.1 No#iuni de baz%
Un exemplu clasic de utilizare a structurilor este preluat din grafica pe calculator: un
punct este o pereche de coordonate, un dreptunghi este o pereche de puncte, etc. n cele ce
urmeaz# vom crea cteva structuri de baz# care pot fi utilizate n grafic#.
Obiectul de baz# este punctul, despre care vom presupune c# are ambele coordonate x !i
y ntregi. Cele dou# componente pot fi plasate ntr-o structur# declarat# astfel:
struct point
{
int x;
int y;
};
Cuvntul cheie struct introduce o declara"ie de structur#, care este o list# de declara"ii
cuprins# ntre acolade. Cuvntul struct poate fi urmat op"ional de un nume numit numele
structurii (structure tag). Acest nume poate fi utilizat n continuare ca o prescurtare pentru
declara"iile din acolade.
Variabilele dintr-o structur# se numesc membri. Numele unui membru al unei structuri
sau al unei structuri poate coincide cu numele unei variabile simple (non-membru), f#r# a
exista vreun conflict, deoarece acest lucru poate fi dedus ntotdeauna din context. Mai mult, n
structuri diferite pot exista membri cu acela!i nume.
O declara"ie de structur# define!te un tip de date. Acolada dreapt# care ncheie lista
membrilor structurii poate fi urmat# de o list# de variabile, la fel ca la orice tip de baz#. Deci
struct
{
...
} x,y,z;
este sintactic echivalent cu
int x,y,z;
n sensul c# fiecare din instruc"iunile de mai sus declar# variabilele x, y !i z ca fiind de tipul
precizat !i rezerv# memorie pentru ele.
O declara"ie de structur# care nu este urmat# de o list# de variabile nu rezerv# memorie,
ci descrie forma (template sau shape) structurii. Dac# structura are un nume, acest nume poate

112
fi utilizat mai trziu n declararea de variabile structur#. De exemplu, avnd declara"ia
structurii point de mai sus, instruc"iunea
struct point pt;
define!te variabila pt ca fiind o structur# de tipul struct point. O structur# poate fi ini"ializat#
punnd dup# defini"ia sa o list# cu ini"ializatori cuprins# ntre acolade, fiecare ini"ializator
pentru un membru fiind o expresie constant#, ca n
struct point maxpt={320,200};
O structur# automat# poate fi de asemenea ini"ializat#, printr-o instruc"iune de asignare
sau prin apelul unei func"ii care returneaz# o structur# de tipul respectiv.
Un membru al unei structuri este accesat ntr-o expresie printr-un procedeu numit
calificare, care este o construc"ie de forma
nume-structur!.membru
Operatorul de membru de structur! . face leg#tura ntre numele structurii !i numele
membrului. De exemplu, pentru a tip#ri coordonatele punctului pt, putem proceda astfel:
printf(%d,%d,pt.x,pt.y);
sau, pentru a calcula distan"a dintre dou# puncte pt$ !i pt2:
double dist;
dist=sqrt((double)pt.xpt.x+(double)pt.ypt.y);
Structurile pot fi incluse una n alta (nested). De exemplu, un dreptunghi este
reprezentat de o pereche de puncte care desemneaz# dou# col"uri diagonal opuse (de obicei
col"ul stnga sus !i col"ul dreapta jos):
struct rect
{
struct point pt$;
struct point pt2;
};
6.2 Structuri "i func#ii
Cu structurile este permis s# facem urm#toarele opera"ii: copierea sau asignarea ei (ca
un ntreg), ob"inerea adresei !i accesarea membrilor ei. Opera"iile de copiere !i asignare includ
transmiterea unei structuri ca parametru unei func"ii !i returnarea ei de c#tre func"ii.
Structurile nu pot fi comparate, dar pot fi ini"ializate de o list# de valori constante. O structur#
automat# (declarat# n interiorul unei func"ii, f#r# ca declara"ia s# fie precedat# de cuvntul
static) poate fi ini"ializat# !i printr-o asignare.
Vom exemplifica cele expuse mai sus scriind cteva func"ii care s# lucreze cu puncte !i
dreptunghiuri. Putem transmite o structur# unei func"ii n mai multe moduri, cum ar fi:
transmiterea fiec#rui membru separat, transmiterea ntregii structuri sau transmiterea unui
pointer la structur#. Fiecare din aceste moduri are avantaje !i dezavantaje.
Prima func"ie pe care o vom scrie, makepoint, are doi parametri ntregi !i returneaz# o
structur# de tip point:

113
struct point makepoint(int x,int y)
{
struct point temp;
temp.x=x;
temp.y=y;
return temp;
}
Men"ion#m faptul c# nu exist! nici un conflict ntre o variabil! #i un membru al unei
structuri cu acela#i nume (de ce?). Func"ia makepoint poate fi utilizat# n unul din modurile
de mai jos:
struct rect screen;
struct point pct;
screen.pt$=makepoint(0,0);
screen.pt2=makepoint(XMAX,YMAX)
pct=makepoint((screen.pt$.x+screen.pt2.x)/2, (screen.pt$.y+screen.pt2.y)/2);
Urm#toarea func"ie, addpoint, adun# dou# puncte. Ea are !i argumentele !i valoarea
returnat# structuri.
struct point addpoint(struct point p$,struct point p2)
{
p$.x+=p2.x;
p$.y+=p2.y;
return p$;
}
Ca un alt exemplu, func"ia ptinrect testeaz# dac# un punct este n interiorul unui
dreptunghi (propriu) definit de col"ul stnga sus !i dreapta jos.
int ptinrect(struct point p,struct rect r)
{
return p.x>=r.pt$.x && p.x<=r.pt2.x &&
p.y>=r.pt$.y && p.y<=r.pt2.y;
}
Dac# o structur# mare trebuie transmis# unei func"ii, n general este mai eficient s#
transmitem func"iei un pointer la acea structur#. Pointerii la structuri snt asem#n#tori cu
pointerii la variabile simple. Declara"ia
struct point pp;
ne spune c# pp este un pointer la o structur# de tip struct point. n aceast# situa"ie, pp este
structura !i (pp).x !i (pp).y snt membrii ei. Men"ion#m faptul c# parantezele snt necesare,
deoarece, n lipsa lor, pp.x nseamn# (pp.x), ceea ce este ilegal, deoarece x nu este pointer.
Deoarece pointerii la structuri snt foarte des utiliza"i, a fost introdus# o nota"ie
prescurtat# pentru ei. Astfel, dac# p este un pointer la o structur#, atunci expresia
p>membru-al-structurii
se refer# la un membru particular al structurii. Ambii operatori de membru de structur# . !i >
snt asociativi de la stnga la dreapta, astfel nct dac# avem declara"iile
struct rect r,rp;
atunci urm#toarele expresii snt echivalente:

114
r.pt$.x
rp>pt$.x
(r.pt$).x
(rp>pt$).x
Operatorii de structur# . !i >, mpreun# cu operatorii () de la apelul func"iilor !i [] de la
tablouri, au prioritate maxim#. De exemplu, avnd declara"ia
struct
{
int len;
char str;
} p;
atunci ++p >len incrementeaz# pe len, (++p) >len incrementeaz# pe p nainte de a accesa pe
len, iar (p++) >len incrementeaz# pe p dup# ce utilizeaz# pe len (parantezele n acest ultim
caz nu snt necesare).
n acela!i mod, expresia p >str se refer# la caracterul pointat de str, p >str++
incrementeaz# pe str dup# ce acceseaz# caracterul pointat de str (la fel ca s++), (p >str)++
incrementeaz# ceea ce pointeaz# str, iar p++ >str incrementeaz# pe p dup# ce acceseaz#
ceea ce pointeaz# str.
6.3 Tablouri de structuri
Vom ilustra utilizarea tablourilor de structuri scriind un program care s# numere
cuvintele cheie C dintr-un text. Pentru aceasta avem nevoie de un tablou de caractere n care
s# avem cuvintele cheie, !i de un tablou de ntregi n care s# contoriz#m apari"iile fiec#rui
cuvnt cheie. O posibilitate de realizare este de a avea dou# tablouri pe care s# le utiliz#m n
paralel, ca de exemplu
char keyword[NKEYS];
int keycount[NKEYS];
Deoarece cele dou# tablouri snt folosite n paralel, putem s# organiz#m datele ntr-un
mod mai eficient. Observ#m c# fiecare cuvnt cheie are dou# propriet#"i: numele !i num#rul de
apari"ii, Aceste propriet#"i formeaz# o pereche care poate fi descris# astfel:
char word;
int count;
n acest mod, cele dou# tablouri de mai sus pot fi nlocuite de un singur tablou de astfel
de perechi. Declara"ia
struct key
{
char word;
int count;
} keytab[NKEYS];
declar# o structur# de tip key, define!te un tablou keytab de structuri de acest tip !i rezerv#
memorie pentru el. Fiecare element din tablou este o structur#. Declara"ia de mai sus poate fi
scris# !i n modul urm#tor:

115
struct key
{
char word;
int count;
};
struct key keytab[NKEYS];
Deoarece tabloul keytab con"ine cuvintele cheie C, care snt constante, putem s# definim
acest tablou ca fiind extern !i s# l ini"ializ#m n momentul definirii. Ini"ializarea se face
simplu, ea fiind analoag# cu ini"ializ#rile pe care le-am mai ntlnit pn# acum. De men"ionat
faptul c# n anumite implement#ri ale limbajului C num#rul cuvintelor cheie poate fi mai
mare, de obicei prin includerea cuvintelor cheie asm !i fortran.
struct key
{
char word;
int count;
} keytab[]=
{
auto,0,
break,0,
case,0,
char,0,
const,0,
continue,0,
default,0,
do,0,
double,0,
else,0,
enum,0,
extern,0,
float,0,
for,0,
goto,0,
if,0,
int,0,
long,0,
register,0,
return,0,
short,0,
signed,0,
sizeof,0,
static,0,
struct,0,
swich,0,
typedef,0,
union,0,
unsigned,0,
void,0,
volatile,0,
while,0
};
Dup# cum se poate observa, ini"ializatorii snt forma"i din perechi corespunz#toare
membrilor structurii. Ini"ializarea se poate face mai exact incluznd n acolade fiecare pereche
de ini"ializatori care corespunde unei structuri (unui membru al tabloului), ca n:

116
{auto,0},
{break,0},
{case,0},
...
n cazul n care ini"ializatorii snt variabile simple sau !iruri de caractere !i cnd to"i
ini"ializatorii snt prezen"i, acoladele interioare nu mai snt necesare, a!a cum reiese din
exemplul nostru. Dac# to"i ini"ializatorii snt prezen"i !i dimensiunea tabloului nu este
precizat#, num#rul de elemente din tablou va fi calculat de compilator.
Programul de contorizare va ncepe cu defini"ia tabloului keytab. Cuvintele cheie le-am
pus n tablou n ordine alfabetic# pentru a putea s# utiliz#m c#utarea binar#.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD $00
int getword(char ,int);
int binsearch(char ,struct key ,int);
main()
{
int n;
char word[MAXWORD];
while(getword(word,MAXWORD)!=EOF)
if(isalpha(word[0]))
if((n=binsearch(word,keytab,NKEYS))>=0)
keytab[n].count++;
for(n=0;n<NKEYS;n++)
if(keytab[n].count>0)
printf(%4d %s\n,keytab[n].count,keytab[n].word);
return 0;
}
int binsearch(char word,struct key tab[],int n)
{
int low=0,high=n$,mid,cond;
while(low<=high)
{
mid=(low+high)/2;
if((cond=strcmp(word,tab[mid].word))<0)
high=mid$;
else if(cond>0)
low=mid+$;
else
return mid;
}
return $;
}
Func"ia getword o vom prezenta pu"in mai trziu. Ceea ce trebuie s# !tim despre ea n
acest moment este c# g#se!te urm#torul cuvnt !i l copiaz# ntr-un tablou care este primul s#u
argument.
Constanta NKEYS este num#rul de cuvinte cheie din keytab. De!i acest num#r l putem
stabili noi, este bine ca acest lucru s# fie f#cut de calculator, mai ales dac# lista poate fi

117
modificat#. O posibilitate de a determina aceast# constant# este ca s# termin#m lista de
ini"ializatori cu un pointer nul !i s# calcul#m ntr-un ciclu num#rul de elemente din tablou.
Dar, deoarece m#rimea unui tablou (num#rul de octe"i) este determinat# de compilator,
num#rul de elemente din tablou poate fi determinat altfel. %tim c# m#rimea tabloului este
egal# cu m#rimea unei componente a tabloului nmul"it# cu num#rul de elemente din tablou.
De aici deducem c# num#rul de elemente din tabloul nostru este egal cu
m!rimea lui keytab/m!rimea structurii key
Limbajul C ne furnizeaz# un operator unar utilizat de compilator, numit sizeof, care
poate fi utilizat pentru a calcula m#rimea oric#rui obiect. Astfel, expresia
sizeof obiect
sau
sizeof (nume tip)
ne d# un ntreg f#r# semn de tip size_t (tip descris n <stddef.h>), care este egal cu m#rimea n
octe"i a obiectului specificat. Un obiect poate fi o variabil#, tablou sau structur#. Un nume de
tip poate fi un tip de baz# ca int sau double, sau un tip derivat ca o structur# sau pointer.
n cazul nostru, num#rul de elemente din tablou poate fi determinat astfel:
#define NKEYS (sizeof keytab/sizeof(struct key))
Un alt mod de a calcula pe NKEYS este de a mp#r"i m#rimea tabloului la m#rimea unui
element particular din tablou:
#define NKEYS (sizeof keytab/sizeof keytab[0])
Aceast# ultim# defini"ie are avantajul c# poate fi utilizat# indiferent de tipul elementelor
tabloului.
Operatorul sizeof se adreseaz# compilatorului !i nu preprocesorului, deci nu poate fi
utilizat ntr-o directiv# #if. O expresie definit# cu #define nu este evaluat# de preprocesor,
deci sizeof poate fi utilizat ntr-o directiv# #define.
n continuare putem prezenta func"ia getword. Aceast# func"ie ia urm#torul cuvnt din
intrare !i returneaz# primul caracter din cuvnt, EOF pentru sfr!itul de fi!ier sau caracterul
nsu!i dac# nu este un caracter alfabetic. Prin cuvnt vom n"elege un !ir de litere sau cifre
care ncepe cu o liter#, sau un singur caracter non-spa"iu.
int getword(char word,int lim)
{
int c;
char w=word;
while(isspace(c=getchar()))
;
if(c!=EOF)
w++=c;
if(!isalpha(c))
{
w=\0;
return c;
}
for(;lim>0;w++)
if(!isalnum(w=getchar()))
{
ungetc(w,stdin);
break;

118
}
}
w=\0;
return word[0];
}
Exerci#iul 73. Modifica"i programul de contorizare a cuvintelor cheie C astfel nct s#
trateze corect liniu"a de subliniere (care n C este considerat# liter#), iar cuvintele cheie C care
se g#sesc n ghilimele, comentarii !i linii de control ale preprocesorului (care ncep cu #) s# fie
ignorate.
6.4 Pointeri la structuri
n unele situa"ii, ca de exemplu cnd o structur# este mare, este mai avantajos s#
transmitem ca parametru unei func"ii sau s# return#m dintr-o func"ie un pointer la o astfel de
structur#, n loc s# lucr#m cu ntreaga structur#. Vom ilustra lucrul cu pointeri la structuri
rescriind programul de contorizare al cuvintelor cheie din paragraful anterior. Ceea ce se
modific# fa"# de programul anterior snt func"iile main !i binsearch.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAXWORD $00
int getword(char ,int);
struct key binsearch(char ,struct key ,int);
main()
{
struct key p;
char word[MAXWORD];
while(getword(word,MAXWORD)!=EOF)
if(isalpha(word[0]))
if((p=binsearch(word,keytab,NKEYS))!=NULL)
p>count++;
for(p=keytab;p<keytab+NKEYS;p++)
if(p>count>0)
printf(%4d %s\n,p>count,p>word);
return 0;
}
struct key binsearch(char word,struct key tab,int n)
{
int cond;
struct key low=&tab[0];
struct key high=&tab[n];
struct key mid;
while(low<high)
{
mid=low+(highlow)/2;
if((cond=strcmp(word,mid>word))<0)

119
high=mid;
else if(cond>0)
low=mid+$;
else
return mid;
}
return NULL;
}
Observ#m c# fa"# de programul anterior avem unele modific#ri. n primul rnd, func"ia
binsearch ntoarce un pointer la structura key, iar acest lucru este reflectat de prototipul !i
defini"ia func"iei.
O alt# modificare este aceea c# elementele lui keytab snt accesate prin intermediul
pointerilor, deci n func"ia binsearch au intervenit unele modific#ri. Una dintre acestea este
calculul elementului din mijloc. Acesta nu poate fi calculat prin formula
mid=(low+high)/2 / GRESIT /
deoarece adunarea a doi pointeri este ilegal#. Sc#derea pointerilor fiind permis# n anumite
condi"ii, elementul din mijloc a trebuit s# l calcul#m cu formula
mid=low+(highlow)/2
Cea mai important# schimbare este modificarea algoritmului, astfel nct s# nu ob"inem
un pointer ilegal sau s# ncerc#m s# acces#m un element din afara tabloului (s# ne reamintim
c# prin intermediul pointerilor putem face cu calculatorul aproape orice!). Problema care apare
este aceea c# expresiile &tab[ $] !i &tab[n] snt ambele n afara limitelor tabloului tab. Prima
din cele dou# expresii este ilegal#, iar a doua expresie nu poate fi utilizat# f#r# operatorul de
adres# (expresia tab[n] este ilegal#). Defini"ia limbajului garanteaz# faptul c# aritmetica
pointerilor n care este utilizat# adresa primului element de dup# limita superioar# a unui
tablou se efectueaz# corect (adic# expresia &tab[n] este corect#).
De asemenea incrementarea sau decrementarea unui pointer se efectueaz# corect, deci
incrementarea p++ din main va func"iona corect !i ne va da urm#torul element din tablou.
De remarcat faptul c# m#rimea unei structuri (exprimat# n octe"i) nu este egal#
ntotdeauna cu suma m#rimilor membrilor s#i. Dac# ave"i nevoie de m#rimea unei structuri, ea
se poate ob"ine utiliznd operatorul sizeof.
6.5 Structuri autoreferite
S# presupunem c# dorim s# trat#m o problem# mai general# referitoare la contorizarea
cuvintelor, !i anume s# num#r#m toate cuvintele din intrare. Deoarece cuvintele care pot apare
nu snt cunoscute dinainte (ca n cazul cuvintelor cheie dintr-un limbaj), nu putem utiliza un
tablou n care s# le memor#m. De fapt putem utiliza, cu riscurile de rigoare, un tablou foarte
mare n care s# memor#m cuvintele distincte, dar se ivesc dou# situa"ii. Cea mai simpl# dintre
ele este aceea n care num#rul cuvintelor distincte este (mult) mai mic dect dimensiunea
tabloului. n acest caz facem o risip# de memorie. Ce se ntmpl# ns# dac# num#rul cuvintelor
distincte este mai mare dect dimensiunea tabloului? Ct de mare poate fi aceast# dimensiune?
Din p#cate nu se poate da un r#spuns la aceste ntreb#ri.
Din aceast# cauz# nu putem s# c#ut#m dac# un cuvnt a mai ap#rut, n vederea
contoriz#rii, cu ajutorul c#ut#rii binare. De asemenea nu putem s# utiliz#m nici c#utarea

120
liniar# a unui nou cuvnt din intrare, deoarece ar dura foarte mult. Cum putem rezolva totu!i
aceast# problem#?
O solu"ie este s# p#str#m cuvintele ntr-o list# liniar# sortat# !i s# plas#m fiecare cuvnt
nou n pozi"ia corespunz#toare, pe m#sur# ce este citit. Nici aceast# solu"ie nu este prea
convenabil#, deoarece !i ea dureaz# foarte mult. O alt# solu"ie, care este ceva mai rapid#,
const# n utilizarea arborilor binari. Aceast# solu"ie o vom utiliza n continuare.
Fiecare nod al arborelui con"ine o structur# cu urm#toarele informa"ii:
un pointer la cuvnt
un contor care num#r# apari"iile cuvntului
un pointer la subarborele stng
un pointer la subarborele drept
Vom crea arborele astfel nct n subarborele stng al oric#rui nod s# avem numai
cuvinte care snt mai mici (lexicografic) dect cuvntul din nod, iar n subarborele drept al
oric#rui nod s# avem numai cuvinte care snt mai mari (lexicografic) dect cuvntul din nod.
Pentru a vedea dac# un cuvnt a mai fost ntlnit, proced#m n felul urm#tor: pornim de
la r#d#cin# !i compar#m cuvntul nou cu cuvntul din acel nod. n caz de egalitate,
increment#m contorul. Dac# noul cuvnt este mai mic dect cuvntul din nod, c#utarea se face
n subarborele stng, iar n caz contrar n subarborele drept. Dac# procesul de c#utare nu mai
poate continua (nodul n care am ajuns nu mai are al"i subarbori), cuvntul va fi plasat n
arbore n locul corespunz#tor (mai precis n subarborele n care ar fi trebuit s# continue
procesul c#ut#rii). Dup# cum se poate observa, procesul c#ut#rii este recursiv.
Descrierea unui nod se poate face astfel:
struct tnode
{
char word;
int count;
struct tnode left;
struct tnode right;
};
Este incorect ca o structur# s# con"in# o referire la ea ns##i (s# fie recursiv#), ca n
exemplul de mai jos:
struct tnode / GRESIT /
{
char word;
int count;
struct tnode left;
struct tnode right;
};
Declara"ia
struct tnode left;
declar# pe left ca fiid un pointer la structura tnode (ceea ce este corect) !i nu o structur# tnode.
Programul utilizeaz# func"ia getword (pe care am mai scris-o n alte programe) pentru a
citi un nou cuvnt, l instaleaz# n arbore, iar la sfr!it tip#re!te arborele.
#include <stdio.h>
#include <ctype.h>

121
#include <string.h>
#define MAXWORD $00
void treeprint(struct tnode );
int getword(char ,int);
struct tnode addtree(struct tnode ,char );
main()
{
struct tnode root;
char word[MAXWORD];
root=NULL;
while(getword(word,MAXWORD)!=EOF)
if(isalpha(word[0]))
root=addtree(root,word);
treeprint(root);
return 0;
}
Func"ia addtree este recursiv# !i reflect# ntocmai procesul de c#utare a unui nou cuvnt
descris mai sus.
struct tnode talloc(void);
char strdup(char );
struct tnode addtree(struct tnode p,char w)
{
int cond;
if(p==NULL)
{
p=talloc();
p>word=strdup(w);
p>count=$;
p>left=p>right=NULL;
}
else if((cond=strcmp(w,p>word))==0)
p>count++;
else if(cond<0)
p>left=addtree(p>left,w);
else
p>right=addtree(p>right,w);
return p;
}
Memoria pentru un nod nou se ob"ine prin apelul func"iei talloc, care returneaz# un
pointer la o zon# de memorie corespunz#toare. Dup# alocarea memoriei, cuvntul este copiat
n nod de func"ia strdup, este ini"ializat contorul, iar cei doi subarbori snt f#cu"i nuli. Aceast#
por"iune de cod este executat# numai n momentul ntlnirii unui nou cuvnt. Dup# cum se
observ#, programul nu verific# erorile care pot apare la apelul func"iilor talloc !i strdup.
Func"ia treeprint tip#re!te arborele alfabetic:
void treeprint(struct tnode p)
{
if(p!=NULL)
{

122
printf(%4d %s\n,p>count,p>word);
treeprint(p>left);
treeprint(p>right);
}
}
De notat faptul c# dac# arborele se dezechilibreaz# din cauz# c# noile cuvinte nu vin
n ordine aleatoare, timpul de execu"ie al programului cre!te. n cel mai r#u caz, dac#
cuvintele snt deja n ordine alfabetic#, programul simuleaz# o c#utare liniar# foarte lent#.
nainte de a prezenta !i func"iile care ne-au mai r#mas, vom face cteva observa"ii legate
de alocarea memoriei. Este evident c# este de dorit s# avem un singur alocator de memorie (o
func"ie care s# aloce memorie !i care s# returneze un pointer la zona de memorie alocat#)
ntr-un program, pe care s# l putem utiliza pentru orice tip de obiecte. Dac# alocatorul trebuie
s# aloce, de exemplu, memorie pentru pointeri la char !i la struct tnode, apar dou# ntreb#ri.
n primul rnd, cum este ndeplinit# condi"ia de aliniere a obiectelor, !tiind c# pe marea
majoritate a calculatoarelor diferitele tipuri de obiecte trebuie s# satisfac# anumite restric"ii de
aliniere (de exemplu ntregii trebuie s# fie de cele mai multe ori la adrese pare)? n al doilea
rnd, cum putem realiza declara"iile !tiind c# alocatorul trebuie s# returneze diferite tipuri de
pointeri?
Necesit#"ile de aliniere pot fi n general satisf#cute simplu, cu condi"ia unei risipe de
spa"iu de memorie, asigurndu-ne de faptul c# alocatorul returneaz# ntotdeauna un pointer la
o zon# de memorie care s# satisfac# toate restric"iile de aliniere. Func"ia alloc pe care am
scris-o n Aritmetica adreselor nu realizeaz# nici o aliniere, dar func"ia standard malloc
satisface aceste condi"ii.
R#spunsul la cea de a doua ntrebare (referitor la declararea tipului alocatorului) este
ceva mai greu de dat n cazul limbajelor care fac verificarea tipului obiectelor (variabile,
func"ii etc.). n cazul limbajului C, metoda cea mai bun# este de a declara c# alocatorul (n
particular malloc) returneaz# un pointer la void, iar acest pointer este convertit printr-o
construc"ie cast la tipul dorit. Func"ia malloc !i cele nrudite cu ea snt declarate n <stdlib.h>.
Func"iile talloc !i strdup se scriu deci n felul urm#tor:
#include <stdlib.h>
struct tnode talloc(void)
{
return (struct tnode )malloc(sizeof(struct tnode));
}
char strdup(char s)
{
char p;
p=(char )malloc(strlen(s)+$);
if(p!=NULL)
strcpy(p,s);
return p;
}
Memoria alocat# cu malloc poate fi eliberat#, pentru a fi reutilizat#, cu func"ia free. De
men"ionat c#, de!i strdup nu este o func"ie standard, ea se g#se!te n majoritatea
implement#rilor limbajului C, declarat# n <string.h>.

123
6.5.1 Aplica#ii
Ca !i aplica"ie la structurile autoreferite s# consider#m implementarea unei stive. Despre
stive am mai vorbit n Variabile externe, statice !i registru, dar n acel moment nu !tiam
despre lucrul cu structuri, deci stiva era implementat# sub form# de tablou care era accesat
doar printr-un singur cap#t. Deoarece n general dimensiunea unei stive nu este cunoscut#
dinainte, cea mai natural# implementare este cea de list# nl#n"uit#. Opera"iile de lucru cu
stiva se pot !i ele implementa ca n secven"a de mai jos:
static struct node
{
int key;
struct node next;
};
static struct node head,z,t;
void stackinit(void)
{
head=(struct node )malloc(sizeof head);
z=(struct node )malloc(sizeof z);
head>next=z;
head>key=0;
z>next=z;
}
void push(int v)
{
t=(struct node )malloc(sizeof t);
t>key=v;
t>next=head>next;
head>next=t;
}
int pop(void)
{
int x;
t=head>next;
head>next=t>next;
x=t>key;
free(t);
return x;
}
int stackempty(void)
{
return head>next==z;
}
Ca !i o aplica"ie imediat# la stiva implementat# mai sus, n cele ce urmeaz# avem o
implementare nerecursiv# a func"iei treeprint. Binen"eles c# func"iile de lucru cu stiva trebuie
adaptate ntr-un mod corespunz#tor.
void treeprint(struct tnode p)
{
push(p);
while(!=stackempty())

124
{
p=pop();
printf(%4d %s\n,p>count,p>word);
if(p>left!=z)
push(p>left);
if(p>right!=z)
push(p>right);
}
}
Exerci#iul 74. Modifica"i exemplul de mai sus de contorizare a tuturor cuvintelor din
intrare, rescriind func"iile recursive astfel nct s# elimina"i recursivitatea prin utilizarea unei
stive implementat# sub form# de structur# autoreferit#, ca n exemplul anterior.

Exerci#iul 75. Scrie"i o versiune nerecursiv# pentru func"ia qsort, utiliznd n mod
corespunz#tor structura de stiv# descris# mai sus. Folosi"i func"ia qsort ntr-un program pentru
a ordona cresc#tor argumentele din linia de comand# (care trebuie s# fie cel pu"in 5).

Exerci#iul 76. Scrie"i un program recursiv care cite!te un program C !i tip#re!te n
ordine alfabetic# fiecare grup de nume de variabile care au identice primele 6 caractere, dar
pot diferi n rest. Nu contoriza"i cuvintele cheie (inclusiv numele directivelor), numele
func"iilor, numele fi!ierelor incluse, numele definite cu directiva #define !i cuvintele cuprinse
n ghilimele !i comentarii. Face"i ca num#rul 6 s# fie un parametru care s# poat# fi schimbat
printr-un argument n linia de comand#.

Exerci#iul 77. Scrie"i un program recursiv care tip#re!te o list# cu toate cuvintele
dintr-un text n ordine alfabetic# !i pentru fiecare cuvnt o list# cu numerele de linii n care
apare. Elimina"i din list# cuvintele care snt alc#tuite numai din consoane, cuvintele care au !i
alte caractere n afar# de litere, precum !i cuvintele cu mai pu"in de 3 litere (alc#tuite din una
sau dou# litere).

Exerci#iul 78. Scrie"i un program recursiv care tip#re!te o list# cu toate cuvintele
distincte dintr-un text n ordinea descresc#toare a frecven"ei de apari"ie. Fiecare cuvnt va fi
precedat de contorul s#u.

Exerci#iul 79. Scrie"i un program recursiv care tip#re!te o list# cu toate cuvintele
distincte dintr-un text n ordinea cresc#toare a num#rului de consoane din cuvnt. Fiecare
cuvnt va fi precedat de contorul s#u. La sfr!it se va afi!a num#rul total de cuvinte !i num#rul
de cuvinte care apar de mai multe ori.


125
Exerci#iul 80. Scrie"i un program recursiv care tip#re!te o list# cu toate cuvintele
distincte dintr-un text n ordinea cresc#toare a num#rului de consoane din cuvnt. Fiecare
cuvnt va fi precedat de contorul s#u !i va fi urmat de o list# cu numerele de linii n care a
ap#rut. La sfr!it se va afi!a o list# cu cuvintele care apar de mai multe ori, fiecare cuvnt fiind
precedat de num#rul de apari"ii suplimentare (de exemplu, dac# un cuvnt apare de 3 ori, n a
doua list# el va apare precedat de cifra 2) !i va fi urmat de o list# cu numerele de linii n care a
ap#rut cuvntul, prima apari"ie nefiind luat# n considerare. Dac# un cuvnt apare de mai multe
ori ntr-o linie, num#rul liniei va fi inclus o singur! dat! n list#.

Exerci#iul 81. Scrie"i un program nerecursiv care cite!te un program C !i tip#re!te n
ordine alfabetic# fiecare grup de nume de variabile care au identice primele 6 caractere, dar
pot diferi n rest. Nu contoriza"i cuvintele cheie (inclusiv numele directivelor), numele
func"iilor, numele fi!ierelor incluse, numele definite cu directiva #define !i cuvintele cuprinse
n ghilimele !i comentarii. Face"i ca num#rul 6 s# fie un parametru care s# poat# fi schimbat
printr-un argument n linia de comand#.

Exerci#iul 82. Scrie"i un program nerecursiv care tip#re!te o list# cu toate cuvintele
dintr-un text n ordine alfabetic# !i pentru fiecare cuvnt o list# cu numerele de linii n care
apare. Elimina"i din list# cuvintele care snt alc#tuite numai din consoane, cuvintele care au !i
alte caractere n afar# de litere, precum !i cuvintele cu mai pu"in de 3 litere (alc#tuite din una
sau dou# litere).

Exerci#iul 83. Scrie"i un program nerecursiv care tip#re!te o list# cu toate cuvintele
distincte dintr-un text n ordinea descresc#toare a frecven"ei de apari"ie. Fiecare cuvnt va fi
precedat de contorul s#u.

Exerci#iul 84. Scrie"i un program nerecursiv care tip#re!te o list# cu toate cuvintele
distincte dintr-un text n ordinea cresc#toare a num#rului de consoane din cuvnt. Fiecare
cuvnt va fi precedat de contorul s#u. La sfr!it se va afi!a num#rul total de cuvinte !i num#rul
de cuvinte care apar de mai multe ori.

Exerci#iul 85. Scrie"i un program nerecursiv care tip#re!te o list# cu toate cuvintele
distincte dintr-un text n ordinea cresc#toare a num#rului de consoane din cuvnt. Fiecare
cuvnt va fi precedat de contorul s#u !i va fi urmat de o list# cu numerele de linii n care a
ap#rut. La sfr!it se va afi!a o list# cu cuvintele care apar de mai multe ori, fiecare cuvnt fiind
precedat de num#rul de apari"ii suplimentare (de exemplu, dac# un cuvnt apare de 3 ori, n a
doua list# el va apare precedat de cifra 2) !i va fi urmat de o list# cu numerele de linii n care a
ap#rut cuvntul, prima apari"ie nefiind luat# n considerare. Dac# un cuvnt apare de mai multe
ori ntr-o linie, num#rul liniei va fi inclus o singur! dat! n list#.

126
6.6 Declara#ia typedef
Limbajul C ne ofer# posibilitatea de a crea noi nume pentru tipuri de date, folosind
typedef. De exemplu, declara"ia
typedef int Lenght;
face ca numele Lenght s# fie sinonim cu int. Tipul Lenght poate fi astfel utilizat n declara"ii,
construc"ii cast, etc., n acela!i mod n care poate fi utilizat tipul int:
Lenght len,maxlen;
Lenght t[];
n mod analog, declara"ia
typedef char String;
face ca numele String s# fie sinonim cu char (pointer la caracter) !i poate fi utilizat n
declara"ii, construc"ii cast etc.:
String p,lineptr[MAXLINES];
int strcmp(String,String);
p=(String)malloc($00);
Not#m c# tipul declarat cu typedef apare n pozi"ia numelui variabilei !i nu imediat dup#
cuvntul typedef. Din punct de vedere sintactic, typedef este identic cu clasele de memorie
extern, static etc.
Ca !i un exemplu mai complex de utilizare a lui typedef, vom rescrie declara"ia unui nod
a arborelui !i func"ia talloc din paragraful anterior.
typedef struct tnode Treeptr;
typedef struct tnode
{
char word;
int count;
Treeptr left;
Treeptr right;
} Treenode;
Aceste declara"ii creaz# dou# noi cuvinte cheie, numite Treenode (o structur#) !i
Treeptr (un pointer la structur#). n acest fel, func"ia talloc poate fi scris# astfel:
struct tnode talloc(void)
{
return (Treeptr)malloc(sizeof(Treenode));
}
Men"ion#m faptul c# o declara"ie typedef nu creaz# un nou tip de date, ci adaug# un
nume nou pentru tipurile existente. Variabilele declarate cu typedef au acelea!i propriet#"i ca
!i variabilele declarate n mod explicit, f#r# typedef. n realitate declara"ia typedef este la fel ca
#define, cu excep"ia faptului c# fiind interpretat# de compilator, ea poate fi utilizat# pentru
substitu"ii pe care preprocesorul nu le poate face. De exemplu, declara"ia
typedef int (PFI)(char ,char );
creaz# tipul PFI, care este pointer la o func"ie cu dou# argumente de tip char !i care
returneaz# un int. Acest tip poate fi utilizat n declara"ii ca cea de mai jos:
PFI strcmp,numcmp;

127
Motivul principal pentru care se utilizeaz# typedef este portabilitatea programelor. Dac#
utiliz#m typedef pentru tipuri de date care snt dependente de calculator, cnd dorim s# rul#m
programul pe un alt calculator trebuie s# schimb#m numai declara"iile typedef. Un exemplu de
astfel de situa"ie este utilizarea lui typedef pentru a defini nume pentru diferite entit#"i ntregi
care snt dependente de calculator. n acest fel exist# tipuri ca size_t sau ptrdiff_t definite n
biblioteca standard. Un alt exemplu este tipul FILE, despre care vom vorbi n Intr#ri !i ie!iri.
6.7 Uniuni
O uniune este o variabil# care poate p#stra (la momente de timp diferite) obiecte de
tipuri !i m#rimi diferite. n cazul uniunilor compilatorul "ine cont de necesit#"ile de memorie
!i de aliniere a membrilor uniunii.
Uniunile furnizeaz# un mod de a manipula tipuri diferite de date ntr-o singur# zon# de
memorie, f#r# s# includem n program informa"ii dependente de calculator. Ele snt analogul
articolelor cu variante din Pascal.
Un exemplu de utilizare a uniunilor este tabela de simboluri a unui compilator. S#
presupunem c# o constant# poate fi un int, float sau pointer la caracter. Valoarea unei
constante particulare trebuie memorat# ntr-o variabil# de tip corespunz#tor (este evident c# o
constant# nu poate avea dect un singur tip, deci poate fi utilizat# o uniune). Este mult mai
convenabil dac# valoarea are alocat# aceea!i cantitate de memorie !i este memorat# n acela!i
loc, indiferent de tipul ei. Declara"ia unei uniuni este foarte asem#n#toare cu declara"ia unei
structuri.
union u_tag
{
int ival;
float fval;
char sval;
} u;
Zona de memorie pentru variabila u va fi suficient de mare astfel nct s# poat# con"ine
cel mai mare din cei trei membri. Variabilei u i se poate asigna o valoare de oricare din aceste
tipuri !i o putem utiliza n expresii, cu condi"ia ca utilizarea s# aib# sens (adic# tipul valorii
utilizate trebuie s# fie tipul valorii care a fost ultima dat# memorat#). Tipul curent al valorii
dintr-o uniune trebuie ntre"inut de programator. Dac# o dat# este memorat# cu un anumit tip
!i este utilizat# cu un alt tip, ceea ce se ntmpl# este dependent de implementarea limbajului.
Membrii unei uniuni se acceseaz# n acela!i mod ca membrii unei structuri. Dac#, de
exemplu, utiliz#m o variabil# utype pentru a p#stra tipul variabilei p#strate n u, atunci, pentru
a afi!a con"inutul variabilei u, putem folosi o secven"# de cod ca cea de mai jos:
if(utype==INT)
printf(%d\n,u.ival);
else if(utype==FLOAT)
printf(%f\n,u.fval);
else if(utype==STRING)
printf(%s\n,u.sval);
else
printf(tipul %d este eronat in variabila utype\n,utype);

128
Uniunile pot apare n structuri sau n tablouri !i invers. De exemplu, dac# avem defini"ia
urm#toare
struct
{
char name;
int flags;
int utype;
union u_tag
{
int ival;
float fval;
char sval;
} u;
} symtab[NSYM];
atunci membrul ival este accesat cu
symtab[i].u.ival
iar primul caracter din !irul sval poate fi accesat cu una din urm#toarele dou# expresii
symtab[i].u.sval
symtab[i].u.sval[0]
n realitate o uniune este o structur# n care to"i membrii au offset-ul zero n raport cu
baza, structura este suficient de mare astfel nct s# poat# cuprinde cel mai mare membru !i
alinierea este corespunz#toare pentru toate tipurile din uniune. Cu o uniune putem face
acelea!i opera"ii ca !i cu o structur#: asignarea sau copierea ei ca o unitate, ob"inerea adresei !i
accesul la membrii ei.
O uniune poate fi ini"ializat! numai cu o valoare de tipul primului ei membru. Astfel
uniunea u de mai sus poate fi ini"ializat# numai cu o valoare de tip ntreg.
6.8 Cmpuri de bi#i
Exist# situa"ii n care spa"iul de memorie ocupat trebuie s# fie ct mai mic. n astfel de
cazuri poate fi necesar s# mpachet#m mai multe obiecte ntr-un singur cuvnt. n aplica"ii ca
tabela de simboluri a unui compilator, este necesar# utilizarea cmpurilor de bi"i. De
asemenea, utilizarea de date externe cu un anumit format, cum ar fi interfa"a cu anumite
periferice, necesit# utilizarea unei p#r"i dintr-un cuvnt, deci a cmpurilor de bi"i.
S# ne imagin#m un compilator care manipuleaz# o tabel# de simboluri. Fiecare
identificator dintr-un program are asociate anumite propriet#"i, de exemplu dac# este cuvnt
cheie sau nu, dac# este extern sau static, etc. Cel mai compact mod de a codifica asemenea
informa"ii este cte un bit pentru fiecare proprietate, iar bi"ii s# fie grupa"i ntr-un singur char
sau int. Modul practic n care se realizeaz# acest lucru este s# definim o mul"ime de m#!ti
corespunz#toare pozi"iilor bit semnificative, ca de exemplu
#define KEYWORD 0$
#define EXTERNAL 02
#define STATIC 04
sau
enum {KEYWORD=0$, EXTERNAL=02, STATIC=04};

129
Numerele trebuie s# fie puteri ale lui 2. Dup# definirea m#!tilor, accesarea bi"ilor se
realizeaz# cu ajutorul operatorilor pe bi"i descri!i n Tipuri de date, operatori !i expresii.
Astfel, pot fi ntlnite frecvent expresii ca
flags |= EXTERNAL | STATIC;
care seteaz# bi"ii din flag corespunz#tor lui EXTERNAL !i STATIC,
flags &= ~(EXTERNAL | STATIC);
care !terge aceea!i bi"i din flag !i
if((flags & (EXTERNAL | STATIC))==0)
care este adev#rat# dac# ambii bi"i din flag corespunz#tor lui EXTERNAL !i STATIC snt
!ter!i (snt egali cu zero).
De!i astfel de expresii snt des utilizate n programe, limbajul C ne ofer# ca o alternativ#
posibilitatea de a defini !i accesa n mod direct cmpuri de bi"i dintr-un cuvnt, n loc de a
utiliza operatorii pe bi"i. Un cmp de bi"i (bit-field ) sau cmp (field ) pe scurt, este o mul"ime
de bi"i consecutivi dintr-o singur# unitate de memorare dependent# de calculator, pe care o
numim cuvnt. Definirea !i accesarea cmpurilor de bi"i se bazeaz# pe structuri. De
exemplu, tabela de simboluri definit# mai sus cu #define poate fi nlocuit# cu defini"ia a trei
cmpuri:
struct
{
unsigned int is_keyword:$;
unsigned int is_extern:$;
unsigned int is_static:$;
} flags;
Variabila flags definit# mai sus con"ine trei cmpuri de cte un bit. Num#rul care
urmeaz# dup# caracterul : reprezint# dimensiunea cmpului n bi"i. Cmpurile snt declarate
unsigned int pentru a ne asigura c# reprezint# entit#"i f#r# semn.
Cmpurile individuale snt accesate n acela!i mod ca !i membrii unei structuri !i se
comport# ca !i ni!te ntregi mici, care pot fi utiliza"i n expresii aritmetice ca !i orice alt ntreg.
Astfel expresiile de mai sus pot fi scrise astfel:
flags.is_extern=flags.is_static=$;
pentru a seta bi"ii,
flags.is_extern=flags.is_static=0;
pentru a !terge bi"ii !i
if(flags.is_extern==0 && flags.is_static==0)
pentru a-i testa.
Implementarea cmpurilor de bi"i este dependent# de implementarea limbajului.
Cmpurile nu trebuie s# aib# neap#rat un nume, cele f#r# nume pot fi utilizate pentru umplerea
bi"ilor nesemnificativi. Dac# un cmp dep#!e!te limita unui cuvnt, ce se ntmpl# depinde de
implementare. De asemenea, cmpurile snt asignate de la dreapta la stnga pe unele
calculatoare, sau de la stnga la dreapta pe altele. Aceasta nseamn# c#, de!i cmpurile snt
utile pentru prelucrarea unor structuri de date, trebuie s# "inem cont de modul de asignare cnd
prelucr#m date externe cu o structur# bine definit#. Programele care depind de acest lucru nu
snt portabile.

130
Cmpurile pot fi declarate numai int. Pentru portabilitatea programului un cmp trebuie
declarat explicit ca fiind signed sau unsigned. Cmpurile de bi"i nu snt tablouri !i nu au
adres#, deci nu le putem aplica operatorul &.
6.9 Rezumat
Tipurile de date simple, sau chiar !i tablourile, nu snt ntotdeauna suficiente pentru a
realiza programe n care datele au o form# complex#. Din aceast# cauz#, structurile ocup# !i
ele un loc important n cadrul limbajului. Dup# cum s-a putut observa, a reie!it importan"a
pointerilor !i n cazul lucrului cu structuri. Un loc aparte l ocup# structurile autoreferite, care
ajut# mult n rezolvarea unor probleme, n special cele care utilizeaz# arbori binari. Uniunile
joac# !i ele un rol important n situa"iile n care o zon# de memorie poate fi folosit# n comun
de dou# sau mai multe variabile.

131
7. INTR&RI $I IE$IRI
Facilit#"ile de I/O nu fac parte din limbajul C. Din acest motiv nu le-am prezentat n
detaliu, ci doar n m#sura n care am avut nevoie de ele. Programele interac"ioneaz# cu
calculatorul ntr-un mod mult mai complicat dect am v#zut pn# acum. n acest capitol vom
descrie o serie de func"ii din biblioteca standard, cum ar fi func"ii de I/O, manipularea
!irurilor, alocarea memoriei, func"ii matematice, precum !i o serie de alte func"ii utile n
programele C. Aten"ia principal# va fi acordat# func"iilor de I/O.
Standardul ANSI define!te func"iile din biblioteca standard n mod precis, astfel nct
ele exist# n forme compatibile n orice implementare a limbajului C. Programele care
utilizeaz# numai func"ii din biblioteca standard pot fi utilizate n orice implementare a
limbajului C, f#r# modific#ri (snt portabile).
7.1 Intrarea "i ie"irea standard
A!a cum am v#zut n Ini"iere n C, pentru introducerea !i afi!area de text exist# o
implementare simpl#. Un text (text stream) const# dintr-o secven"# de linii n care fiecare linie
se termin# cu un caracter newline. Dac# sistemul de operare nu lucreaz# n acest mod,
func"iile din biblioteca standard a limbajului C pot face ceea ce trebuie, astfel nct pentru
program s# apar# ca !i cum acest lucru ar fi f#cut de sistemul de operare. De exemplu,
func"iile de bibliotec# pot s# converteasc# perechea de caractere carriage return !i linefeed
ntr-un singur caracter newline la intrare !i invers la ie!ire.
7.1.1 Func#iile getchar "i putchar
Cel mai simplu mecanism de intrare este citirea unui singur caracter la un moment dat
din intrarea standard, n mod normal de la tastatur#, cu ajutorul func"iei getchar:
int getchar(void);
Func"ia getchar returneaz# urm#torul caracter din intrare de fiecare dat# cnd este
apelat#, sau EOF cnd ntlne!te sfr!itul fi!ierului. Constanta simbolic# EOF este definit# n
<stdio.h>. De obicei valoarea lui EOF este 1, dar n teste trebuie utilizat# constanta EOF,
astfel nct programele s# devin# independente de valoarea ei.
n multe sisteme de operare un fi!ier poate fi utilizat n locul tastaturii prin utilizarea
nota"iei < << < pentru redirectarea intr#rii, n modul urm#tor: dac# programul prog este lansat n
execu"ie n forma
prog <infile
atunci citirea se face din fi!ierul infile !i nu de la tastatur#. Aceast# comutare a intr#rii este
f#cut# astfel nct programul nu o realizeaz#. Func"ia getchar poate fi redirectat# astfel nct
citirea s# se realizeze dintr-un fi!ier n locul tastaturii.

132
%irul < << <infile nu este inclus n argumentele liniei de comand!.
Comutarea intr#rii este de asemenea invizibil# dac# intrarea vine de la un alt program
printr-un mecanism numit pipe. Astfel, n unele sisteme de operare, linia de comand#
altprog | prog
execut# ambele programe altprog !i prog !i transmite (pipe) ie!irea standard a programului
altprog n intrarea standard a programului prog.
Func"ia
int putchar(int);
este utilizat# pentru ie!ire. Apelul putchar(c) pune caracterul c n ie#irea standard, care este
n mod implicit ecranul. Func"ia returneaz# caracterul scris, sau EOF n cazul apari"iei unei
erori. %i ie!irea standard poate fi redirectat# ntr-un fi!ier cu ajutorul conven"iei > >> >, astfel nct
linia de comand#
prog >outfile
scrie n fi!ierul outfile tot ceea ce programul prog scrie n ie!irea standard.
%irul >outfile nu este inclus n argumentele liniei de comand!.
Dac# este suportat mecanismul pipe, atunci comanda
prog | altprog
pune ie!irea standard a programului prog n intrarea standard a programului altprog.
Ie!irile realizate de func"ia printf folosesc acelea!i conven"ii pentru ie!irea standard ca !i
func"ia putchar.
Fiecare fi!ier surs# care utilizeaz# func"ii de I/O din biblioteca standard trebuie s#
con"in# linia
#include <stdio.h>
Folosind numai aceste func"ii pentru intr#ri !i ie!iri, precum !i redirectarea intr#rii !i a
ie!irii, se pot scrie programe surprinz#tor de complexe. De exemplu, programul de mai jos
transform# toate literele din intrare n litere mici:
#include <stdio.h>
#include <ctype.h>
main()
{
int c;
while((c=getchar())!=EOF)
putchar(tolower(c));
return 0;
}
Func"ia tolower este definit# n <ctype.h> !i converte!te o liter# mare n liter# mic#,
l#snd celelalte caractere nemodificate. Deseori getchar, putchar sau tolower snt definite ca
macro-uri. Programele care folosesc astfel de func"ii snt independente de setul de caractere
utilizat pe un anumit calculator.

133
Exerci#iul 86. Scrie"i un program care converte!te literele mari n litere mici sau invers,
n func"ie de un parametru n linia de comand#.
7.1.2 Func#ia printf
Func"ia printf transform# valorile interne n !iruri de caractere. n capitolele anterioare
am utilizat aceast# func"ie !i am prezentat o parte din facilit#"ile oferite. n continuare o vom
prezenta mai n detaliu. Declara"ia func"iei este
int printf(const char format,...);
Func"ia printf converte!te, formateaz# !i tip#re!te n ie!irea standard argumentele sale
sub controlul !irului format !i returneaz# num#rul de caractere tip#rite sau un num#r negativ
n caz de eroare.
%irul de formatare con"ine dou# tipuri de obiecte: caractere obi!nuite, care snt copiate n
ie!irea standard !i specificatori de conversie, fiecare din ei cauznd conversia !i tip#rirea
urm#torului argument succesiv al lui printf. Fiecare specificator de conversie ncepe cu
caracterul % !i se termin# cu un caracter de conversie. ntre caracterul % !i caracterul de
conversie pot fi, n ordine:
Indicatori (flags), n orice ordine, care au urm#toarea semnifica"ie:
: argumentul va fi aliniat la stnga n cmp, iar cmpul va fi umplut la
dreapta cu spa"ii.
+ ++ +: num#rul va fi tip#rit ntotdeauna cu semn.
spa"iu: dac# num#rul este nenegativ, primul caracter va fi spa"iu n loc de
semnul +, iar dac# num#rul este negativ !i p#streaz# semnul.
0: pentru conversii numerice specific# umplerea spa"iilor din fa"a num#rului
cu zerouri.
#: specific# o form# de ie!ire alternat#. Pentru o, prima cifr# va fi zero.
Pentru x sau X, dac# num#rul este nenul, el va fi prefixat de 0x, respectiv
0X. Pentru e, E, f, g sau G, num#rul va fi tip#rit ntotdeauna cu punct
zecimal, chiar dac# nu are parte zecimal#. n plus, pentru g sau G zerourile
de la sfr!it nu snt !terse. Pentru celelalte caractere de conversie nu are
efect.
Un num!r, care specific# dimensiunea minim! a cmpului. Argumentul
convertit va fi tip#rit n cmp pe cel pu"in attea caractere. Dac# argumentul
convertit are mai pu"ine caractere dect dimensiunea cmpului, el va fi umplut
cu spa"ii sau cu zero (dac# flagul 0 este prezent).
Un punct, care separ! dimensiunea cmpului de precizie.
Un num!r, care reprezint# precizia !i specific# num#rul maxim de caractere
dintr-un !ir care vor fi tip#rite, num#rul de cifre care vor fi tip#rite dup# punctul
zecimal pentru e, E sau f, num#rul de cifre semnificative pentru g sau G, sau
num#rul minim de cifre care vor fi tip#rite pentru un ntreg.
Un modificator de lungime h, l sau L. Modidicatorul h indic# faptul c#
argumentul va fi tip#rit ca short sau unsigned short, l indic# un argument de
tip long, iar L indic# un argument de tip long double.

134
Dimensiunea, precizia sau ambele pot fi nlocuite cu , caz n care valoarea este
calculat# prin conversia urm#torului argument (sau argumente), care trebuie s# fie un int. De
exemplu, pentru a tip#ri cel mult max caractere din !irul s se poate utiliza
printf(%.s,max,s);
Caracterele de conversie au semnifica"ia din tabelul de mai jos. Dac# dup# % urmeaz#
un alt caracter dect unul de conversie, comportarea func"iei printf este nedefinit#.
Caracter Tip argument Ce se tip%re"te
d, i int num#r zecimal.
o int num#r octal f#r# semn (f#r# zero n fa"#).
x, X int num#r hexazecimal f#r# semn, f#r# 0x sau 0X n fa"# !i
utiliznd literele a, b, c, d, e, f, respectiv A, B, C, D, E, F
pentru cifrele 10, 11, 12, 13, 14, respectiv 15 (din baza
16!).
u int num#r zecimal f#r# semn.
c int un singur caracter.
s char tip#re!te caracterele din !ir pn# cnd ntlne!te caracterul
\0 sau pn# cnd tip#re!te caracterele cerute de precizie.
f double []m.dddddd, unde num#rul de cifre zecimale este dat de
precizie (implicit este 6).
e, E double []m.dddddde[]xx sau [-]m.ddddddE[]xx, unde num#rul
de cifre zecimale este dat de precizie (implicit este 6).
g, G double se utilizeaz# %e sau %E dac# exponentul este mai mic dect
4 sau mai mare sau egal cu precizia. Zerourile
nesemnificative de la sfr!it !i punctul zecimal cnd
partea zecimal# este zero nu snt tip#rite.
p void pointer. Ce se afi!eaz# este depenent de calculatorul pe care
este implementat limbajul.
% %% % nu se realizeaz# nici o conversie, ci este tip#rit caracterul % %% %.
De men"ionat c# printf utilizeaz# primul argument pentru a determina cte argumente
mai snt dup# el !i care este tipul lor. Dac# acest lucru nu poate fi dedus corect, comportarea
func"iei printf este nedefinit#. Trebuie s# "inem cont de diferen"a dintre cele dou# apeluri de
mai jos:
printf(s); /Esueaza daca s contine caracterul %/
printf(%s,s); /Corect/
Func"ia sprintf face acelea!i conversii ca printf, cu deosebirea c# rezultatul este depus
ntr-un !ir:
int sprintf(char string,const char format,...);
%irul string trebuie s# fie suficient de mare pentru ca s# poat# cuprinde rezultatul.
7.1.3 Func#ii cu num%r variabil de argumente
Declara"ia func"iei printf este:
int printf(const char format,);
Se observ# c# aceast# declara"ie con"ine caracterele , ceea ce nseamn# c# num#rul de
argumente al func"iei !i tipul lor poate varia. Declara"ia poate apare numai la sfr!itul listei
de argumente.

135
Pentru a vedea cum se prelucreaz# o list# cu un num#r variabil de argumente, vom scrie
o func"ie mpf, care este o versiune simplificat# a func"iei printf. Deoarece ne intereseaz# n
primul rnd modul de prelucrare al argumentelor, func"ia mpf va avea doar o parte din
facilit#"ile func"iei printf.
Problema cea mai important# este cum putem s# parcurgem lista de argumente cnd
aceasta nu are nici m#car un nume. Fi!ierul header standard <stdarg.h> con"ine un set de
macro defini"ii care definesc modul n care se poate parcurge lista de argumente.
Implementarea acestor macro defini"ii difer# de la un calculator la altul, dar interfa"a pe care o
ofer# este aceea!i.
Tipul va_list este utilizat pentru a declara o variabil# care va referi pe rnd fiecare
argument (aceast# variabil# este n realitate un pointer de tipul va_list).
Macro-ul va_start ini"ializeaz# variabila de tipul va_list pentru a pointa la primul
argument f#r# nume. El trebuie apelat o singur# dat# nainte de a utiliza variabila. ntr-o
func"ie cu un num#r variabil de argumente trebuie s# existe cel pu"in un argument care s# aib#
nume, iar ultimul argument cu nume va fi utilizat de va_start.
Fiecare apel la macro-ul va_arg ne returneaz# o valoare, iar variabila de tipul va_list ne
va indica urm#torul argument. va_arg utilizeaz# numele tipului pentru a determina ce tip are
rezultatul returnat !i cu ct trebuie incrementat# variabila de tip va_start astfel nct s# ne
indice urm#torul argument din list#. n final trebuie apelat macro-ul va_end cnd func"ia se
termin#.
#include <stdio.h>
#include <stdarg.h>
void mpf(char fmt)
{
int ival;
double dval;
char p,sval;
va_list ap;
va_start(ap,fmt);
for(p=fmt;p;p++)
{
if(p!=%)
{
putchar(p);
continue;
}
switch(++p)
{
case d:
ival=va_arg(ap,int);
printf(%d,ival);
break;
case f:
dval=va_arg(ap,double);
printf(%f,dval);
break;
case s:
for(sval=va_arg(ap,char );sval;sval++)
putchar(sval);
break;
default:
putchar(p);

136
break;
}
}
va_end(ap);
}
Exerci#iul 87. Extinde"i func"ia mpf de mai sus astfel nct s# aib# o comportare identic#
cu func"ia printf.
7.1.4 Func#ia scanf
Func"ia scanf este analoaga lui printf, !i este folosit# pentru introducerea datelor.
Func"ia scanf furnizeaz# majoritatea facilit#"ilor de conversie pe care le are printf, dar n sens
opus.
int scanf(const char format,...);
Func"ia scanf cite!te caractere din intrarea standard, le interpreteaz# conform
specifica"iilor din format !i memoreaz# rezultatele n argumentele r#mase. Primul argument
va fi descris n continuare. Celelalte argumente, care trebuie s! fie n mod obligatoriu
pointeri, ne indic# unde vor fi memorate valorile corespondente din intrare, dup# ce a fost
realizat# conversia lor la tipul corespunz#tor.
Func"ia scanf se opre!te cnd !irul cu specifica"iile de formatare (primul argument) este
parcurs n ntregime, sau cnd la intrare snt ntlnite date care nu respect# specifica"iile de
formatare. Valoarea returnat# este num#rul de asign#ri realizate, sau EOF dac# a fost ntlnit
sfr!itul de fi!ier. Men"ion#m faptul c# dac# scanf returneaz# zero, nseamn# c# urm#torul
caracter din intrare nu respect# specificatorul de formatare din !irul de control.
Analog ca la printf, exist# func"ia sscanf, care cite!te datele dintr-un !ir n loc de
intrarea standard. Aceast# func"ie se comport# identic cu scanf.
int sscanf(char string,const char format,...);
Argumentul format con"ine de obicei specifica"ii de conversie, care snt utilizate pentru
interpretarea introducerii. %irul format poate con"ine:
Spa"ii sau tab-uri, care snt ignorate.
Caracter obi!nuit, altul dect %, care trebuie s# fie identic cu urm#torul caracter
non-spa"iu din intrare.
Specificatori de conversie, care snt forma"i din caracterul %, un caracter
op"ional de suspendare a asign#rii, un num#r op"ional care specific#
dimensiunea maxim# a cmpului, o liter# op"ional# h, l sau L care indic#
m#rimea zonei de memorare !i un caracter de conversie.
Un specificator de conversie determin# conversia urm#torului cmp din intrare. n mod
normal rezultatul este plasat n variabila pointat# de argumentul corespondent. Dac# este
utilizat caracterul de suspendare a asign#rii, ca de exemplu n %s, cmpul din intrare este
s#rit !i nu se realizeaz# nici o asignare. Un cmp din intrare este definit ca un !ir de caractere
non-spa"iu !i se ntinde pn# la urm#torul caracter non-spa"iu sau pn# cnd este dep#!it#
dimensiunea cmpului, dac# este precizat#.
Caracterele de conversie au semnifica"ia din tabelul de mai jos.

137
Caracter Dat% introdus% Tip argument
d ntreg zecimal. int
i ntreg. Poate fi !i n baza 8 dac# are un zero n fa"#, sau n
baza 16 dac# are n fa"# 0x sau 0X.
int
o ntreg octal, cu sau f#r# zero n fa"#. int
u num#r zecimal f#r# semn. unsigned int
x ntreg hexazecimal, cu sau f#r# 0x sau 0X n fa"#. int
c caracter. Urm#toarele caractere din intrare snt plasate n
tabloul indicat, pn# la num#rul dat de dimensiunea
cmpului. Nu se adaug# la sfr!it terminatorul \0. n mod
normal nu se sare peste caracterele spa"iu. Dac# dorim s#
citim urm#torul caracter non-spa"iu trebuie s# utiliz#m
forma %1s.
char
s !ir de caractere nonspa"iu, f#r# delimitatori. Argumentul
trebuie s# fie un pointer la un tablou de caractere sufi-
cient de mare astfel nct s# poat# con"ine !irul !i
terminatorul \0, care va fi ad#ugat la sfr!it.
char
e, f, g num#r real care poate avea forma [-]m[.dddddd[exx]] sau
[-]m[.dddddd[Exx]].
float
p pointer. Valoarea citit# trebuie s# aib# aceea!i form# ca cea
tip#rit# de func"ia printf( % %% %p ).
void
% %% % nu se realizeaz# nici o asignare, ci este citit caracterul %.
Caracterele de conversie d, i, o, u !i x pot fi precedate de h pentru a indica un pointer la
short n loc de un pointer la int, sau de litera l, pentru a indica un pointer la long. Analog,
caracterele de conversie e, f !i g pot fi precedate de litera l, pentru a indica un pointer la
double n loc de un pointer la float.
Ca o observa"ie final#, facem precizarea c# argumentele lui scanf !i sscanf trebuie s! fie
pointeri. Una din cele mai frecvente erori care pot apare n utilizarea acestor func"ii este de
forma
scanf(%d,n);
Astfel de erori nu snt detectate la compilare !i snt mai greu de observat.
Exerci#iul 88. Scrie"i o versiune a func"iei scanf, analoag# cu func"ia mpf din paragraful
anterior (de la printf), care s# aib# toate facilit#"ile func"iei scanf.
7.2 Lucrul cu fi"iere
7.2.1 No#iuni generale
Programele pe care le-am scris pn# acum citeau din intrarea standard !i scriau n ie!irea
standard, iar acestea puteau fi redirectate c#tre fi!iere. Intrarea !i ie!irea standard snt definite
pentru un program n mod automat de sistemul de operare.
n afara acestor dou# fi!iere, exist# !i fi!iere care nu snt conectate la program. n cele ce
urmeaz# vom vedea cum putem utiliza astfel de fi!iere.

138
S# consider#m problema scrierii unui program cat care s# concateneze un set de fi!iere
n ie!irea standard. Numele fi!ierelor snt date ca argumente n linia de comand#. Programul
cat poate fi utilizat pentru tip#rirea fi!ierelor pe ecran.
Problema care se pune este cum s# conect#m un fi!ier (care este un obiect extern
programului !i care poate s# nici nu existe) la program, astfel nct s# putem realiza opera"ii de
citire !i/sau scriere n fi!ier. Acest lucru se realizeaz# simplu. nainte de a lucra cu un fi!ier,
acesta trebuie deschis cu ajutorul func"iei din biblioteca standard fopen. Aceast# func"ie
conecteaz# la program fi!ierul cu numele precizat n primul argument !i returneaz# un pointer
care poate fi utilizat n citirea sau scrierea fi!ierului. Acest pointer, numit pointer de fi#ier,
pointeaz# c#tre o structur# care con"ine informa"ii despre fi!ier, cum ar fi loca"ia bufferului,
pozi"ia caracterului curent din buffer, dac# fi!ierul este deschis pentru citire sau pentru scriere,
dac# au ap#rut erori sau a fost detectat sfr!itul de fi!ier etc. Utilizatorul nu trebuie s#
cunoasc# aceste detalii, deoarece n <stdio.h> este definit# aceast# structur# cu numele FILE.
Not#m c# FILE este un nume de tip (este definit cu typedef n <stdio.h>). De asemenea
prototipul func"iei fopen nu trebuie declarat n programe, deoarece el este declarat n
<stdio.h>.
Apelul la fopen se realizeaz# n program cu o instruc"iune de forma
fp=fopen(name,mode);
unde fp este un pointer de tipul FILE .
Primul argument al func"iei fopen este un !ir de caractere care con"ine numele fi!ierului.
Cel de-al doilea argument, mode, este un !ir de caractere care indic# modul de deschidere !i
de utilizare al fi!ierului. Modurile permise snt date n tabelul de mai jos.
Modul Descrierea
r Deschide un fi!ierul pentru citire. Fi!ierul trebuie s#
existe.
w Deschide un fi!ier vid pentru scriere. Dac# fi!ierul
exist#, con"inutul lui este distrus.
a Deschide fi!ierul pentru scriere la sfr!it (ad#ugare).
Dac# fi!ierul nu exist#, l creaz#.
r+ Deschide fi!ierul pentru citire !i scriere. Fi!ierul
trebuie s# existe.
w+ Deschide un fi!ier vid pentru citire !i scriere. Dac#
fi!ierul nu exist#, l creaz#.
a+ Deschide fi!ierul pentru citire !i scriere la sfr!it
(ad#ugare). Dac# fi!ierul nu exist#, l creaz#.
Unele sisteme de operare fac distinc"ie ntre fi!ierele text !i cele binare. n acest caz,
pentru a prelucra un fi!ier binar trebuie ad#ugat# op"iunea b la argumentul mode.
Dac# deschidem un fi!ier cu a sau a+, toate opera"iile de scriere n fi!ier apar la
sfr!itul fi!ierului. Chiar dac# pointerul de fi!ier este mutat n alt# parte, el este pozi"ionat
ntotdeauna la sfr!it naintea unei opera"ii de scriere.
Dac# deschidem pentru scriere un fi!ier care nu exist#, acesta este creat, dac# este
posibil. Dac# deschidem pentru scriere un fi!ier existent, con"inutul acestuia este distrus, iar la
deschiderea pentru ad#ugare vechiul con"inut se p#streaz#. ncercarea de a deschide pentru
citire un fi!ier inexistent produce o eroare. Tot o eroare se produce !i la deschiderea pentru
scriere a unui fi!ier protejat la !tergere sau deschiderea unui fi!ier la care nu avem acces. n

139
toate situa"iile n care apare o eroare la deschiderea fi!ierului, func"ia fopen returneaz#
pointerul NULL.
Exist# mai multe posibilit#"i de a realiza opera"ii de citire dintr-un fi!ier sau de scriere n
fi!ier. Cel mai simplu mod este utilizarea func"iilor getc !i putc. La fel ca n cazul lui getchar
!i putchar, getc !i putc pot fi macro-uri.
Func"ia getc returneaz# urm#torul caracter din fi!ier, sau EOF n cazul apari"iei unei
erori sau a detect#rii sfr!itului de fi!ier.
int getc(FILE fp);
Func"ia putc este folosit# pentru scriere n fi!ier. Ea returneaz# caracterul scris, sau EOF
n cazul apari"iei unei erori.
int putc(int c,FILE fp);
Cnd un program C este lansat n execu"ie, sistemul de operare deschide automat trei
fi!iere !i furnizeaz# pointeri de fi!ier pentru ele. Aceste fi!iere snt intrarea standard, ie#irea
standard !i ie#irea standard pentru erori, iar pointerii de fi!ier corespunz#tori se numesc
stdin, stdout !i respectiv stderr, !i snt declara"i n <stdio.h>. Ace!ti pointeri snt obiecte de
tipul FILE . Ei snt constante !i nu variabile, deci nu este posibil s# le asociem valori. n
mod normal stdin este conectat la tastatur#, iar stdout !i stderr snt conecta"i la ecran, dar
stdin !i stdout pot fi redirecta"i c#tre fi!iere, a!a cum am v#zut la nceputul acestui capitol.
Avnd getc !i putc, putem defini getchar !i putchar astfel:
#define getchar() getc(stdin)
#define putchar(c) putc(c,stdout)
Pentru intr#ri !i ie!iri cu format n fi!iere putem utiliza func"iile fscanf !i fprintf. Aceste
dou# func"ii snt identice cu scanf !i respectiv printf, cu deosebirea c# primul argument este
un pointer de fi!ier, care indic# fi!ierul n care se face opera"ia de citire sau scriere, iar !irul cu
format este al doilea argument.
int fscanf(FILE fp,constant char format,...);
int fprintf(FILE fp,constant char format,...);
Cu ajutorul func"iilor prezentate mai sus putem scrie programul cat.
#include <stdio.h>
void filecopy(FILE ,FILE );
main(int argc,char argv)
{
FILE fp;
if(argc==$)
filecopy(stdin,stdout);
else
while(argc>0)
if((fp=fopen(++argv,r))==NULL)
{
printf(cat: nu se poate deschide %s\n,argv);
return $;
}
else
{
filecopy(fp,stdout);
fclose(fp);
}

140
return 0;
}
void filecopy(FILE ifp,FILE ofp)
{
int c;
while((c=getc(ifp))!=EOF)
putc(c,ofp);
}
Func"ia
int fclose(FILE fp);
este inversa lui fopen. Ea ntrerupe leg#tura dintre pointerul de fi!ier !i numele extern,
leg#tur# stabilit# de fopen, elibernd pointerul de fi!ier pentru alte fi!iere (nchide fi!ierul).
Deoarece marea majoritate a sistemelor de operare permit deschiderea simultan# a unui num#r
limitat (mic) de fi!iere, este bine ca atunci cnd nu mai avem nevoie de un anumit fi!ier s# l
nchidem (acest lucru este important de realizat dac# fi!ierul este de ie!ire). n plus, fclose
gole!te bufferul n care scrie putc. Dac# un program se termin# normal, func"ia fclose este
apelat# automat pentru fiecare fi!ier deschis. Dac# nu mai avem nevoie de stdin sau stdout, le
putem nchide.
Biblioteca standard ne furnizeaz# func"ia ungetc, utilizat# pentru a pune napoi n fi!ier
un caracter citit n plus. Ea poate fi utilizat# cu orice func"ie de tipul lui scanf, getc sau
getchar !i are sintaxa
int ungetc(int c,FILE fp);
n caz de eroare ungetc returneaz# EOF, iar n caz contrar returneaz# caracterul pus
napoi n fi!ier.
7.2.2 Tratarea erorilor
n programul anterior erorile nu snt tratate n modul cel mai bun. Problema este c# dac#
un fi!ier nu poate fi accesat (din diverse motive), se tip#re!te un mesaj n fi!ierul standard de
ie!ire, la sfr!itul ultimului fi!ier concatenat. Acest lucru nu este prea important dac# ie!irea se
face pe ecran, dar dac# ie!irea este redirectat#, atunci mesajul este scris n fi!ier. Mai mult, noi
nici nu vom !ti dac# a fost sau nu vreo eroare la deschiderea fi!ierelor.
Pentru a putea rezolva mai bine o astfel de situa"ie, exist# un al doilea fi!ier standard de
ie!ire, numit stderr, care este asociat programului la lansarea n execu"ie. n mod normal
ie!irile n stderr apar pe ecran, chiar dac# ie!irea standard este redirectat#.
Folosind aceste observa"ii, programul cat poate fi rescris ca mai jos.
#include <stdio.h>
void filecopy(FILE ,FILE );
main(int argc,char argv)
{
FILE fp;
char prog=argv;
if(argc==$)
filecopy(stdin,stdout);
else
while(argc>0)

141
if((fp=fopen(++argv,r))==NULL)
{
fprintf(stderr,%s: nu se poate deschide %s\n, prog,argv);
exit($);
}
else
{
filecopy(fp,stdout);
fclose(fp);
}
if(ferror(stdout))
{
fprintf(stderr,%s: eroare la scriere in stdout,prog);
exit(2);
}
exit(0);
}
Programul semnaleaz# erorile n dou# moduri. n primul rnd, mesajul este scris n
stderr, deci va apare pe ecran. Este tip#rit !i numele programului, pentru ca n situa"ia n care
numele extern al programului este schimbat (de exemplu nu se mai nume!te cat) s# se !tie de
unde a ap#rut eroarea.
n al doilea rnd, programul utilizeaz# func"ia standard exit, care termin# execu"ia
programului cnd este apelat#. Argumentul lui exit poate fi utilizat de alte programe care
utilizeaz# acest program, pentru a vedea modul de terminare al programului. Prin conven"ie,
cnd programul se termin# f#r# erori, este returnat# valoarea 0, iar n caz de terminare
anormal# este returnat# o valoare nenul#. Func"ia exit apeleaz# fclose pentru fiecare fi!ier
deschis, pentru a goli bufferele fi!ierelor de ie!ire.
n func"ia main,
return expresie;
este echivalent cu
exit(expresie);
Avantajul folosirii func"iei exit este acela c# ea poate fi apelat# din orice func"ie, nu
numai din main.
Func"ia ferror returneaz# o valoare nenul# dac# a ap#rut o eroare n fi!ierul fp !i are
prototipul
int ferror(FILE fp);
De!i erorile la ie!ire snt mai rare, ele pot apare (de exemplu n situa"ii n care discul
este plin), deci este bine s# le trat#m.
Func"ia feof returneaz# o valoare nenul# dac# a fost ntlnit sfr!itul de fi!ier, iar
prototipul ei este
int feof(FILE fp);
7.2.3 Citirea "i scrierea liniilor
Biblioteca standard furnizeaz# o func"ie pentru citirea unei linii, care este asem#n#toare
cu func"ia getline pe care am utilizat-o n capitolele anterioare:
char fgets(char line,int maxline,FILE fp);

142
Func"ia fgets cite!te urm#toarea linie din fi!ierul fp (inclusiv caracterul newline) !i
depune caracterele citite n tabloul line. Vor fi citite cel mult maxline-1 caractere, iar linia
ob"inut# se va termina cu caracterul \0, care este ad#ugat automat. fgets returneaz# line sau
EOF la sfr!itul fi!ierului (func"ia getline returneaz# lungimea liniei, care este o valoare mai
util#, iar la sfr!itul fi!ierului returneaz# zero).
Pentru scrierea unei linii avem func"ia fputs. Linia care se scrie nu trebuie s# se termine
cu caracterul newline. Func"ia fputs returneaz# EOF dac# a ap#rut o eroare, sau zero n caz
contrar.
int fputs(char line,FILE fp);
Func"iile standard gets !i puts snt similare cu fgets !i fputs, dar lucreaz# cu stdin,
respectiv stdout. n plus, gets !terge caracterul newline de la sfr!itul liniei, iar puts l adaug#.
Pentru a vedea cum arat# o func"ie din biblioteca standard, vom da mai jos textul surs#
al func"iilor fgtes !i fputs, a!a cum apare el n multe implement#ri ale limbajului C.
char fgets(char s,int n,FILE iop)
{
register int c;
register char s;
cs=s;
while(n>0 && (c=getc(iop))!=EOF)
if((cs++=c)==\n)
break;
cs=\0;
return (c==EOF && cs==s) ? NULL : s;
}
int fputs(const char s,FILE iop)
{
int c;
while(c=s++)
putc(c,iop);
return ferror(iop) ? EOF : 0;
}
Avnd func"ia fgets, este u!or s# rescriem func"ia getline:
int getline(char line,int max)
{
if(fgets(line,max,stdin)==NULL)
return 0;
else
return strlen(line);
}
Exerci#iul 89. Scrie"i un program care s# compare dou# fi!iere (text), tip#rind prima
linie !i caracterul unde ele difer#. Numele fi!ierelor se citesc ca argumente n linia de
comand#.


143
Exerci#iul 90. Scrie"i un program care tip#re!te pe ecran un set de fi!iere (text),
ncepnd fiecare fi!ier pe o pagin# nou#, cu un titlu !i num#r de pagin#, iar la umplerea fiec#rei
pagini s# fac# o pauz# (o pagin# va avea dimensiunea unui ecran, de 24 linii !i 80 coloane, a
25-a linie fiind utilizat# pentru afi!area de mesaje). Numele fi!ierelor se citesc ca argumente
distincte n linia de comand#, sau se pot combina ntr-un singur argument, n acest caz fiind
desp#r"ite de semnul +.

Exerci#iul 91. Scrie"i un program care concateneaz# un set de fi!iere (text) ntr-un
singur fi!ier. Numele fi!ierelor se citesc ca argumente distincte n linia de comand#, sau se pot
combina ntr-un singur argument, n acest caz fiind desp#r"ite de semnul +. Numele fi!ierului
destina"ie este ultimul argument !i nu poate fi combinat cu numele altor fi!iere. Fi!ierul
ob"inut se va sorta n ordinea cresc#toare a liniilor, folosind metoda shellsort sau quicksort.

Exerci#iul 92. Scrie"i un program care concateneaz# un set de fi!iere (text) ntr-un
singur fi!ier. Numele fi!ierelor se citesc ca argumente distincte n linia de comand#, sau se pot
combina ntr-un singur argument, n acest caz fiind desp#r"ite de semnul +. Numele fi!ierului
destina"ie este ultimul argument !i nu poate fi combinat cu numele altor fi!iere. Fi!ierul
ob"inut se va sorta n ordinea cresc#toare a liniilor, folosind metoda shellsort sau quicksort,
eliminnd liniile duble din fi!ierul sortat. n linia de comand# putem s# avem un argument
op"ional /c sau c, !i semnific# faptul c# la sortare nu se "ine cont de diferen"a dintre literele
mari !i mici.

Exerci#iul 93. Modifica"i programul de c#utare a unui !ir de caractere din Argumentele
liniei de comand#, astfel nct c#utarea s# se fac# ntr-un set de fi!iere. Numele fi!ierelor se
citesc ca argumente distincte n linia de comand#, sau se pot combina ntr-un singur argument,
n acest caz fiind desp#r"ite de semnul +.

Exerci#iul 94. Modifica"i programul de c#utare a unui !ir de caractere din Argumentele
liniei de comand#, astfel nct liniile fi!ierului s# fie ordonate, ntr-un alt fi!ier, n func"ie de
pozi"ia de nceput a !irului de caractere n cadrul liniei. De exemplu, c#utarea tiparului este
ntr-un fi!ier cu con"inutul
In spatiu este loc
Aici nu este spatiu
Deci aici nu este loc
va crea un alt fi!ier cu con"inutul
Aici nu este spatiu
In spatiu este loc
Deci aici nu este loc

144
7.3 Func#ii diverse
Biblioteca standard a limbajului C con"ine multe func"ii. n acest paragraf vom prezenta
unele din cele mai des utilizate. De men"ionat c# toate aceste func"ii trebuie s# existe n orice
implementare a limbajului C care respect# standardul ANSI.
7.3.1 Opera#ii cu "iruri
Func"iile care lucreaz# cu !iruri snt descrise n <string.h>. O parte din aceste func"ii
le-am utilizat deja. n cele ce urmeaz# vom prezenta func"iile cele mai uzuale. O parte din
aceste func"ii ncep cu str, iar o parte cu mem. n cele ce urmeaz#, s !i t snt de tip char , iar c
!i n snt de tip int (mai precis, n este de tip size_t). Men"ion#m c# dac# copierea are loc peste
surs# (dac# s !i t au elemente comune), comportarea func"iilor este nedefinit#. Excep"ie de la
aceast# regul# o face func"ia memmove.
strcat(s,t) concateneaz# pe t la sfr!itul lui s.
strncat(s,t,n) concateneaz# n caractere din sirul t la sfr!itul !irului s.
strcmp(s,t) returneaz# o valoare negativ#, zero sau pozitiv#, dup# cum s<t,
s==t sau s>t.
strncmp(s,t,n) la fel cu strcmp, dar compar# numai primele n caractere.
strcpy(s,t) copiaz# pe t n s.
strncpy(s,t,n) copiaz# cel mult n caractere din t n s.
strlen(s) returneaz# lungimea lui s.
strchr(s,c) returneaz# un pointer la primul caracter c din s, sau NULL dac#
c nu apare n s.
strrchr(s,c) returneaz# un pointer la ultimul caracter c din s, sau NULL dac#
c nu apare n s.
Func"iile care urmeaz# snt destinate s# lucreze cu obiecte de orice tip, n particular !i cu
tablouri de caractere (nu trebuie s# fie !iruri). Din aceast# cauz#, s !i t snt de tip void , iar n
de tip size_t.
memcpy(s,t,n) copiaz# n caractere din t n s.
memmove(s,t,n) la fel cu memcpy, cu excep"ia faptului c# s !i t se pot suprapune.
memcmp(s,t,n) la fel cu strcmp, dar compar# numai primele n caractere.
memchr(s,c,n) returneaz# un pointer la primul caracter c din s, sau NULL dac#
c nu apare n s n primele n caractere.
memset(s,c,n) plaseaz# caracterul c n primele n caractere din s (umple primele
n caractere din s cu caracterul c).
7.3.2 Testarea "i conversia caracterelor
Func"iile pentru testarea !i conversia caracterelor snt descrise n <ctype.h>. O parte din
acestea le-am utilizat n programe. Folosirea acestor func"ii n programe are avantajul c# acele
programe devin independente de setul de caractere al calculatorului, deci vor fi portabile.
n cele ce urmeaz#, c este un int care poate fi reprezentat ca un unsigned char sau EOF.
Toate func"iile returneaz# un int.
isalpha(c) non-zero dac# c este alfabetic, 0 n caz contrar.

145
isupper(c) non-zero dac# c este liter# mare, 0 n caz contrar.
islower(c) non-zero dac# c este liter# mic#, 0 n caz contrar.
isdigit(c) non-zero dac# c este cifr#, 0 n caz contrar.
isalnum(c) non-zero dac# isalpha(c) sau isdigit(c), 0 n caz contrar.
isspace(c) non-zero dac# c este spa"iu, tab, newline, return, formfeed, tab
vertical, 0 n caz contrar.
toupper(c) returneaz# caracterul c convertit n liter# mare.
tolower(c) returneaz# caracterul c convertit n liter# mic#.
7.3.3 Execu#ia unei comenzi
Func"ia system(char s) execut# comanda din !irul s, dup# care reia execu"ia
programului curent. Comanda din !irul s se adreseaz# sistemului de operare pe care ruleaz#
programul !i este dependent# de acesta.
7.3.4 Alocarea memoriei
Pentru lucrul cu memoria exist# n principal trei func"ii. Dintre acestea, malloc !i calloc
aloc# o zon# de memorie n mod dinamic, iar free elibereaz# memoria alocat# cu malloc sau
calloc.
Func"ia
void malloc(size_t n);
returneaz# un pointer la n octe"i de memorie al c#ror con"inut este nedefinit, sau NULL dac#
alocarea a e!uat (de exemplu nu este memorie suficient#).
Func"ia
void calloc(size_t n,size_t size);
returneaz# un pointer la un spa"iu de memorie suficient de mare pentru un tablou de n obiecte
de m#rimea precizat# n argumentul size, sau NULL dac# alocarea a e!uat (de exemplu nu este
memorie suficient#). Con"inutul memoriei alocate va fi ini"ializat cu zero.
Pointerul returnat de una din func"iile malloc sau calloc are alinierea corespunz#toare
pentru obiectul pentru care se aloc# memorie, dar trebuie s# fie convertit n mod explicit la
tipul obiectului, ca n secven"a
int ip;
ip=(int )calloc(n,sizeof(int));
Func"ia free(p) elibereaz# spa"iul de memorie ocupat de p, unde p este un pointer
ob"inut de una din func"iile malloc sau calloc. Nu exist# restric"ii referitoare la ordinea
eliber#rii memoriei, dar constituie o eroare foarte grav# eliberarea unei zone care nu a fost
ob"inut# prin apelul lui malloc sau calloc.
De asemenea este eronat s# utiliz#m o zon# de memorie dup# ce aceasta a fost eliberat#.
Un exemplu tipic de eroare este secven"a de cod de mai jos, n care se ncearc# eliberarea
memoriei ocupat# de elementele unei liste:
for(p=head;p!=NULL;p=p>next) / GRESIT /
free(p);

146
Eliberarea corect# a memoriei se face cu o secven"# ca cea de mai jos:
for(p=head;p!=NULL;p=q)
{
q=p>next;
free(p);
}
7.3.5 Func#ii matematice
Exist# aproape 20 de func"ii matematice declarate n <math.h>. Mai jos prezent#m pe
cele mai uzuale, fiecare din acestea au un argument de tip double !i returneaz# un double.
sin(x) x este dat n radiani.
cos(x) x este dat n radiani.
atan2(y, x) arctg(y/x), x !i y snt n radiani, x0.
exp(x) e
x
.
log(x) ln x, x>0.
log10(x) lg x, x>0.
pow(x, y) x
y
.
sqrt(x) func"ia radical, x0.
fabs(x) x.
7.4 Rezumat
n acest capitol snt descrise o serie de func"ii din biblioteca standard, cum ar fi func"ii
de intrare !i ie!ire, manipularea !irurilor, alocarea memoriei, func"ii matematice, precum !i o
serie de alte func"ii utile n programele C. Aten"ia principal# este acordat# func"iilor de intrare
!i ie!ire.
Intr#rile !i ie!irile snt fundamentale n orice limbaj de programare, deoarece constituie
modul de comunicare dintre program !i mediul s#u. De!i nu fac parte din limbajul C, intr#rile
!i ie!irile snt disponibile n orice implementare a limbajului, sub form# de func"ii. n acest
capitol este prezentat modul de lucru cu aceste func"ii. Deoarece orice informa"ie n calculator
este prezent# sub form# de fi!ier, este prezentat !i modul de lucru cu fi!iere.

147
8. INTERFA!A CU SISTEMUL DE OPERARE MS DOS
Acest capitol este axat pe interfa"a dintre programele C !i sistemul de operare MS-DOS.
Capitolul este mp#r"it n trei p#r"i mari: intr#ri !i ie!iri la nivel inferior, sistemul de fi!iere !i
modele de memorie.
n Intr#ri !i ie!iri ne-am referit la interfa"a de I/O care este disponibil# pentru orice
sistem de operare unde exist# implement#ri ale limbajului C. Pentru un sistem de operare
particular, func"iile de I/O standard snt scrise astfel nct s# respecte particularit#"ile
sistemului respectiv.
8.1 Intr%ri "i ie"iri de nivel inferior
n cele ce urmeaz# vom descrie principalele func"ii de intrare !i ie!ire caracteristice
sistemului de operare MS-DOS. Marea majoritate a acestor func"ii snt preluate din sistemul
UNIX aproape identic (deci pot fi folosite !i sub UNIX cu modific#ri minime sau chiar f#r#
modific#ri).
8.1.1 Descriptori de fi"ier
n sistemul de operare MS-DOS toate intr#rile !i ie!irile snt realizate prin citirea sau
scrierea fi!ierelor, deoarece toate perifericele, chiar !i tastatura !i ecranul utilizatorului, snt
fi!iere. Aceasta nseamn# c# pentru comunica"ia dintre un program !i dispozitivele periferice
este nevoie de o singur# interfa"# omogen#.
n cazul cel mai general, nainte de citirea sau scrierea unui fi!ier, este necesar s#
inform#m sistemul de operare despre ceea ce dorim s# facem, printr-un procedeu numit
deschiderea fi!ierului. Dac# dorim s# scriem ntr-un fi!ier, poate fi necesar s# creem fi!ierul
sau s# !tergem vechiul con"inut. Sistemul de operare verific# dac# avem dreptul s# facem
opera"ia solicitat#, iar dac# r#spunsul este afirmativ returneaz# programului un num#r ntreg
pozitiv numit descriptor de fi#ier (file descriptor). Ori de cte ori se realizeaz# o opera"ie de
intrare sau de ie!ire cu un fi!ier, n locul numelui este utilizat descriptorul de fi!ier pentru a
identifica fi!ierul. (Un descriptor de fi!ier este analog cu un pointer de fi!ier utilizat de
biblioteca standard) Toate informa"iile referitoare la un fi!ier deschis snt ntre"inute de
sistemul de operare, iar programul utilizatorului se refer# la fi!ier numai prin descriptorul de
fi!ier.
Deoarece intr#rile !i ie!irile care utilizeaz# tastatura !i ecranul snt foarte frecvente, au
fost f#cute unele conven"ii pentru ca acestea s# poat# fi utilizate u!or. Cnd interpretorul de
comenzi (shell ) execut# un program, snt deschise automat trei fi!iere, cu descriptorii 0, 1 !i
2, numite intrarea standard, ie#irea standard !i ie#irea standard pentru erori (standard
error). Dac# programul utilizeaz# unul din aceste trei fi!iere, nu mai trebuie s# l deschid#.

148
Utilizatorul poate redirecta opera"iile de I/O spre !i dinspre fi!iere folosind conven"iile <
!i > (vezi Intrarea !i ie!irea standard). n acest caz interpretorul de comenzi schimb# asign#rile
implicite pentru descriptorii 0 !i 1 c#tre fi!ierele specificate. n mod normal descriptorul 2
r#mne ata!at la ecran, astfel nct mesajele de eroare apar pe ecran.
8.1.2 Intr%ri "i ie"iri
Cel mai sc#zut nivel pentru opera"ii de I/O nu furnizeaz# nici bufferizare !i nici alte
servicii. Aceasta nseamn# c# zona de memorie n care are loc citirea sau din care se face
scrierea trebuie rezervat# !i ntre"inut# de programator. Acest nivel sc#zut este de fapt o
intrare direct# n sistemul de operare. Intr#rile !i ie!irile snt date de dou# func"ii numite read
!i write. Pentru ambele func"ii primul argument este un descriptor de fi!ier. Al doilea
argument este un tablou de caractere din program unde snt depuse datele citite din fi!ier, sau
de unde vor fi luate datele pentru a fi scrise n fi!ier, iar al treilea argument este num#rul de
octe"i care se transfer#.
int n_read=read(int fd,char buf,int n);
int n_written=write(int fd,char buf,int n);
Fiecare din cele dou# func"ii returneaz# num#rul de octe"i transferat. La citire num#rul
de octe"i poate fi mai mic dect num#rul solicitat. Dac# este returnat# valoarea zero nseamn#
c# a fost ntlnit sfr!itul de fi!ier, iar dac# a fost returnat# valoarea 1 nseamn# c# a ap#rut o
eroare. La scriere valoarea returnat# este num#rul de octe"i scri!i. Dac# acest num#r este diferit
de num#rul de octe"i care se transfer# (dat de cel de-al treilea argument), a ap#rut o eroare (de
exemplu discul este plin).
Num#rul de octe"i care snt scri!i sau citi"i este arbitrar, cele mai utilizate valori fiind
numere ca 512, 1024, 2048, 4096 etc., care corespund m#rimii fizice a unui bloc de pe disc.
Dac# snt utilizate valori mai mari, programul va fi mai rapid, deoarece accesele la
dispozitivul periferic vor fi mai rare.
Folosind aceste dou# func"ii, putem scrie un program simplu care s# copieze intrarea n
ie!ire, analog cu programul de copiere al fi!ierelor scris n Ini"iere n C. Dac# utiliz#m
redirectarea c#tre un fi!ier sau alt dispozitiv, programul va copia orice n orice.
#define BUFSIZE 4096
main()
{
char buf[BUFSIZE];
int n;
while((n=read(0,buf,sizeof buf))>0)
write($,buf,n);
return 0;
}
Dac# m#rimea fi!ierului nu este multiplu de BUFSIZE, read va returna un num#r de
octe"i mai mic dect BUFSIZE, iar urm#torul apel la read va returna valoarea zero.
Func"iile read !i write pot fi utilizate pentru a scrie rutine de nivel nalt ca getchar,
putchar, etc. De exemplu, func"ia getchar poate fi scris# ca mai jos:

149
int getchar(void)
{
char c;
return (read(0,&c,$)==$) ? (unsigned char) c : EOF;
}
Variabila c trebuie s# fie de tip char, deoarece func"ia read necesit# un pointer la
caracter. Am for"at ca valoarea returnat# s# fie de tip unsigned char pentru a evita eventualele
probleme de extensie a semnului.
Aceast# versiune a lui getchar cite!te un caracter o dat#, deci accesul la dispozitiv este
des !i viteza programului scade. Pentru a cre!te viteza programului, putem scrie o a doua
versiune a lui getchar, care cite!te mai multe caractere o dat# !i returneaz# cte unul.
#define BUFSIZE 4096
int getchar(void)
{
static char buf[BUFSIZE];
static char bufp=buf;
static int n=0;
if(n==0)
{
n=read(0,buf,sizeof buf);
bufp=buf;
}
return (--n>=0) ? (unsigned char) bufp++ : EOF;
}
Dac# dorim s# compil#m aceast# versiune a lui getchar !i s# includem fi!ierul
<stdio.h>, atunci trebuie s# folosim o directiv# #undef pentru getchar n cazul n care aceasta
este implementat# ca un macro.
8.1.3 Fi"iere
Dac# avem nevoie de alte fi!iere dect cele standard, pentru a putea lucra cu ele trebuie
s# le deschidem explicit. Pentru deschiderea fi!ierelor exist# dou# func"ii, open !i creat.
open este asem#n#toare cu fopen, cu excep"ia faptului c# returneaz# un descriptor de
fi!ier, care este un int. n cazul apari"iei unei erori, open returneaz# valoarea 1.
#include <fcntl.h>
#include <sys\stat.h>
int fd;
int open(const char name,int flags,int pmode);
fd=open(name,flags,pmode);
La fel ca la fopen, argumentul name este un !ir de caractere care con"ine numele
fi!ierului. Al doilea argument specific# modul n care este deschis fi!ierul. Modurile posibile
de deschidere snt date n <fcntl.h> sub forma unor constante. Dac# dorim s# specific#m mai
multe constante, vom utiliza operatorul |. Principalele constante care pot fi utilizate snt:
O_RDONLY deschide fi!ierul numai pentru citire

150
O_WRONLY deschide fi!ierul numai pentru scriere
O_RDWR deschide fi!ierul pentru citire !i scriere
O_CREAT creaz# fi!ierul
O_EXCL deschide fi!ierul n mod exclusiv. Se folose!te numai cu O_CREAT !i
func"ia returneaz# o eroare dac# fi!ierul specificat exist#
O_TRUNC deschide fi!ierul !i l trunchiaz# (vechiul con"inut se pierde)
Nu se poate folosi o combina"ie a constantelor O_RDONLY, O_WRONLY !i
O_RDWR, ci doar una din ele. Nu exist# o valoare implicit# pentru modul de acces.
Argumentul pmode este necesar numai cnd specific#m O_CREAT. Dac# fi!ierul exist#
argumentul este ignorat, iar n caz contrar specific# modul de acces permis la fi!ier dup# ce
acesta este nchis. Modul de acces este dat sub forma unor constante definite n <sys\stat.h>.
Dac# nu este permis# scrierea n fi!ier, dup# nchidere fi!ierul va fi marcat read-only.
Constituie o eroare ncercarea de deschidere pentru citire a unui fi!ier inexistent. Func"ia
creat este utilizat# pentru a crea fi!iere noi sau pentru a suprascrie unul existent. Ea returneaz#
un descriptor de fi!ier dac# fi!ierul poate fi creat, sau 1 n caz contrar. Argumentul pmode
are acela!i efect ca la open.
int creat(const char name,int pmode);
fd=creat(name,pmode);
Pentru a ilustra utilizarea acestor func"ii, vom scrie un program copyf care copiaz# un
fi!ier ntr-un alt fi!ier.
#include <stdio.h>
#include <fcntl.h>
#include <sys\stat.h>
#define BUFSIZE 4096
#define PMODE S_IREAD|S_IWRITE
void error(const char ,...);
main(int argc,char argv[])
{
char buf[BUFSIZE];
int n,f$,f2;
if(argc!=3)
error(Utilizare: copyf sursa dest);
if((f$=open(argv[$],O_RDONLY))==-$)
error(copyf: nu pot deschide %s,argv[$]);
if((f2=creat(argv[2],PMODE))==-$)
error(copyf: nu pot crea %s in modul de acces %#x,
argv[2],PMODE);
while((n=read(f$,buf,BUFSIZE))>0)
if(write(f2,buf,n)!=n)
error(copyf: eroare la scrierea in %s,argv[2]);
return 0;
}
Func"ia error este apelat# cu un num#r variabil de argumente ntr-un mod asem#n#tor cu
printf. Func"ia standard vprintf este asem#n#toare cu printf, cu excep"ia faptului c# lista de
argumente variabil# este nlocuit# cu un singur argument care este ini"ializat prin apelul
macroului va_start. n mod asem#n#tor, func"ia vfprintf corespunde func"iei fprintf, iar
func"ia vfsprintf corespunde func"iei sprintf.

151
#include <stdio.h>
#include <stdarg.h>
void error(const char fmt,...)
{
va_list args;
va_start(args,fmt);
fprintf(stderr,eroare: );
vfprintf(stderr,fmt,args);
fprintf(stderr,\n);
va_end(args);
exit($);
}
Num#rul de fi!iere care poate fi deschis simultan este limitat. De aceea, orice program
care utilizeaz# multe fi!iere trebuie s# le nchid# pe acelea de care nu mai este nevoie,
descriptorii de fi!ier elibera"i putnd fi utiliza"i la altceva. Func"ia
int close(int fd);
ntrerupe leg#tura dintre un descriptor de fi!ier !i un fi!ier deschis !i elibereaz# descriptorul de
fi!ier, iar acesta poate fi utilizat pentru alt fi!ier. close este analoag# cu fclose, cu excep"ia
faptului c# nu mai snt buffere care trebuie golite. Dac# un program se termin# prin apelul
func"iei exit sau cu o instruc"iune return din func"ia main, toate fi!ierele deschise snt nchise
nainte de terminarea programului.
Func"ia
int unlink(const char name);
!terge fi!ierul specificat !i corespunde func"iei standard remove. Dac# fi!ierul nu poate fi
!ters, unlink returneaz# -1.
Exerci#iul 95. Rescrie"i programele de concatenare a fi!ierelor din Lucrul cu fi!iere,
utiliznd func"iile read, write, open !i close n locul celor echivalente din biblioteca standard.
8.1.4 Accesul aleator la un fi"ier
Opera"iile de I/O snt secven"iale, adic# un read sau write are loc la pozi"ia din fi!ier
unde s-a terminat opera"ia anterioar#. Exist# situa"ii n care este necesar ca un fi!ier s# poat# fi
citit sau scris n mod aleator. Acest lucru se face prin pozi"ionarea n locul dorit din fi!ier
naintea unei opera"ii de citire sau scriere. Pozi"ionarea se face cu ajutorul func"iei lseek, care
are prototipul:
long lseek(int fd,long offset,int origin);
Func"ia lseek seteaz# pozi"ia curent# din fi!ierul cu descriptorul fd la valoarea offset,
valoare care este relativ# la loca"ia specificat# de origin. Valoarea returnat# de lseek este noua
pozi"ie din fi!ier, sau -1 dac# a ap#rut o eroare. Urm#toarea opera"ie de citire sau scriere va
avea loc n acea pozi"ie. Parametrul origin poate avea valorile 0, 1 sau 2 pentru a specifica
faptul c# offset se m#soar# de la nceputul fi!ierului, de la pozi"ia curent# sau de la sfr!itul
fi!ierului.
De exemplu, pentru a ad#uga date la sfr!itul unui fi!ier, naintea scrierii n fi!ier este
necesar# o pozi"ionare pe sfr!itul fi!ierului:

152
lseek(fd,0L,2);
Dac# dorim s# determin#m pozi"ia curent# din fi!ier, folosim o instruc"iune de tipul:
pos=lseek(fd,0L,$);
De remarcat faptul c# al doilea argument este de tip long int, deci scriem 0L.
Cu ajutorul lui lseek putem considera un fi!ier ca fiind un tablou de dimensiuni mari
memorat pe un suport extern. De exemplu, func"ia de mai jos cite!te un num#r oarecare de
octe"i dintr-un loc arbitrar dintr-un fi!ier:
int get(int fd,long pos,char buf,int n)
{
if(lseek(fd,pos,0)==$)
return $;
else
return read(fd,buf,n);
}
Dac# avem nevoie de pozi"ia curent# din fi!ier putem folosi func"ia tell, care are
prototipul:
long tell(int fd);
Func"iile lseek !i tell au drept corespondent n biblioteca standard func"iile fseek !i
respectiv ftell, care au ca prim parametru un pointer de tipul FILE n loc de un descriptor de
fi!ier !i returneaz# o valoare nenul# n cazul apari"iei unei erori.
8.2 Sistemul de fi"iere
O alt# situa"ie n care snt utilizate fi!iere este aceea n care dorim s# !tim anumite
informa"ii despre un fi!ier !i nu despre ce con"ine el (dac# fi!ierul exist#, ce atribute are, ce
spa"iu ocup# pe disc etc.).
Una din principalele caracteristici ale sistemului de operare MS-DOS (care o putem
considera totodat# !i un neajuns) o constituie numele unui fi!ier. Numele unui fi!ier este
format din 1-8 caractere, iar pentru a deosebi ntre ele dou# fi!iere cu acela!i nume se folosesc
0-3 caractere pentru extensie.
Un director n MS-DOS este un fi!ier care con"ine numele unor fi!iere sau directori,
precum !i informa"ii despre locul unde se g#sesc acestea pe disc. Pentru a ob"ine numele
fi!ierelor dintr-un director snt folosite dou# func"ii, findfirst !i findnext. Numele acestor
func"ii este specific implement#rii Borland. n alte implement#ri ale limbajului C numele
func"iilor poate s# difere, dar efectul lor este acela!i.
Pentru a ilustra utilizarea acestor func"ii, vom da mai jos un program care !terge toate
fi!ierele de care nu mai este nevoie de pe unitatea curent# de disc (astfel de fi!iere snt
fi!ierele temporare create de diverse programe care nu se termin# normal, fi!ierele backup
create de diverse editoare de texte sau programe etc.). Programul poate fi mbun#t#"it astfel
nct s# fie !terse fi!ierele de care nu mai este nevoie !i de pe o alt# unitate de disc. Dup# cum
se poate observa, programul este recursiv, iar fi!ierele temporare snt !terse, indiferent de
atributele pe care le au. De men"ionat c# diverse programe pot crea fi!iere temporare care s#
aib# nume care nu snt incluse n acest exemplu.
#include <io.h>

153
#include <dir.h>
#include <dos.h>
char path[MAXDIR];
void sterg() / sterg fisierele temporare /
{
struct ffblk ffblk;
int done;
done=findfirst(.bak,&ffblk, FA_ARCH|FA_RDONLY|FA_SYSTEM|FA_HIDDEN);
while(!done)
{
_chmod(ffblk.ff_name,$,FA_ARCH);
unlink(ffblk.ff_name);
done=findnext(&ffblk);
}
done=findfirst(.$,&ffblk, FA_ARCH|FA_RDONLY|FA_SYSTEM|FA_HIDDEN);
while(!done)
{
_chmod(ffblk.ff_name,$,FA_ARCH);
unlink(ffblk.ff_name);
done=findnext(&ffblk);
}
done=findfirst(~.,&ffblk, FA_ARCH|FA_RDONLY|FA_SYSTEM|FA_HIDDEN);
while(!done)
{
_chmod(ffblk.ff_name,$,FA_ARCH);
unlink(ffblk.ff_name);
done=findnext(&ffblk);
}
done=findfirst($.,&ffblk, FA_ARCH|FA_RDONLY|FA_SYSTEM|FA_HIDDEN);
while(!done)
{
_chmod(ffblk.ff_name,$,FA_ARCH);
unlink(ffblk.ff_name);
done=findnext(&ffblk);
}
done=findfirst(.tmp,&ffblk, FA_ARCH|FA_RDONLY|FA_SYSTEM|FA_HIDDEN);
while(!done)
{
_chmod(ffblk.ff_name,$,FA_ARCH);
unlink(ffblk.ff_name);
done=findnext(&ffblk);
}
}
/
Sterg fisierele temporare din directorul curent,
dupa care caut fisiere temporare si in subdirectori
/
void changedir() / schimb directorul /
{
struct ffblk ffblk;
int done;
/
Sterg fisierele temporare din directorul curent
/
sterg();

154
/
Caut primul director
/
done=findfirst(.,&ffblk,FA_DIREC);
while(!done)
{
if(ffblk.ff_name!='.') / Este director propriu ? /
if(chdir(ffblk.ff_name)!=-$)
{
/
Daca subdirectorul exista, sterg prima data
fisierele din acel subdirector,
/
changedir();
/
dupa care revin in directorul parinte.
/
chdir(..);
}
/
Caut urmatorul director
/
done=findnext(&ffblk);
}
}
main(void)
{
/
Citesc directorul curent,
/
getcwd(path,MAXPATH);
/
ma mut in directorul radacina,
/
chdir(\\);
/
sterg de acolo toate fisierele tomporare,
/
changedir();
/
dupa care revin in directorul in care am fost
la lansarea programului in executie.
/
chdir(path);
return 0;
}
Exerci#iul 96. Scrie"i un program care !terge un fi!ier dintr-un director. Numele
fi!ierului este dat ca argument n linia de comand# !i poate con"ine caracterele !i ?. n linia
de comand# mai putem avea un argument op"ional /d sau -d, care semnific# faptul c# fi!ierul
va fi !ters !i din to"i subdirectorii acelui director. Nu se va folosi func"ia system !i nici alte
func"ii echivalente (exec, spawn, etc.).


155
Exerci#iul 97. Scrie"i un program care !terge un director, indiferent de con"inutul
acestuia. Numele directorului este dat ca argument n linia de comand#. Nu se va folosi func"ia
system !i nici alte func"ii echivalente (exec, spawn, etc.).

Exerci#iul 98. Scrie"i un program care mparte un fi!ier oarecare n una sau mai multe
p#r"i de m#rime identic# (opera"ie numit# split), ultima parte putnd fi mai mic#. Numele
fi!ierului se cite!te ca argument n linia de comand#, iar m#rimea unei p#r"i se cite!te. P#r"ile
vor avea un nume comun, iar extensia va fi .001, .002, etc.

Exerci#iul 99. Scrie"i un program care reface un fi!ier oarecare mp#r"it n una sau mai
multe p#r"i conform exerci"iului anterior (opera"ie numit# merge). Numele comun al p#r"ilor !i
numele fi!ierului se citesc ca argumente n linia de comand#, iar num#rul de p#r"i se va
determina n func"ie de extensia p#r"ilor. De exemplu, dac# numele comun este fis, atunci se
vor uni toate fi!ierele cu acest nume !i cu extensia .001, .002 etc., ntr-un singur fi!ier.
8.3 Modele de memorie
Fiecare program C are dou# p#r"i: o parte de cod format# din instruc"iuni !i o parte de
date care cuprinde variabilele !i constantele din program. Cnd programul este executat, aceste
dou# p#r"i snt nc#rcate n memorie !i se caracterizeaz# printr-o adres#. Modelele de memorie
determin# m#rimea maxim# pentru partea de cod !i pentru partea de date a unui program C.
Pentru a studia !i a n"elege ce model de memorie este necesar pentru un anumit
program, vom explica pe scurt arhitectura microprocesoarelor din familia Intel

80x86 !i cum
se determin# o adres# de memorie.
8.3.1 Arhitectura microprocesoarelor Intel

80x86
Calculatoarele compatibile IBM PC utilizeaz# microprocesoare Intel

(sau compatibile)
8086, 80286, 80386, 80486, Pentium, Pentium Pro, Pentium II !i Pentium III, denumite pe
scurt 80x86. Aceste microprocesoare snt compatibile de jos n sus (adic# un program care
ruleaz# de exemplu pe un microprocesor 8086 poate fi rulat f#r# modific#ri pe oricare din
celelalte microprocesoare) !i utilizeaz# regi!tri pentru a accesa datele din memorie.
Microprocesoarele 8086 !i 80286 snt pe 16 bi"i, adic# regi!trii lor au 16 bi"i. Pe 16 bi"i
cel mai mare num#r care se poate reprezenta este 65535, ceea ce nseamn# adresarea a 64K de
memorie. Microprocesorul 8086 poate adresa 2
20
loca"ii de memorie, deci pentru accesarea
memoriei snt utilizate alte tehnici.
Microprocesoarele 80x86 au o arhitectur# segmentat!, adic# trateaz# memoria ca pe o
serie de segmente. Un segment este o zon# contigu! de memorie de 65536 octe"i (64K), care
ncepe la o adres# multiplu de 16. Pentru a accesa informa"iile din interiorul unui segment se
folose!te un num#r pe 16 bi"i numit offset. Pentru adresarea memoriei snt utiliza"i doi regi!tri.
Un registru con"ine offsetul, iar cel#lalt registru con"ine adresa unui segment mp#r"it# la 16.
Astfel, pentru a determina adresa unei loca"ii de memorie, se nmul"e!te registrul segment cu
16 !i se adun# offsetul.

156
Instruc"iunile care utilizeaz# adrese de segment !i offseturi snt mai lente dect cele care
utilizeaz# numai offseturi din cadrul unui singur segment. Viteza unui program poate fi m#rit#
dac# toate accesele la memorie au loc ntr-un acela!i segment. n acest caz registrul segment
nu se modific#. Din aceast# cauz#, adresele care snt formate numai din offset se numesc
adrese near sau pointeri near, iar adresele care snt formate dintr-o adres# de segment !i
dintr-un offset se numesc adrese far sau pointeri far.
Microprocesoarele 80x86 utilizeaz# patru regi!tri de segment. Registrul CS (Code
Segment) pointeaz# c#tre segmentul care con"ine codul executabil. Registrul DS (Data
Segment) pointeaz# c#tre segmentul care con"ine datele utilizate de program (variabile globale
!i statice). Registrul SS (Stack Segment) pointeaz# c#tre segmentul care p#streaz# obiecte
temporare (variabile locale, informa"ii necesare pentru realizarea apelurilor de func"ii, etc.).
Registrul ES (Extended Segment) este furnizat ca !i un registru suplimentar !i nu este utilizat
de obicei de limbajul C (de!i poate fi utilizat de anumite func"ii de bibliotec#).
Orice obiect (dat#) C poate fi accesat n dou# moduri distincte: near sau far. Dac# este
accesat ca obiect near, adresa sa se determin# lund offset-ul !i adunnd la acesta segmentul de
date implicit. Toate obiectele near trebuie s# ncap# ntr-un singur segment, astfel nct pentru
obiectele near sntem limita"i la 64K. Dac# un obiect este accesat ca obiect far, adresa sa este
determinat# lund valoarea unui registru segment, plasnd-o n registrul segment de date !i
adunnd un offset.
Pentru apelurile de func"ii se aplic# acelea!i reguli ca !i pentru obiecte. Adresa unei
func"ii near este offset-ul s#u adunat la segmentul de cod implicit. Adresa unei func"ii far este
offset-ul s#u adunat la valoarea segmentului de cod propriu.
Pointerii far au avantajul c# pot accesa orice loca"ie de memorie !i dezavantajul c#
ocup# mai mult# memorie !i programele snt mai lente.
8.3.2 Modele de memorie
Limbajul C admite !ase modele de memorie standard: tiny, small, medium, compact,
large !i huge. Modelul de memorie implicit utilizat pentru compilarea programelor este small,
dar el poate fi schimbat.
8.3.2.1 Modelul small
Cnd compil#m un program utiliznd modelul small, compilatorul C creaz# toate
obiectele near. Segmentele de stiv# !i de date pointeaz# automat la aceea!i adres#. Spa"iul
total ocupat de func"ii nu trebuie s# dep#!easc# 64K. Spa"iul total ocupat de variabilele
globale, de stiv# pentru variabilele locale !i cel utilizat prin folosirea lui malloc trebuie de
asemenea s# nu dep#!easc# 64K. Astfel, un program n modelul small poate avea cel mult
128K, din care cel mult 64K pentru cod !i cel mult 64K pentru date. Deoarece se utilizeaz#
numai offset-ul, to"i pointerii snt near, deci ocup# doar doi octe"i n memorie.
8.3.2.2 Modelul large
Programele din modelul large utilizeaz# pointeri far pentru cod !i date. Regi!trii SS !i
DS nu trebuie s# fie egali. n acest model codul nu mai este limitat la 64K, putnd avea
teoretic orice dimensiune. Stiva poate avea pn# la 64K, iar spa"iul ocupat de date este de
asemenea teoretic nelimitat. Spa"iul de memorie utilizat prin apelul lui malloc este limitat la
memoria r#mas# liber#. Singura restric"ie este aceea c# nu putem avea o variabil# care s#
dep#!easc# 64K. Deoarece se utilizeaz# !i segmentul !i offset-ul, to"i pointerii snt far !i ocup#
patru octe"i n memorie.

157
8.3.2.3 Modelele medium "i compact
Cele mai multe programe utilizeaz# unul din cele dou# modele de memorie prezentate
mai sus. Programele care utilizeaz# modelul small snt mai mici !i mai rapide, dar snt limitate
n ceea ce prive!te memoria pe care o pot utiliza. Programele care utilizeaz# modelul large pot
folosi orict# memorie, dar snt mai mari !i mai lente.
Exist# situa"ii n care codul nu ncape n 64K !i datele ncap n 64K (programe mari care
prelucreaz# date pu"ine, ca de exemplu programe !tiin"ifice), sau pentru cod este suficient 64K
!i pentru date avem nevoie de mai mult de 64K (programe mici care prelucreaz# date multe,
ca de exemplu programe economice). n astfel de situa"ii modelul small nu este bun, iar
modelul large este prea mare. De aceea snt puse la dispozi"ie dou# modele hibrid care
respect# cerin"ele descrise mai sus, medium !i compact.
Modelul medium este util pentru programele mari care folosesc date pu"ine. Acest
model este identic cu modelul small, exceptnd faptul c# snt utiliza"i pointeri far pentru toate
apelurile de func"ii.
Modelul compact este util pentru programele mici care utilizeaz# date multe. Acest
model este identic cu modelul large, exceptnd faptul c# snt utiliza"i pointeri near pentru cod.
Un program compilat n unul din modelele medium sau compact ocup# mai pu"in spa"iu
!i este mai rapid dect acela!i program compilat n modelul large.
8.3.2.4 Modelele tiny "i huge
n afara celor patru modele descrise mai sus, snt disponibile nc# dou# modele speciale.
Modelul tiny este similar cu modelul small, cu excep"ia faptului c# regi!trii DS !i CS
snt ntotdeauna egali. Aceasta nseamn# c# spa"iul total ocupat de cod !i date nu poate dep#!i
64K.
Modelul huge este identic cu modelul large, cu excep"ia faptului c# datele individuale
pot dep#!i 64K. Aceasta nseamn# c# putem avea tablouri a c#ror dimensiune s# dep#!easc#
64K.
8.3.2.5 Reguli n utilizarea modelelor de memorie
n oricare din aceste modele codul !i datele ob"inute prin compilarea unui singur fi!ier
surs# nu pot dep#!i 64K. Programele mari trebuie mp#r"ite n mai multe fi!iere surs# !i
compilate separat.
Este posibil s# declar#m explicit orice func"ie sau variabil# ca near sau far. Aceasta face
ca acea func"ie sau variabil# s# fie tratat# conform declara"iei, indiferent de modelul de
memorie utilizat.
Not#m c# dac# declar#m o func"ie ca !i near ntr-un program care utilizeaz# pointeri far
pentru cod, aceast# func"ie va fi accesibil# numai din fi!ierul n care se afl#. De asemenea
cuvntul static are acela!i efect !i cauzeaz# utilizarea pointerilor near n toate modelele de
memorie.
Chiar !i n programele din modelul large o singur# dat# nu poate ocupa mai mult de un
segment. Aceasta este o problem# n special pentru tablourile foarte mari, deoarece n
calculele cu un pointer far este utilizat numai offsetul, segmentul fiind l#sat ntotdeauna
neschimbat. Un tablou se caracterizeaz# printr-un segment !i un offset. Deoarece offsetul nu
poate dep#!i 64K, nu exist# nici o posibilitate de a accesa un element de tablou care se g#se!te
la mai mult de 64K de nceputul tabloului. Din aceast# cauz#, datele declarate ca far trebuie s#
fie n interiorul segmentului n care ncep, dar pot fi oriunde n memorie.
Un pointer huge este un pointer special care combin# offsetul !i segmentul ntr-un
singur num#r, care este utilizat ca adres#. n acest mod putem s# creem date mai mari de un
segment. Pointerii huge pot fi aplica"i numai datelor (variabilelor), nu !i func"iilor (de ce?).

158
Cu toate c# n modelul huge un tablou poate dep#!i 64K, exist# unele restric"ii care se
aplic#, restric"ii relative la dimensiune:
Un singur element de tablou nu poate dep#!i 64K, de!i tabloul poate fi mai mare
de 64K.
Pentru un tablou mai mare de 128K toate elementele trebuie s# aib# o dimensiune
care s# fie putere a lui 2. Dac# tabloul este mai mic de 128K, elementele sale pot
avea orice dimensiune pn# la 64K inclusiv.
Un tablou huge nu poate fi local, ci doar static sau global.
Ca o regul# final#, men"ion#m c# toate fi!ierele surs# care alc#tuiesc un program trebuie
s# fie compilate cu acela#i model de memorie. Dac# dorim s# schimb#m modelul de memorie
pentru un program, trebuie s# recompil#m toate fi!ierele care l alc#tuiesc. De men"ionat c#
m#rimea implicit# pentru pointeri este determinat# de modelul de memorie utilizat pentru
compilarea programului.
8.3.3 Cuvinte speciale folosite n declara#ii
Pentru a putea folosi obiecte near, far sau huge diferite de modelul de memorie folosit
pentru program, trebuie s# utiliz#m pentru declararea acestor obiecte cuvintele speciale near,
far sau huge. Dac# un cuvnt special apare ntr-o declara"ie, el modific# data care urmeaz#
imediat n dreapta acestui cuvnt. n aceea!i declara"ie putem avea mai multe cuvinte speciale.
Cuvintele near !i far pot fi aplicate variabilelor !i func"iilor, n timp ce huge nu poate fi
aplicat func"iilor !i tablourilor automate.
Pentru a vedea cum pot fi utilizate cuvintele speciale ntr-o declara"ie, s# consider#m
declara"ia urm#toare:
char far (far (near var)())[$0];
^ ^ ^ ^ ^ ^ ^
7 6 4 2 1 3 5
n acest exemplu pa!ii snt numerota"i n ordinea n care snt interpreta"i !i,
parcurgndu-i, ob"inem:
1. Identificatorul var este
2. pointer near la
3. o func"ie (f#r# parametri) care returneaz#
4. pointer far la
5. tablou de 10 elemente care snt
6. pointeri far
7. char
Exerci#iul 100. Modifica"i programul dcl din Declara"ii complexe astfel nct s# poat# fi
analizat# o declara"ie n care intervin !i cuvintele speciale !i s# fie analizate !i argumentele
func"iilor, calificatori ca !i const, etc.

159
8.4 Accesarea direct% a memoriei
%tim c# fiecare variabil# dintr-un program C are asociat# o loca"ie de memorie unic#, iar
aceast# loca"ie poate fi accesat# prin nume. Zonele de memorie cele mai importante nu au un
nume. Aceste zone includ loca"ii de memorie speciale utilizate de sistemul de operare, de alte
programe, sau chiar de periferice cum ar fi ecranul. Pentru a putea accesa astfel de loca"ii,
trebuie s# memor#m adresele zonelor pe care dorim s# le acces#m ntr-o variabil# pointer !i
apoi s# utiliz#m acel pointer. S# consider#m urm#torul exemplu, n care dorim s# examin#m
con"inutul unei loca"ii de memorie:
#include <stdio.h>
main(void)
{
int look;
look=(int )$0;
printf(Locatia $0 contine %d\n,look);
return 0;
}
Acest lucru pare destul de simplu de realizat, dar n realitate nu este chiar a!a. O
ntrebare important# care se pune este ce model de memorie am utilizat. S# consider#m cazul
modelelor de memorie small !i large.
Dac# programul este compilat n modelul small, to"i pointerii constau numai din offset.
Offset-ul pointeaz# ntotdeauna la o loca"ie din segmentul de date (cu excep"ia celor care
pointeaz# la func"ii). n modelul small programul de mai sus va examina numai con"inutul
loca"iei 10 din segmentul de date.
Dac# programul este compilat n modelul large, to"i pointerii constau din offset !i
segment. Un astfel de pointer poate accesa orice loca"ie din memorie. n modelul large
programul de mai sus va examina con"inutul loca"iei 10 din memorie.
8.4.1 Pointeri far "i near
Nu este bine ca un program s# se comporte diferit n func"ie de modelul de memorie
utilizat. Dac# dorim s# examin#m loca"ia 10 din memorie, indiferent de modelul de memorie
utilizat, programul va ar#ta astfel:
#include <stdio.h>
main(void)
{
int look;
look=(int far )$0;
printf(Locatia $0 din memorie contine %d\n,look);
return 0;
}
Dac# dorim s# examin#m con"inutul loca"iei 10 din segmentul de date, indiferent de
modelul de memorie utilizat, programul va ar#ta astfel:

160
#include <stdio.h>
main(void)
{
int look;
look=(int near )$0;
printf(Locatia $0 din segmentul de date contine %d\n,look);
return 0;
}
n realitate pointerii nu con"in adrese fizice de memorie. n cazul pointerilor near,
pointerul con"ine un offset n segmentul implicit, deci este clar c# nu con"ine o adres#.
Pointerii far snt mai complica"i.
Un pointer far este ntotdeauna o pereche segment/offset. Este important s# ne amintim
acest lucru ori de cte ori utiliz#m aritmetica pointerilor. S# consider#m c# dorim s# acces#m
loca"ia de la adresa hexazecimal# 0x12000. Cnd asign#m aceast# valoare unui pointer, ne
gndim c# ea reprezint# loca"ia de memorie cu num#rul 12000 n hexazecimal. Acest lucru
ns# nu este adev#rat. Pentru a ne convinge c# a!a este, s# vedem ce se ntmpl# cnd aceast#
valoare este asignat# unui pointer far. Num#rul 12000 n hexazecimal este prea mare pentru a
ncape pe doi octe"i. De aceea la offset snt memorate ultimele patru cifre (0x2000), iar ceea
ce r#mne (0x1) este considerat ca adres# de segment. Deci pointerul nostru va con"ine
num#rul 0x1 n por"iunea de segment !i num#rul 0x2000 n por"iunea de offset. %tim c# o
adres# se ob"ine nmul"ind segmentul cu 0x10 (16 n zecimal) !i adunnd offsetul. Rezult# c#
adresa care se g#se!te n pointerul nostru este
0x$0x$0+0x2000=0x20$0,
deci pointerul pointeaz# la adresa 0x2010 !i nu la adresa 0x12000, a!a cum am dorit.
Trebuie s# avem mare grij# cnd asign#m constante unui pointer, pentru a ob"ine adresa
dorit# !i nu alta. Din fericire cele mai interesante !i mai importante loca"ii de memorie se
g#sesc n primii 64K de memorie, deci segmentul va fi zero, iar adresa va fi dat# numai de
offset.
8.4.2 Utilizarea adres%rii directe a memoriei
Adresarea direct# a memoriei se utilizeaz# n special pentru dou# motive: accelerarea
ie!irilor pe ecran !i accesarea zonelor speciale din MS-DOS. n continuare vom prezenta
ie!irile pe ecran.
Ecranul poate fi gndit ca !i un tablou de caractere de dimensiune 2580, fiecare
element fiind reprezentat n memorie pe doi octe"i. Primul octet con"ine caracterul de pe
ecran, iar al doilea octet con"ine atributele acelui caracter. Atributele unui caracter snt clipirea
intermitent#, culoarea de fond, luminozitatea !i culoarea de scris. Despre atribute vom vorbi
mai n detaliu pu"in mai ncolo.
Acest tablou poate fi declarat astfel:
char screen[25][80][2];
Primul index reprezint# coordonata y a ecranului (linia), iar al doilea index reprezint#
coordonata x (coloana). Ultimul index memoreaz# mai nti caracterul !i apoi atributele acelui
caracter. Not#m c# indec!ii trebuie declara"i n aceast# ordine pentru a coincide cu
coordonatele ecranului din memorie.
Adresa de memorie la care ncepe ecranul depinde de tipul echipamentului utilizat. n
mod text, memoria ecran poate fi la adresa B800:0000 (adresa fizic# B8000) pentru ecranele

161
color, sau la adresa B000:0000 (adresa fizic# B0000) pentru ecranele monocrom. Pentru a crea
o variabil# care s# trateze aceast# zon# de memorie ca !i un tablou, trebuie s# declar#m un
pointer far la tabloul descris mai sus, care s# con"in# adresa dorit#. Programul urm#tor
afi!eaz# pe un ecran color n linia 10 !i coloana 10 caracterul a.
main(void)
{
typedef char screen_t[25][80][2];
screen_t far screen;
screen=(screen_t far )0xB8000000;
(screen)[9][9][0]=a;
return 0;
}
Pointerul screen l-am declarat far deoarece ecranul utilizeaz# un segment propriu.
Not#m c# valoarea care ne d# adresa fizic# 0xB8000 este 0xB8000000.
Octetul de atribute prezint# o importan"# aparte. El are patru sec"iuni diferite:
Blinking
(clipire)
Background
(fond)
Intensity
(Bright)
Foreground
(scris)
Bit 7 Bi"ii
6 5 4
R G B
Bit 3 Bi"ii
2 1 0
R G B
Nota"iile R, G, B snt prescurt#ri de la Red, Green !i respectiv Blue. Culorile care pot fi
ob"inute snt:
Culoarea Zecimal Binar
R G B
Black 0 0 0 0
Blue 1 0 0 1
Green 2 0 1 0
Cyan 3 0 1 1
Red 4 1 0 0
Magenta 5 1 0 1
Yellow 6 1 1 0
White 7 1 1 1
Dac# primul bit (bitul 7) este 1, caracterul va clipi intermitent. Urm#torii trei bi"i
determin# culoarea de fond. Pentru aceasta pot fi utilizate oricare din culorile din tabelul de
mai sus. Bitul 3 determin# dac# acel caracter va fi afi!at cu intensitate, iar ultimii trei bi"i
determin# culoarea de scris.

162
Exerci#iul 101. Scrie"i un program C care s# vizualizeze !i s# editeze con"inutul
memoriei. Programul trebuie s# realizeze urm#toarele: afi!area con"inutului memoriei n
hexazecimal !i zecimal, modificarea unei loca"ii, afi!area unei zone de memorie specificate
prin adresa de nceput !i de sfr!it (afi!area va fi, la cerere, n mod continuu sau n mod
pagin#) !i umplerea unei zone de memorie cu o anumit# valoare. Saltul de la o loca"ie la alta
se va face cu tastele s#ge"i, iar de la o pagin# la alta cu PgUp !i PgDn. Fiecare cerin"# va fi
implementat# ca func"ie distinct# !i va fi selectat# cu ajutorul unui meniu cu bar# de selec"ie.
Meniul va fi realizat folosind accesul direct la memoria ecran.
8.5 Tratarea erorilor n func#iile matematice
n cele ce urmeaz# vom prezenta modul n care pot fi tratate erorile care pot apare
ntr-un program n care snt utilizate func"ii din biblioteca matematic# definite n < << <math.h> >> >.
Pentru tratarea erorilor care pot apare prin utilizarea unei func"ii din < << <math.h> >> > exist#
dou# func"ii, _matherr !i matherr. Cnd este detectat# o excep"ie (eroare) de c#tre una din
func"iile din biblioteca matematic#, este realizat un apel la func"ia _matherr cu informa"iile
corespunz#toare erorii. Aceast# func"ie seteaz# variabila global# errno pe valoarea ERANGE
sau EDOMAIN !i nu trebuie apelat# direct dintr-un program utilizator.
Func"ia matherr poate fi modificat# n programe pentru a trata !i eventual a corecta
astfel de erori, care n mod implicit au ca efect oprirea programului din execu"ie, cu afi!area
unui mesaj de eroare.
De exemplu, dac# utiliz#m func"ia log cu argumentul 2.0, este mult mai sugestiv un
mesaj de eroare de tipul
Apelul log(-2.0) produce eroare
dect mesajul furnizat de defini"ia implicit# a func"iei _matherr.
Erorile de dep#!ire inferioar# !i de precizie snt n general mascate, deoarece astfel de
erori nu snt considerate erori n concordan"# cu standardul ANSI C. n consecin"#, avem
exp($000)=0
sin($e$00)=NAN
f#r# a ob"ine vreo avertizare sau eroare, de!i n ambele cazuri rezultatele ob"inute snt total
inexacte. Astfel de erori pot fi interceptate modificnd func"ia matherr.
Erorile care pot apare snt definite ca !i constante n < << <math.h> >> > !i snt urm#toarele:
DOMAIN, SING, OVERFLOW, UNDERFLOW, TLOSS !i PLOSS. Erorile UNDERFLOW
!i TLOSS snt mascate de func"ia matherr implicit#, iar PLOSS nu este utilizat#. Celelalte
erori, DOMAIN, SING !i OVERFLOW snt fatale n func"ia matherr implicit#. Putem
modifica func"ia matherr pentru a trata erorile ntr-un mod propriu programului, iar n unele
cazuri chiar s# rezolv#m eroarea ap#rut#. ntr-o astfel de situa"ie func"ia matherr trebuie s#
returneze zero dac# a e!uat n rezolvarea erorii, sau non-zero dac# eroarea a fost rezolvat#.
Ca un exemplu, d#m mai jos un model posibil al func"iei matherr. Modelul nu ncearc#
s# corecteze vreo eroare ap#rut#.
#include <math.h>
matherr(struct exception e)
{
printf(\a\nA aparut o eroare, si anume:\n\n);
switch(e->type)

163
{
case UNDERFLOW:
printf(Argumentul %lf este prea mic in functia %s,e->arg$, e->name);
break;
case DOMAIN:
printf(Argumentul %lf este eronat in %s,e->arg$,e->name);
break;
case SING:
printf(Expresia %s(%lf,%lf) nu are sens,e->name, e->arg$,e->arg2);
break;
case TLOSS:
printf(Nu are sens evaluarea %s(%lf),e->name,e->arg$);
break;
case OVERFLOW:
printf(Argumentul %lf e prea mare in functia %s,e->arg$, e->name);
break;
default:
printf(Depasirea stivei aritmetice in functia %s,e->name);
break;
}
printf(\n\n);
exit(-$);
}
8.6 Rezumat
n acest capitol este prezentat# interfa"a limbajului C cu sistemul de operare MS-DOS.
Sistemul de fi!iere din MS-DOS este foarte asem#n#tor cu cel din Unix, deci cea mai mare
parte din aceste lucruri se reg#sesc !i n Unix. Modelele de memorie snt specifice
microprocesoarelor Intel

, deci lucrul cu modele de memorie l-am inclus n acest capitol,


deoarece sistemul de operare MS-DOS ruleaz# pe astfel de microprocesoare. De asemenea
este prezentat !i modul de accesare direct# a memoriei, utiliznd pointeri !i "innd cont de
structura memoriei.

164
9. NO!IUNI DE PROGRAMARE N WIN32


n cele ce urmeaz# vom prezenta pe scurt unele aspecte referitoare la programarea n
Win32

. Programarea n Win32 este specific# sistemelor de operare Windows NT, Windows


2000, Windows 95 !i Windows 98. Func"iile standard C lucreaz# n mod identic !i n Win32,
dar utilizeaz# facilit#"ile oferite (de exemplu lucreaz# cu fi!iere cu nume lungi).
Trebuie remarcat faptul c# lucrurile referitoare la intr#ri !i ie!iri de nivel inferior din
Intr#ri !i ie!iri de nivel inferior snt valabile !i n Win32, cu deosebirea c# fi!iere pot avea
nume lungi (vezi explica"iile de mai jos). n Visual C++ 6.0 func"iile !i constantele utilizate la
intr#ri !i ie!iri de nivel inferior au caracterul _ (underscore) n fa"#.
Programele prezentate n acest capitol au fost compilate cu Visual C++ 6.0 SP3 !i rulate
pe un calculator Dual PII 300MHz, 256MB RAM, sub sistemul de operare Windows NT
Server 4.0 SP5.
9.1 Fi"iere "i directori
n Win32, spre deosebire de MS DOS, fi!ierele !i directorii (care se mai numesc !i
foldere) pot avea nume lungi (pn# la 255 caractere) !i chiar unele caractere care n MS DOS
snt ilegale (de exemplu, C++ este un nume corect n Win32 !i incorect n MS DOS).
Func"iile standard de I/O lucreaz# n Win32 cu fi!iere cu nume lungi, iar programele C
care utilizeaz# numai func"ii standard pot fi compilate !i rulate n Win32 f#r# nici o
modificare.
Deoarece scopul acestei c#r"i nu este programarea n Win32, nu vom prezenta dect pe
scurt analogul func"iilor MS DOS din Interfa"a cu sistemul de operare MS DOS referitoare la
fi!iere !i directori. Func"iile !i constantele utilizate n programe pot fi g#site n documenta"ia
de Win32.
n continuare prezent#m un program care !terge atributele unui fi!ier sau director.
Trebuie remarcat faptul c#, spre deosebire de MS DOS, n Win32 acest program !terge !i
atributele unui director, indiferent dac# directorul este pe un hard disc cu sistem de fi!iere
FAT sau NTFS.
Ca !i o observa"ie, sistemul de fi!iere NTFS mai are !i atributul compressed pentru un
director sau fi!ier, care indic# dac# un director sau fi!ier este sau nu comprimat. Acest atribut
nu poate fi modificat utiliznd programul de mai jos.
#include <afx.h>
char path[MAX_PATH];
void rw()
{
WIN32_FIND_DATA finfo;
HANDLE h;
if((h=FindFirstFile(_T("."),&finfo))!=INVALID_HANDLE_VALUE)
{

165
do
{
SetFileAttributes(finfo.cFileName,FILE_ATTRIBUTE_NORMAL);
} while(FindNextFile(h,&finfo));
FindClose(h);
}
}
void changedir()
{
WIN32_FIND_DATA finfo;
HANDLE h;
rw();
if((h=FindFirstFile(_T("."),&finfo))!=INVALID_HANDLE_VALUE)
{
do
{
if(finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
if(*finfo.cFileName!='.')
if(SetCurrentDirectory(finfo.cFileName))
{
changedir();
SetCurrentDirectory("..");
}
} while(FindNextFile(h,&finfo));
FindClose(h);
}
}
void main()
{
GetCurrentDirectory(sizeof(path),path);
changedir();
SetCurrentDirectory(path);
}
Exerci#iul 102. Scrie"i un program C n Win32 care s# modifice atributele unui fi!ier
sau director. Numele fi!ierului sau al directorului, precum !i atributele, se vor da ca argumente
n linia de comand#. Fiecare atribut va fi codificat cu o liter# !i va fi precedat de semnul + sau
, avnd semnifica"ia c# atributul corespunz#tor al fi!ierului sau al directorului va fi setat (+)
sau !ters ().
9.2 Lucrul cu fi"iere
n cele ce urmeaz# vom prezenta pe scurt principalele func"ii pentru lucrul cu fi!iere,
specifice Win32. Cititorul care dore!te detalii suplimentare pentru o anumit# func"ie poate
consulta bibliografia.
Func"ia CopyFile copiaz# un fi!ier existent ntr-un fi!ier nou !i returneaz# o valoare
nonzero n caz de succes !i zero n caz de e!ec:
BOOL CopyFile(lpOldName,lpNewName,pmode);

166
Argumentul lpOldName este un !ir de caractere care con"ine numele fi!ierului surs#.
Argumentul lpNewName specific# numele fi!ierului destina"ie. Argumentul pmode
precizeaz# ce se ntmpl# n cazul n care fi!ierul destina"ie exist# !i poate avea una din
valorile:
TRUE func"ia e!ueaz# dac# fi!ierul destina"ie exist#
FALSE suprascrie fi!ierul destina"ie dac# acesta exist#
Func"ia MoveFile redenume!te sau mut# un fi!ier sau director existent !i returneaz# o
valoare nonzero n caz de succes !i zero n caz de e!ec. Fi!ierul poate fi pe un alt disc sau
calculator, iar directorul trebuie s# fie pe acela!i disc. Directorul este mutat cu to"i
subdirectorii pe care i con"ine.
BOOL MoveFile(lpOldName,lpNewName);
Argumentul lpOldName este un !ir de caractere care con"ine numele vechi al fi!ierului
sau directorului. Argumentul lpNewName specific# noul nume al fi!ierului sau directorului.
Noul nume nu trebuie s# existe.
Func"ia MoveFileEx redenume!te sau mut# un fi!ier sau director existent !i returneaz# o
valoare nonzero n caz de succes !i zero n caz de e!ec. Directorul este mutat cu to"i
subdirectorii pe care i con"ine. Aceast# func"ie exist# numai n Windows NT !i Windows
2000.
BOOL MoveFileEx(lpOldName,lpNewName,dwFlag);
Argumentul lpOldName este un !ir de caractere care con"ine numele vechi al fi!ierului
sau directorului de pe calculatorul local.
Argumentul lpNewName specific# noul nume al fi!ierului sau directorului. Cnd este
mutat un director, destina"ia trebuie s# fie pe acela!i disc. Cnd este mutat un fi!ier, destina"ia
poate fi pe un alt disc sau calculator. Dac# destina"ia este pe un alt disc, argumentul dwFlag
trebuie s# aib# setat# valoarea MOVEFILE_COPY_ALLOWED. Dac# dwFlag con"ine
MOVEFILE_DELAY_UNTIL_REBOOT, argumentul lpNewName poate fi NULL. n acest
caz sistemul nregistreaz# c# lpOldName va fi !ters cnd calculatorul va fi repornit. n cazul
cnd lpOldName este director, acesta va fi !ters la repornirea calculatorului numai dac# este
vid.
Argumentul dwFlag specific# modul n care se realizeaz# mutarea !i poate avea una din
valorile:
MOVEFILE_COPY_ALLOWED dac# fi!ierul este mutat pe un volum diferit,
func"ia simuleaz# mutarea utiliznd func"iile CopyFile !i DeleteFile. Aceast#
valoare nu poate fi utilizat# mpreun# cu valoarea
MOVEFILE_DELAY_UNTIL_REBOOT
MOVEFILE_DELAY_UNTIL_REBOOT func"ia nu mut# fi!ierul pn# cnd
sistemul de operare nu este repornit. Sistemul mut# fi!ierul imediat dup#
execu"ia lui AUTOCHK, dar nainte de crearea oric#rui paging file. Aceast#
valoare poate fi folosit# numai dac# programul a fost lansat de un utilizator
cu drept de administrator !i nu poate fi utilizat# mpreun# cu
MOVEFILE_COPY_ALLOWED
MOVEFILE_REPLACE_EXISTING suprascrie fi!ierul destina"ie dac# acesta exist#
MOVEFILE_WRITE_THROUGH func"ia nu se termin# pn# cnd fi!ierul nu a fost
mutat efectiv. Aceasta asigur# faptul c# opera"ia de mutare, format# dintr-o
opera"ie de copiere !i una de !tergere, este scris# efectiv (flushed) pe disc
nainte ca func"ia s# se termine. Scrierea pe disc are loc la sfr!itul copierii.

167
Aceast# valoare nu are nici un efect dac# se utilizeaz#
MOVEFILE_DELAY_UNTIL_REBOOT
Ca !i o alt# aplica"ie util# prezent#m n continuare un program care mparte un fi!ier n
mai multe p#r"i de aceea!i dimensiune (split), ultima parte putnd fi mai mic#. Acest program,
cu modific#ri minime, poate fi rulat n orice sistem de operare. Numele fi!ierului care este
mp#r"it este dat ca argument n linia de comand#, iar m#rimea unei p#r"i este citit#. Dac#
fi!ierul are nume lung care con"ine spa"ii, acesta trebuie inclus n ghilimele.
Utilitatea acestui program este evident#, de exemplu n cazul n care dorim s#
transport#m pe dischete de pe un calculator pe altul un fi!ier de dimensiune mare care nu
ncape pe o singur# dischet#. De men"ionat c# Norton Commander 5.0 are aceast# facilitate,
dar nu lucreaz# cu fi!iere cu nume lung.
#include <io.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys\stat.h>
#include <stdlib.h>
const max=32000;
unsigned char buf[max];
main(int argc, char argv)
{
char nr[$$],end;
int citit,fisier=$;
int fd,fo;
char nume[_MAX_PATH],str[4];
long n,n$;
if(argc!=2)
{
printf("\aLipseste numele fisierului!\n");
return $;
}
if((fd=_open(++argv,_O_RDONLY|_O_BINARY,
_S_IWRITE|_S_IREAD))==$)
{
printf("\aFisierul %s nu exista sau nu poate fi deschis!\n",argv);
return 2;
}
printf("Introduceti marimea partii ca numar intreg: ");
fgets(nr,sizeof(nr),stdin);
n=strtol(nr,&end,$0);
if(n<=0)
{
printf("\aNu se poate crea un fisier de %ld octeti\n",n);
return 4;
}
n$=n;
strcpy(nume,"tai.");
itoa(fisier,str,$0);
strcat(nume,str);
if((fo=_open(nume,_O_CREAT|_O_BINARY|_O_WRONLY,
_S_IREAD|_S_IWRITE))==$)
{
printf("\aNu pot crea fisierul destinatie %s\n",nume);

168
return 3;
}
while((citit=_read(fd,buf,max))>0)
{
itoa(fisier,str,$0);
strcat(nume,str);
if((long)citit>n)
{
if(_write(fo,buf,n)==$)
{
printf("\aEroare la scriere. %s\n",(errno==28)?"Disc full.":"");
_close(fo);
_close(fd);
return 5;
}
_close(fo);
lseek(fd,citit+n,SEEK_CUR);
strcpy(nume,"tai.");
itoa(++fisier,str,$0);
strcat(nume,str);
if((fo=open(nume,O_CREAT|O_BINARY|O_WRONLY,
S_IREAD|S_IWRITE))==$)
{
printf("\aNu pot crea fisierul destinatie %s\n",nume);
return 3;
}
n=n$;
continue;
}
else
{
if(_write(fo,buf,citit)==$)
{
printf("\aEroare la scriere. %s\n",(errno==28)?"Disc full.":"");
_close(fo);
_close(fd);
return 5;
}
n=citit;
if(n<=0)
{
_close(fo);
break;
}
}
}
_close(fd);
return 0;
}
Exerci#iul 103. Modifica"i programul anterior astfel nct s# realizeze mp#r"irea unui
fi!ier n mai multe fi!iere de aceea!i m#rime, introducnd num#rul de p#r"i n care se
descompune fi!ierul n locul m#rimii unei p#r"i.


169
Exerci#iul 104. Scrie"i un program C n Win32 care s# realizeze opera"ia invers# a
programului anterior, adic# s# combine (merge) ntr-un ntreg p#r"ile unui fi!ier descompus cu
metoda anterioar#.
9.3 Rezumat
n acest capitol este prezentat# interfa"a limbajului C cu sistemul Win32

caracteristic
sistemelor de operare Windows NT, Windows 2000, Windows 95 !i Windows 98. Am
prezentat doar o parte din acele aspecte care nu se reg#sesc n alte sisteme de operare. Scopul
capitolului nefiind prezentarea sintaxei func"iilor, acestea snt utilizate doar pentru necesit#"i.
Cititorul care dore!te s# cunoasc# mai multe detalii despre func"iile din Win32, poate consulta
bibliografia.

170
APPENDIX
Pentru a veni n sprijinul cititorilor, d#m n continuare lista cu func"iile care se g#sesc n
carte.
A
addpoint, 119
addtree, 127
afree, 83
alloc, 83
am_gasit_plus_minus, 97
atof, 54
atoi, 30, 46, 55
B
binsearch, 43, 122, 125
C
changedir, 161, 173
copy, 20, 22
D
day_of_year, 101
dcl, 114
delete_bit, 36
dirdcl, 114
E
error, 158
F
fgets, 148
filecopy, 146
fputs, 149
G
get, 159
getch, 59
getchar, 156
getline, 20, 22, 52, 149
getop, 58
gettoken, 113
getword, 123
I
inclus, 49
initnext, 91
itoa, 47
L
lower, 30
M
makepoint, 119
matherr, 170
month_day, 101
month_name, 100
mpf, 141
N
next_char, 97
numcmp, 110
O
op, 98
operator, 89
P
pondere, 88
pop, 58, 88, 97, 130
pot_introduce, 89
power, 16, 18
printd, 67
ptinrect, 119
pun_in_paranteza, 97
push, 58, 88, 97, 129

171
Q
qsort, 68, 95, 109
R
rand, 33
readlines, 95
reverse, 47
rw, 172
S
shellsort, 46
squeeze, 34
srand, 33
srch_nxt_chr, 88
stackempty, 130
stackinit, 129
sterg, 160
strcat, 34
strcmp, 87
strcpy, 86, 87
strdup, 129
strindex, 52, 90, 91
strlen, 27, 79, 84, 85
swap, 68, 74, 75, 96, 109
T
talloc, 129, 133
treeprint, 128, 130
trim, 48
U
ungetch, 59
W
writelines, 95


172
BIBLIOGRAFIE
[1] Steve Burnap, Advanced Turbo C Programming, Greensboro, North Carolina, 1988.
[2] Narain Gehani, C: An Advanced Introduction, Rockville, Maryland, 1985.
[3] Brian W. Kernighan, Rob Pike, The Unix Programming Environment, Prentice Hall,
1984.
[4] Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language, Prentice Hall,
1978.
[5] Brian W. Kernighan, Dennis M. Ritchie, The C Programming Language, Second
Edition, Prentice Hall, 1988.
[6] Mircea Dr#gan, Programarea n C, Universitatea din Oradea, 1996.
[7] MSDN Library Visual Studio 6.0, C Language Reference, 1998.
[8] MSDN Library Visual Studio 6.0, Windows NT 4.0 Server Resource Kit, 1998.
[9] MSDN Library Visual Studio 6.0, Windows NT 4.0 Workstation Resource Kit, 1998.
[10] Robert Sedgewick, Algorithms in C, Addison Wesley, 1990.
[11] Richard Stevens, Advanced Programming in the Unix Environment, Addison Wesley,
1992.
[12] Clovis L. Tondo, Scott E. Gimpel, The C Answer Book, Second Edition, Prentice Hall,
1989.

Вам также может понравиться