You are on page 1of 90

__________________________________________________________________________

3
Theory without practice is useless; practice without theory is blind
Roger Bacon
Limbajul C a fost creat la nceputul anilor '70 de ctre Brian W
Kernigham i Dennis M Ritchie de la Bell Laboratories New Jersey, fiind
iniial destinat scrierii unei pri din sistemul de operare Unix. Lucrarea
The C Programming Language a celor doi autori, aprut n mai multe
versiuni, a rmas cartea de referin n domeniu, impunnd un standard
minimal pentru orice implementare.
Caracteristicile distinctive ale limbajului au fost clar definite de la
nceput, ele pstrndu-se n toate dezvoltrile ulterioare:
portabilitate maxim;
structurare;
posibilitatea efecturii operaiilor la nivelul mainii cu pstrarea
caracteristicilor unui limbaj evoluat.
Acest manual este structurat pe 12 capitole astfel nct elementele
limbajului C s fie prezentate ntr-o manier unitar. Primul capitol face o
scurt introducere i prezint patru programe C. Urmtoarele nou capitole
descriu elementele limbajului C. Capitolele unsprezece i doisprezece trec n
revist funciile cele mai des utilizate definite n biblioteca standard,
mpreun cu cteva programe demonstrative. Au fost selectate doar funciile
definite de mai multe standarde (n primul rnd ANSI C), pentru a garanta o
portabilitate ct mai mare.
Acest manual a fost conceput pentru a servi ca document care s poat
fi consultat de programatori n elaborarea proiectelor, i nu pentru a fi
memorat. Manualul nu este o introducere n limbajul C; se presupune c
cititorul este familiarizat cu:
concepte de baz referitoare la programare: variabile, instruciuni de
atribuire, de control al execuiei, apeluri de funcii;
reprezentarea informaiei n calculator a valorilor ntregi, n virgul
mobil, a codurilor ASCII;
operaii de intrare / ieire.
Deoarece avem convingerea c cea mai bun explicaie este un program
funcional, majoritatea exemplelor din acest manual se regsesc n fiiere
surs C care pot fi rulate pe orice mediu de programare C i sub orice sistem
de operare.
Ca o ultim observaie amintim recomandarea fcut de nii creatorii
limbajului: cea mai bun metod de nvare este practica.
__________________________________________________________________________
4
1. Generaliti asupra limbajului C
1.1. Introducere
Limbajul C este un limbaj de programare universal, caracterizat
printr-o exprimare concis, un control modern al fluxului execuiei,
structuri de date, i un bogat set de operatori.
Limbajul C nu este un limbaj de nivel foarte nalt i nu este
specializat pentru un anumit domeniu de aplicaii. Absena
restriciilor i generalitatea sa l fac un limbaj mai convenabil i mai
eficient dect multe alte limbaje mai puternice.
Limbajul C permite scrierea de programe bine structurate,
datorit construciilor sale de control al fluxului: grupri de
instruciuni, luri de decizii (i f ), cicluri cu testul de terminare
naintea ciclului (whi l e, f or ) sau dup ciclu (do) i selecia unui
caz dintr-o mulime de cazuri (swi t ch).
Limbajul C permite lucrul cu pointeri i are o aritmetic de
adrese puternic.
Limbajul C nu are operaii care prelucreaz direct obiectele
compuse cum snt irurile de caractere, mulimile, listele sau
masivele, considerate fiecare ca o entitate. Limbajul C nu prezint
faciliti de alocare a memoriei altele dect definiia static sau
disciplina de stiv relativ la variabilele locale ale funciilor. n
sfrit, limbajul C nu are faciliti de intrare-ieire i nici metode
directe de acces la fiiere. Toate aceste mecanisme de nivel nalt snt
realizate prin funcii explicite.
Dei limbajul C este, aadar, un limbaj de nivel relativ sczut, el
este un limbaj agreabil, expresiv i elastic, care se preteaz la o gam
larg de programe. C este un limbaj restrns i se nva relativ uor,
iar subtilitile se rein pe msur ce experiena n programare crete.
__________________________________________________________________________
5
1.2. Primele programe
n aceast seciune snt prezentate i explicate patru programe cu
scopul de a asigura un suport de baz pentru prezentrile din
capitolele urmtoare.
Prin tradiie primul program C este un mic exemplu din lucrarea
devenit clasic The C programming language, de Brian W
Kernigham i Dennis M Ritchie.
#i ncl ude <st di o. h>
mai n( ) {
pr i nt f ( "Hel l o, wor l d\ n" ) ;
r et ur n 0;
}
Acest program afieaz un mesaj de salut.
Prima linie indic faptul c se folosesc funcii de intrare / ieire,
i descrierea modului de utilizare (numele, tipul argumentelor, tipul
valorii returnate etc) a acestora se afl n fiierul cu numele
st di o. h .
A doua linie declar funcia mai n care va conine instruciunile
programului. n acest caz singura instruciune este un apel al funciei
pr i nt f care afieaz un mesaj la terminal. Mesajul este dat ntre
ghilimele i se termin cu un caracter special new-line (' \ n' ).
Instruciunea r et ur n pred controlul sistemului de operare la
terminarea programului i comunic acestuia codul 0 pentru
terminare. Prin convenie aceast valoare semnific terminarea
normal a programului - adic nu au aprut erori n prelucrarea
datelor.
Corpul funciei mai n apare ntre acolade.
Al doilea program ateapt de la terminal introducerea unor
numere ntregi nenule i determin suma lor. n momentul n care se
introduce o valoare zero, programul afieaz suma calculat.
#i ncl ude <st di o. h>
mai n( ) {
i nt s, n;
__________________________________________________________________________
6
s = 0;
do {
scanf ( " %d" , &n) ;
s += n;
} whi l e ( n! =0) ;
pr i nt f ( "%d\ n" , s) ;
r et ur n 0;
}
n cadrul funciei mai n se declar dou variabile s i n care vor
memora valori ntregi. Variabila s (care va pstra suma numerelor
introduse) este iniializat cu valoarea 0.
n continuare se repet o secven de dou instruciuni, prima
fiind o operaie de intrare i a doua o adunare.
Primul argument al funciei scanf - formatul de introducere
" %d" - indic faptul c se ateapt introducerea unei valori ntregi n
format zecimal de la terminal (consol). Al doilea argument indic
unde se va depune n memorie valoarea citit; de aceea este necesar
s se precizeze adresa variabilei n (cu ajutorul operatorului &).
n a doua instruciune la valoarea variabilei s se adun valoarea
variabilei n. Operatorul += are semnificaia adun la.
Aceast secven se repet (do) ct timp (whi l e) valoarea
introdus (n) este nenul. Operatorul ! = are semnificaia diferit de.
n final funcia pr i nt f afieaz pe terminal valoarea variabilei
s n format zecimal.
Al treilea program ateapt de la terminal introducerea unei
valori naturale n, dup care mai ateapt introducerea a n valori reale
(dubl precizie): a
0
, a
1
, ..., a
n1
. n continuare se parcurge aceast
list i se determin produsul valorilor strict pozitive. n final
programul afieaz produsul calculat.
#i ncl ude <st di o. h>
mai n( ) {
i nt n, i ;
doubl e a[ 100] , p;
scanf ( " %d" , &n) ;
__________________________________________________________________________
7
f or ( i =0; i <n; i ++)
scanf ( " %l f " , &a[ i ] ) ;
p = 1;
f or ( i =0; i <n; i ++)
i f ( a[ i ] >0)
p *= a[ i ] ;
pr i nt f ( "%l f \ n" , p) ;
r et ur n 0;
}
n cadrul funciei mai n se declar dou variabile n i i care vor
memora valori ntregi. Variabila n pstreaz numrul de valori reale
din lista a. Se declar de asemenea un tablou unidimensional a care
va memora 100 de valori de tip real (dubl precizie), i o variabil p
care va memora produsul cerut.
Se citete de la terminal o valoare n. n continuare se introduc
valorile reale a
i
(i = 0, 1, ..., n1). Formatul de introducere " %l f "
indic faptul c se ateapt introducerea unei valori reale de la
terminal, care va fi depus la locaia de memorie asociat variabilei
a
i
. n locul construciei &a[ i ] se poate folosi forma echivalent
a+i .
Pentru a introduce toate valorile a
i
se efectueaz un ciclu f or ,
n cadrul cruia variabila i (care controleaz ciclul) ia toate valorile
ntre 0 (inclusiv) i n (exclusiv) cu pasul 1. Trecerea la urmtoarea
valoare a variabilei i se face cu ajutorul operatorului ++.
n continuare variabila p, care va memora produsul valorilor
cerute, se iniializeaz cu 1. Fiecare valoare a
i
este verificat
(instruciunea i f ) dac este strict pozitiv i n caz afirmativ este
nmulit cu valoarea p. Operatorul *= are semnificaia nmulete
cu.
Al patrulea program este o ilustrare a unor probleme legate de
capacitatea reprezentrilor valorilor de tip ntreg i virgul mobil.
#i ncl ude <st di o. h>
i nt mai n( ) {
shor t k, i ;
__________________________________________________________________________
8
f l oat a, b, c, u, v, w;
i =240; k=i *i ;
pr i nt f ( "%hd\ n" , k) ;
a=12345679; b=12345678;
c=a*a- b*b;
u=a*a; v=b*b; w=u- v;
pr i nt f ( "%f %f \ n" , c, w) ;
i f ( c==w) r et ur n 0;
el se r et ur n 1;
}
Variabila k, care ar trebui s memoreze valoarea 57600, are tipul
ntreg scurt (shor t ), pentru care domeniul de valori este restrns la
32768 32767. Astfel c valoarea 1110000100000000
(2)
(n
zecimal 57600), n reprezentare ntreag cu semn este de fapt 7936.
Al doilea set de operaii necesit o analiz mai atent; explicaiile
snt valabile pentru programe care ruleaz pe arhitecturi Intel.
Variabila c, care ar trebui s memoreze valoarea 2461357 (rezultatul
corect), va avea valoarea 2461356, deoarece tipul f l oat are
rezervate pentru mantis doar 24 de cifre binare. Rezultatul este
foarte apropiat de cel corect deoarece rezultatele intermediare se
pstreaz n regitrii coprocesorului matematic cu precizie maxim.
Abia la memorare se efectueaz trunchierea, de unde rezult valoarea
afiat.
Cu totul altfel stau lucrurile n cazul celui de al treilea set de
operaii. Aici rezultatele intermediare snt memorate de fiecare dat
cu trunchiere n variabile de tip f l oat . n final se calculeaz i
diferena dintre cele dou valori trunchiate, de unde rezult valoarea
16777216.
nainte de terminare se verific dac valorile c i wsnt egale. n
caz afirmativ se comunic sistemului de operare un cod 0 (terminare
normal). n caz contrar se comunic un cod 1 (terminare anormal).
Rulai acest program pe diferite sisteme de calcul i observai
care este rezultatul.
__________________________________________________________________________
9
1.3. Meta-limbajul i setul de caractere
Meta-limbajul care servete la descrierea formal a sintaxei
limbajului C este simplu.
Categoriile sintactice sau noiunile care trebuie definite snt
urmate de simbolul ':'.
Definiiile alternative de categorii snt listate pe linii separate.
Dac o linie nu este suficient, se trece la linia urmtoare, aliniat la
un tab fa de linia precedent.
Un simbol opional, terminal sau neterminal este indicat prin
adugarea imediat dup el a configuraiei de caractere <opt>.
Setul de caractere al limbajului C este un subset al setului de
caractere ASCII, format din:
26 litere mici
a b c d e f g h i j k l m n o p q r s t u v w x y z
26 litere mari
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
10 cifre
0 1 2 3 4 5 6 7 8 9
30 simboluri speciale
Blanc ! " # % & ' ( ) * + , . / : ; < = > ? [ \ ] ^ _ ~ { | }
6 simboluri negrafice
\n, \t, \b, \r, \f, \a
__________________________________________________________________________
10
2. Unitile lexicale ale limbajului C
n limbajul C exist ase tipuri de uniti lexicale: identificatori,
cuvinte-cheie, constante, iruri, operatori i separatori.
2.1. Identificatori
Un identificator este o succesiune de litere i cifre dintre care
primul caracter este n mod obligatoriu o liter. Se admit i litere mari
i litere mici dar ele se consider caractere distincte. Liniua de
subliniere _ este considerat ca fiind liter. Deci alfabetul peste care
snt definii identificatorii este urmtorul:
A = <a,...,z,A,...,Z,0,...,9,_>
2.2. Cuvinte cheie
Cuvintele cheie snt identificatori rezervai limbajului. Ei au o
semnificaie bine determinat i nu pot fi utilizai dect aa cum cere
sintaxa limbajului. Cuvintele-cheie se scriu obligatoriu cu litere mici.
Aceste cuvinte snt (fiecare mediu de programare C poate folosi i
alte cuvinte rezervate):
i nt r egi st er i f const
char aut o el se voi d
f l oat ext er n f or swi t ch
doubl e st at i c whi l e case
l ong st r uct do def aul t
shor t uni on cont i nue r et ur n
unsi gned t ypedef br eak si zeof
__________________________________________________________________________
11
2.3. Constante
n limbajul C exist urmtoarele tipuri de constante: ntreg
(zecimal, octal, hexazecimal), ntreg lung explicit, flotant, caracter,
simbolic.
Constante ntregi
O constant ntreag const dintr-o succesiune de cifre.
O constant octal este o constant ntreag care ncepe cu 0
(cifra zero), i este format cu cifre de la 0 la 7.
O constant hexazecimal este o constant ntreag precedat de
0x sau 0X(cifra 0 i litera x). Cifrele hexazecimale includ literele de
la Ala F i de la a la f cu valori de la 10 la 15.
n orice alt caz, constanta ntreag este o constant zecimal.
Exemplu: constanta zecimal 31 poate fi scris ca 037 n octal i
0x1f sau 0X1F n hexazecimal.
O constant ntreag este generat pe un cuvnt (doi sau patru
octei, dac sistemul de calcul este pe 16 sau 32 de bii).
O constant zecimal a crei valoare depete pe cel mai mare
ntreg cu semn reprezentabil pe un cuvnt scurt (16 bii) se consider
de tip l ong i este generat pe 4 octei.
O constant octal sau hexazecimal care depete pe cel mai
mare ntreg fr semn reprezentabil pe un cuvnt scurt se consider de
asemenea de tip l ong.
O constant ntreag devine negativ dac i se aplic operatorul
unar de negativare - .
Constante de tip explicit
O constant ntreag zecimal, octal sau hexazecimal, urmat
imediat de litera l sau L este o constant lung. Aceasta va fi
generat n calculator pe 4 octei.
Exemplu: 123L.
__________________________________________________________________________
12
O constant ntreag zecimal urmat imediat de litera u sau U
este o constant de tip ntreg fr semn. Litera u sau U poate fi
precedat de litera l sau L.
Exemplu: 123l u.
Constante flotante
O constant flotant const dintr-o parte ntreag, un punct
zecimal, o parte fracionar, litera e sau E i opional, un exponent
care este un ntreg cu semn. Partea ntreag i partea fracionar snt
constituite din cte o succesiune de cifre. ntr-o constant flotant, att
partea ntreag ct i partea fracionar pot lipsi dar nu ambele; de
asemenea poate lipsi punctul zecimal sau litera e i exponentul, dar
nu deodat (i punctul i litera e i exponentul).
Exemplu: 123. 456e7 sau 0. 12e3
Orice constant flotant se consider a fi n precizie extins.
Constante caracter
O constant caracter const dintr-un singur caracter scris ntre
apostrofuri, de exemplu ' x' . Valoarea unei constante caracter este
valoarea numeric a caracterului, n setul de caractere al
calculatorului. De exemplu n setul de caractere ASCII caracterul
zero sau ' 0' are valoarea 48 n zecimal, total diferit de valoarea
numeric zero.
Constantele caracter particip la operaiile aritmetice ca i oricare
alte numere. De exemplu, dac variabila c conine valoarea ASCII a
unei cifre, atunci prin instruciunea:
c = c - ' 0' ;
aceast valoare se transform n valoarea efectiv a cifrei.
Anumite caractere negrafice i caractere grafice ' (apostrof) i \
(backslash) pot fi reprezentate ca i constante caracter cu ajutorul aa
numitor secvene de evitare. Secvenele de evitare ofer de altfel i
un mecanism general pentru reprezentarea caracterelor mai greu de
introdus n calculator i a oricror configuraii de bii. Aceste
secvene de evitare snt:
__________________________________________________________________________
13
\ n new-line \ r carriage return \ \ backslash
\ t tab orizontal \ f form feed \ ' apostrof
\ b backspace \ a semnal sonor \ " ghilimele
\ ddd configuraie de bii (ddd)
Aceste secvene, dei snt formate din mai multe caractere, ele
reprezint n realitate un singur caracter. Secvena ' \ ddd' unde ddd
este un ir de 1 pn la 3 cifre octale, genereaz pe un octet valoarea
caracterului dorit sau a configuraiei de bii dorite, date de irul ddd.
Exemplu: secvena ' \ 040' va genera caracterul spaiu.
Un caz special al acestei construcii este secvena ' \ 0' care
indic caracterul NULL, care este caracterul cu valoarea zero. ' \ 0'
este scris deseori n locul lui 0 pentru a sublinia natura de caracter a
unei anumite expresii.
Cnd caracterul care urmeaz dup un backslash nu este unul
dintre cele specificate, backslash-ul este ignorat. Atragem atenia c
toate caracterele setului ASCII snt pozitive, dar o constant caracter
specificat printr-o secven de evitare poate fi i negativ, de
exemplu ' \ 377' are valoarea - 1.
Constante simbolice
O constant simbolic este un identificator cu valoare de
constant. Valoarea constantei poate fi orice ir de caractere introdus
prin construcia #def i ne (vezi capitolul 8).
Exemplu: #def i ne MAX 1000
Dup ntlnirea acestei construcii compilatorul va nlocui toate
apariiile constantei simbolice MAXcu valoarea 1000.
Numele constantelor simbolice se scriu de obicei cu litere mari
(fr a fi obligatoriu).
__________________________________________________________________________
14
2.4. iruri
Un ir este o succesiune de caractere scrise ntre ghilimele, de
exemplu " ABCD".
Ghilimelele nu fac parte din ir; ele servesc numai pentru
delimitarea irului. Caracterul " (ghilimele) poate aprea ntr-un ir
dac se utilizeaz secvena de evitare \ ". n interiorul unui ir pot fi
folosite i alte secvene de evitare pentru constante caracter, de
asemenea poate fi folosit caracterul \ (backslash) la sfritul unui
rnd pentru a da posibilitatea continurii unui ir pe mai multe linii,
situaie n care caracterul \ nsui va fi ignorat.
Pentru irul de caractere se mai folosete denumirea constant ir
sau constant de tip ir.
Cnd un ir apare ntr-un program C, compilatorul creeaz un
masiv de caractere care conine caracterele irului i plaseaz automat
caracterul NULL (' \ 0' ) la sfritul irului, astfel ca programele care
opereaz asupra irurilor s poat detecta sfritul acestora. Aceast
reprezentare nseamn c, teoretic, nu exist o limit a lungimii unui
ir, iar programele trebuie s parcurg irul, analizndu-l pentru a-i
determina lungimea. Se admit i iruri de lungime zero.
Tehnic, un ir este un masiv ale crui elemente snt caractere. El
are tipul masiv de caractere i clasa de memorie st at i c (vezi
seciunea 3.1). Un ir este iniializat cu caracterele date (vezi
seciunea 5.4).
La alocare, memoria fizic cerut este cu un octet mai mare dect
numrul de caractere scrise ntre ghilimele, datorit adugrii
automate a caracterului null la sfritul fiecrui ir.
Exemplu. Funcia st r l en( s) returneaz lungimea irului de
caractere s, excluznd caracterul terminal null.
i nt st r l en( char s[ ] ) {
/* returneaz lungimea irului */
i nt i ;
i =0;
whi l e ( s[ i ] ! =' \ 0' )
++i ;
__________________________________________________________________________
15
r et ur n i ;
}
Atragem atenia asupra diferenei dintre o constant caracter i un
ir care conine un singur caracter. " x" nu este acelai lucru cu ' x' .
' x' este un singur caracter, folosit pentru a genera pe un octet
valoarea numeric a literei x, din setul de caractere al calculatorului.
" x" este un ir de caractere, care n calculator se reprezint pe doi
octei, dintre care primul conine un caracter (litera x), iar al doilea
caracterul NULL care indic sfritul de ir.
2.5. Operatori
Limbajul C prezint un numr mare de operatori care pot fi
clasificai dup diverse criterii. Exist operatori unari, binari i
ternari, operatori aritmetici, logici, operatori pe bii etc.
ntr-un capitol separat vom prezenta clasele de operatori care
corespund la diferite nivele de prioritate.
2.6. Separatori
Un separator este un caracter sau un ir de caractere care separ
unitile lexicale ntr-un program scris n C.
Separatorul cel mai frecvent este aa numitul spaiu alb (blanc)
care conine unul sau mai multe spaii, tab-uri, new-line-uri sau
comentarii.
Aceste construcii snt eliminate n faza de analiza lexical a
compilrii.
Dm mai jos lista separatorilor admii n limbajul C.
( ) Parantezele mici ncadreaz lista de argumente ale unei
funcii sau delimiteaz anumite pri n cadrul expresiilor
aritmetice etc
{ } Acoladele ncadreaz instruciunile compuse, care
constituie corpul unor instruciuni sau corpul funciilor
[ ] Parantezele mari ncadreaz dimensiunile de masiv sau
__________________________________________________________________________
16
indicii elementelor de masiv
" " Ghilimelele ncadreaz un ir de caractere
' ' Apostrofurile ncadreaz un singur caracter sau o secven
de evitare
; Punct i virgula termin o instruciune
/ * Slash asterisc nceput de comentariu
*/ Asterisc slash sfrit de comentariu
Un comentariu este un ir de caractere care ncepe cu caracterele
/ * i se termin cu caracterele */ .
Un comentariu poate s apar oriunde ntr-un program, unde
poate aprea un blanc i are rol de separator; el nu influeneaz cu
nimic semnificaia programului, scopul lui fiind doar o documentare
a programului.
Nu se admit comentarii imbricate.
__________________________________________________________________________
17
3. Variabile
O variabil este un obiect de programare cruia i se atribuie un
nume.
Variabilele ca i constantele snt elemente de baz cu care
opereaz un program scris n C.
Variabilele se deosebesc dup denumire i pot primi diferite
valori. Numele variabilelor snt identificatori. Numele de variabile se
scriu de obicei cu litere mici (fr a fi obligatoriu).
Variabilele n limbajul C snt caracterizate prin dou atribute:
clas de memorie i tip.
Aceste dou atribute i snt atribuite unei variabile prin
intermediul unei declaraii. Declaraiile listeaz variabilele care
urmeaz a fi folosite, stabilesc clasa de memorie, tipul variabilelor i
eventual valorile iniiale, dar despre declaraii vom discuta n
capitolul 5.
3.1. Clase de memorie
Limbajul C prezint patru clase de memorie: automatic, extern,
static, registru.
Variabile automatice
Variabilele automatice snt variabile locale fiecrui bloc
(seciunea 6.2) sau funcii (capitolul 7). Ele se declar prin
specificatorul de clas de memorie auto sau implicit prin context. O
variabil care apare n corpul unei funcii sau al unui bloc pentru care
nu s-a fcut nici o declaraie de clas de memorie se consider
implicit de clas aut o.
O variabil aut o este actualizat la fiecare intrare n bloc i se
distruge n momentul cnd controlul a prsit blocul. Ele nu i rein
valorile de la un apel la altul al funciei sau blocului i trebuie
iniializate la fiecare intrare. Dac nu snt iniializate, conin valori
__________________________________________________________________________
18
reziduale. Nici o funcie nu are acces la variabilele aut o din alt
funcie. n funcii diferite pot exista variabile locale cu aceleai nume,
fr ca variabilele s aib vreo legtur ntre ele.
Variabile externe
Variabilele externe snt variabile cu caracter global. Ele se
definesc n afara oricrei funcii i pot fi apelate prin nume din
oricare funcie care intr n alctuirea programului.
n declaraia de definiie aceste variabile nu necesit specificarea
nici unei clase de memorie.
La ntlnirea unei definiii de variabil extern compilatorul aloc
i memorie pentru aceast variabil.
ntr-un fiier surs domeniul de definiie i aciune al unei
variabile externe este de la locul de declaraie pn la sfritul
fiierului.
Aceste variabile exist i i pstreaz valorile de-a lungul
execuiei ntregului program.
Pentru ca o funcie s poat utiliza o variabil extern, numele
variabilei trebuie fcut cunoscut funciei printr-o declaraie.
Declaraia poate fi fcut fie explicit prin utilizarea specificatorului
extern, fie implicit prin context.
Dac definiia unei variabile externe apare n fiierul surs
naintea folosirii ei ntr-o funcie particular, atunci nici o declaraie
ulterioar nu este necesar, dar poate fi fcut.
Dac o variabil extern este referit ntr-o funcie nainte ca ea
s fie definit, sau dac este definit ntr-un fiier surs diferit de
fiierul n care este folosit, atunci este obligatorie o declaraie extern
pentru a lega apariiile variabilelor respective.
Dac o variabil extern este definit ntr-un fiier surs diferit de
cel n care ea este referit, atunci o singur declaraie extern dat n
afara oricrei funcii este suficient pentru toate funciile care
urmeaz declaraiei.
Funciile snt considerate n general variabile externe afar de
cazul cnd se specific altfel.
__________________________________________________________________________
19
Variabilele externe se folosesc adeseori n locul listelor de
argumente pentru a comunica date ntre funcii, chiar dac funciile
snt compilate separat.
Variabile statice
Variabilele statice se declar prin specificatorul de clas de
memorie st at i c. Aceste variabile snt la rndul lor de dou feluri:
interne i externe.
Variabilele statice interne snt locale unei funcii i se definesc n
interiorul unei funcii, dar spre deosebire de variabilele aut o, ele i
pstreaz valorile tot timpul execuiei programului. Variabilele
statice interne nu snt create i distruse de fiecare dat cnd funcia
este activat sau prsit; ele ofer n cadrul unei funcii o memorie
particular permanent pentru funcia respectiv.
Alte funcii nu au acces la variabilele statice interne proprii unei
funcii.
Ele pot fi declarate i implicit prin context; de exemplu irurile
de caractere care apar n interiorul unei funcii cum ar fi argumentele
funciei pr i nt f (vezi capitolul 11) snt variabile statice interne.
Variabilele statice externe se definesc n afara oricrei funcii i
orice funcie are acces la ele. Aceste variabile snt ns globale numai
pentru fiierul surs n care ele au fost definite. Nu snt recunoscute
n alte fiiere.
n concluzie, variabila static este extern dac este definit n
afara oricrei funcii i este static intern dac este definit n
interiorul unei funcii.
n general, funciile snt considerate obiecte externe. Exist ns
i posibilitatea s declarm o funcie de clas st at i c. Aceasta face
ca numele funciei s nu fie recunoscut n afara fiierului n care a
fost declarat.
__________________________________________________________________________
20
Variabile registru
O variabil registru se declar prin specificatorul de clas de
memorie r egi st er . Ca i variabilele aut o ele snt locale unui
bloc sau funcii i valorile lor se pierd la ieirea din blocul sau funcia
respectiv. Variabilele declarate r egi st er indic compilatorului c
variabilele respective vor fi folosite foarte des. Dac este posibil,
variabilele r egi st er vor li plasate de ctre compilator n regitrii
rapizi ai calculatorului, ceea ce conduce la programe mai compacte i
mai rapide.
Variabile r egi st er pot fi numai variabilele automatice sau
parametrii formali ai unei funcii. Practic exist cteva restricii
asupra variabilelor r egi st er care reflect realitatea hardware-ului
de baz. Astfel:
numai cteva variabile din fiecare funcie pot fi pstrate n regitri
(de obicei 2 sau 3); declaraia register este ignorat pentru celelalte
variabile;
numai tipurile de date i nt , char i poi nt er snt admise;
nu este posibil referirea la adresa unei variabile r egi st er .
3.2. Tipuri de variabile
Limbajul C admite numai cteva tipuri fundamentale de variabile:
caracter, ntreg, flotant.
Tipul caracter
O variabil de tip caracter se declar prin specificatorul de tip
char . Zona de memorie alocat unei variabile de tip char este de
un octet. Ea este suficient de mare pentru a putea memora orice
caracter al setului de caractere implementate pe calculator.
Dac un caracter din setul de caractere este memorat ntr-o
variabil de tip char , atunci valoarea sa este egal cu codul ntreg al
caracterului respectiv. i alte cantiti pot fi memorate n variabile de
tip char , dar implementarea este dependent de sistemul de calcul.
__________________________________________________________________________
21
Ordinul de mrime al variabilelor caracter este ntre - 128 i
127. Caracterele setului ASCII snt toate pozitive, dar o constant
caracter specificat printr-o secven de evitare poate fi i negativ,
de exemplu ' \ 377' are valoarea - 1. Acest lucru se ntmpl atunci
cnd aceast constant apare ntr-o expresie, moment n care se
convertete la tipul i nt prin extensia bitului cel mai din stnga din
octet (datorit modului de funcionare a instruciunilor
calculatorului).
Tipul ntreg
Variabilele ntregi pozitive sau negative pot fi declarate prin
specificatorul de tip i nt . Zona de memorie alocat unei variabile
ntregi poate fi de cel mult trei dimensiuni.
Relaii despre dimensiune snt furnizate de calificatorii shor t ,
l ong i unsi gned, care pot fi aplicai tipului i nt .
Calificatorul shor t se refer totdeauna la numrul minim de
octei pe care poate fi reprezentat un ntreg, n cazul nostru 2.
Calificatorul l ong se refer la numrul maxim de octei pe care
poate fi reprezentat un ntreg, n cazul nostru 4.
Tipul i nt are dimensiunea natural sugerat de sistemul de
calcul. Scara numerelor ntregi reprezentabile n main depinde de
asemenea de sistemul de calcul: un ntreg poate lua valori ntre
- 32768 i 32767 (sisteme de calcul pe 16 bii) sau ntre -
2147483648 i 2147483647 (sisteme de calcul pe 32 de bii).
Calificatorul unsi gned alturi de declaraia de tip i nt
determin ca valorile variabilelor astfel declarate s fie considerate
ntregi fr semn.
Numerele de tipul unsigned respect legile aritmeticii modulo 2
n
,
unde n este numrul de bii din reprezentarea unei variabile de tip
i nt . Numerele de tipul unsi gned snt totdeauna pozitive.
Declaraiile pentru calificatori snt de forma:
shor t i nt x;
l ong i nt y;
unsi gned i nt z;
__________________________________________________________________________
22
Cuvntul i nt poate fi omis n aceste situaii.
Tipul flotant (virgul mobil)
Variabilele flotante pot fi n simpl precizie i atunci se declar
prin specificatorul de tip f l oat sau n dubl precizie i atunci se
declar prin specificatorul doubl e. Majoritatea sistemelor de calcul
admit i reprezentarea n precizie extins; o variabil n precizie
extins se declar prin specificatorul l ong doubl e.
Tipul void (tip neprecizat)
Un pointer poate fi declarat de tip voi d. n aceast situaie
pointerul nu poate fi folosit pentru indirectare (derefereniere) fr un
cast explicit, i aceasta deoarece compilatorul nu poate determina
mrimea obiectului pe care pointerul l indic.
i nt x;
f l oat r ;
voi d *p = &x; /* p indic pe x */
i nt mai n( ) {
*( i nt *) p = 2;
p = &r ; /* p indic pe r */
*( f l oat *) p = 1. 1;
}
Dac o funcie este declarat de tip voi d, aceasta nu va returna o
valoare:
voi d hel l o( char *name) {
pr i nt f ( "Hel l o, %s. " , name) ;
}
Tipuri derivate
n afar de tipurile aritmetice fundamentale, exist, n principiu, o
clas infinit de tipuri derivate, construite din tipurile fundamentale
n urmtoarele moduri:
__________________________________________________________________________
23
masive de T pentru masive de obiecte de un tip dat T, unde T este
unul dintre tipurile admise;
funcii care returneaz T pentru funcii care returneaz obiecte de
un tip dat T;
pointer la T pentru pointeri la obiecte de un tip dat T;
structuri pentru un ir de obiecte de tipuri diferite;
reuniuni care pot conine obiecte de tipuri diferite, tratate ntr-o
singur zon de memorie.
n general aceste metode de construire de noi tipuri de obiecte
pot fi aplicate recursiv. Amnunte despre tipurile derivate snt date n
seciunea 5.3.
3.3. Obiecte i valori-stnga
Alte dou noiuni folosite n descrierea limbajului C snt obiectul
i valoarea-stnga.
Un obiect este coninutul unei zone de memorie.
O valoare-stnga este o expresie care se refer la un obiect. Un
exemplu evident de valoare-stnga este un identificator. Exist
operatori care produc valori-stnga: de exemplu, dac E este o
expresie de tip pointer, atunci *E este o expresie valoare-stnga care
se refer la obiectul pe care-l indic E. Numele valoare-stnga (n
limba englez left value) a fost sugerat din faptul c n expresia de
atribuire E1 = E2 operandul stng E1 trebuie s fie o expresie
valoare-stnga. n paragraful de descriere a operatorilor se va indica
dac operanzii snt valori-stnga sau dac rezultatul operaiei este o
valoare-stnga.
3.4. Conversii de tip
Un numr mare de operatori pot cauza conversia valorilor unui
operand de la un tip la altul. Dac ntr-o expresie apar operanzi de
diferite tipuri, ei snt convertii la un tip comun dup un mic numr
de reguli. n general se fac automat numai conversiile care an sens,
de exemplu: din ntreg n flotant, ntr-o expresie de forma f +i .
__________________________________________________________________________
24
Expresii care nu au sens, ca de exemplu un numr flotant ca indice,
nu snt admise.
Caractere i ntregi
Un caracter poate aprea oriunde unde un ntreg este admis. n
toate cazurile valoarea caracterului este convertit automat ntr-un
ntreg. Deci ntr-o expresie aritmetic tipul char i i nt pot aprea
mpreun. Aceasta permite o flexibilitate considerabil n anumite
tipuri de transformri de caractere. Un astfel de exemplu este funcia
at oi descris n seciunea 7.5 care convertete un ir de cifre n
echivalentul lor numeric.
Expresia:
s[ i ] - ' 0'
produce valoarea numeric a caracterului (cifr) memorat n ASCII.
Atragem atenia c atunci cnd o variabil de tip char este
convertit la tipul i nt , se poate produce un ntreg negativ, dac bitul
cel mai din stnga al octetului conine 1. Caracterele din setul de
caractere ASCII nu devin niciodat negative, dar anumite configuraii
de bii memorate n variabile de tip caracter pot aprea ca negative
prin extensia la tipul i nt .
Conversia tipului i nt n char se face cu pierderea biilor de
ordin superior.
ntregii de tip shor t snt convertii automat la i nt . Conversia
ntregilor se face cu extensie de semn; ntregii snt totdeauna cantiti
cu semn.
Un ntreg l ong este convertit la un ntreg shor t sau char prin
trunchiere la stnga; surplusul de bii de ordin superior se pierde.
Conversii flotante
Toate operaiile aritmetice n virgul mobil se execut n
precizie extins. Conversia de la f l oat la i nt se face prin
trunchierea prii fracionare. Conversia de la i nt la f l oat este
acceptat.
__________________________________________________________________________
25
ntregi fr semn
ntr-o expresie n care apar doi operanzi, dintre care unul
unsi gned iar cellalt un ntreg de orice alt tip, ntregul cu semn
este convertit n ntreg fr semn i rezultatul este un ntreg fr
semn.
Cnd un i nt trece n unsi gned, valoarea sa este cel mai mic
ntreg fr semn congruent cu ntregul cu semn (modulo 2
16
sau 2
32
).
ntr-o reprezentare la complementul fa de 2 (deci pentru numere
negative), conversia este conceptual, nu exist nici o schimbare
real a configuraiei de bii.
Cnd un ntreg fr semn este convertit la l ong, valoarea
rezultatului este numeric aceeai ca i a ntregului fr semn, astfel
conversia nu face altceva dect s adauge zerouri la stnga.
Conversii aritmetice
Dac un operator aritmetic binar are doi operanzi de tipuri
diferite, atunci tipul de nivel mai sczut este convertit la tipul de nivel
mai nalt nainte de operaie. Rezultatul este de tipul de nivel mai
nalt. Ierarhia tipurilor este urmtoarea:
char < shor t < i nt < l ong;
f l oat < doubl e < l ong doubl e;
tip ntreg cu semn < tip ntreg fr semn;
tip ntreg < virgul mobil.
Conversii prin atribuire
Conversiile de tip se pot face prin atribuire; valoarea membrului
drept este convertit la tipul membrului stng, care este tipul
rezultatului.
Conversii logice
Expresiile relaionale de forma i <j i expresiile logice legate
prin operatorii &&i | | snt definite ca avnd valoarea 1 dac snt
adevrate i 0 dac snt false.
__________________________________________________________________________
26
Astfel atribuirea:
d = ( c>=' 0' ) && ( c<=' 9' ) ;
l face pe d egal cu 1 dac c este cifr i egal cu 0 n caz contrar.
Conversii explicite
Dac conversiile de pn aici le-am putea considera implicite,
exist i conversii explicite de tipuri pentru orice expresie. Aceste
conversii se fac prin construcia special numit cast de forma:
( nume-tip) expresie
n aceast construcie expresie este convertit la tipul specificat
dup regulile precizate mai sus. Mai precis aceasta este echivalent
cu atribuirea expresiei respective unei variabile de un tip specificat, i
aceast nou variabil este apoi folosit n locul ntregii expresii. De
exemplu, n expresia:
sqr t ( ( doubl e) n)
se convertete n la doubl e nainte de a se transmite funciei sqr t .
Notm ns c, coninutul real al lui n nu este alterat. Operatorul
cast are aceeai preceden ca i oricare operator unar.
Expresia constant
O expresie constant este o expresie care conine numai
constante. Aceste expresii snt evaluate n momentul compilrii i nu
n timpul execuiei; ele pot fi astfel utilizate n orice loc unde sintaxa
cere o constant, ca de exemplu:
#def i ne MAXLI NE 1000
char l i ne[ MAXLI NE+1] ;
__________________________________________________________________________
27
4. Operatori i expresii
Limbajul C prezint un numr mare de operatori, caracterizai
prin diferite nivele de prioritate sau preceden.
n acest capitol descriem operatorii n ordinea descresctoare a
precedenei lor. Operatorii descrii n acelai paragraf au aceeai
preceden. Vom specifica de fiecare dat dac asociativitatea este la
stnga sau la dreapta.
Expresiile combin variabile i constante pentru a produce valori
noi i le vom introduce pe msur ce vom prezena operatorii.
4.1. Expresii primare
Expresie-primar:
identificator
constant
ir
( expresie)
expresie-primar [ expresie]
expresie-primar ( list-expresii<opt>)
valoare-stnga . identificator
expresie-primar - > identificator
List-expresii:
expresie
list-expresii, expresie
Un identificator este o expresie-primar, cu condiia c el s fi
fost declarat corespunztor. Tipul su este specificat n declaraia sa.
Dac tipul unui identificator este masiv de ..., atunci valoarea
expresiei-identificator este un pointer la primul obiect al masivului,
iar tipul expresiei este pointer la .... Mai mult, un identificator de
masiv nu este o expresie valoare-stnga.
__________________________________________________________________________
28
La fel, un identificator declarat de tip funcie care returneaz
..., care nu apare pe poziie de apel de funcie este convertit la
pointer la funcie care returneaz ....
O constant este o expresie-primar. Tipul su poate fi i nt ,
l ong sau doubl e. Constantele caracter snt de tip i nt , constantele
flotante snt de tip l ong doubl e.
Un ir este o expresie-primar. Tipul su original este masiv de
caractere, dar urmnd aceleai reguli descrise mai sus pentru
identificatori, acesta este modificat n pointer la caracter i
rezultatul este un pointer la primul caracter al irului. Exist cteva
excepii n anumite iniializri (vezi paragraful 5.4).
O expresie ntre paranteze rotunde este o expresie-primar, al
crei tip i valoare snt identice cu cele ale expresiei din interiorul
parantezelor (expresia din paranteze poate fi i o valoare-stnga).
O expresie-primar urmat de o expresie ntre paranteze ptrate
este o expresie-primar. Sensul intuitiv este de indexare. De obicei
expresia-primar are tipul pointer la ..., expresia-indice are tipul
i nt , iar rezultatul are tipul .... O expresie de forma E1[ E2] este
identic (prin definiie) cu *( ( E1) +( E2) ) , unde * este operatorul
de indirectare.
Un apel de funcie este o expresie-primar. Ea const dintr-o
expresie-primar urmat de o pereche de paranteze rotunde, care
conin o list-expresii separate prin virgule. Lista-expresii constituie
argumentele reale ale funciei; aceast list poate fi i vid. Expresia-
primar trebuie s fie de tipul funcie care returneaz ..., iar
rezultatul apelului de funcie va fi de tipul ....
naintea apelului, oricare argument de tip f l oat este convertit la
tipul doubl e, oricare argument de tip char sau shor t este
convertit la tipul i nt . Numele de masive snt convertite n pointeri la
nceputul masivului. Nici o alt conversie nu se efectueaz automat.
Dac este necesar pentru ca tipul unui argument actual s
coincid cu cel al argumentului formal, se va folosi un cast (vezi
seciunea 3.4).
Snt permise apeluri recursive la orice funcie.
__________________________________________________________________________
29
O valoare-stnga urmat de un punct i un identificator este o
expresie-primar. Valoarea-stnga denumete o structur sau o
reuniune (vezi capitolul 10) iar identificatorul denumete un membru
din structur sau reuniune. Rezultatul este o valoare-stnga care se
refer la membrul denumit din structur sau reuniune.
O expresie-primar urmat de o sgeat (constituit dintr-o
liniu i semnul > urmat de un identificator este o expresie-
primar. Prima expresie trebuie s fie un pointer la o structur sau
reuniune, iar identificatorul trebuie s fie numele unui membru din
structura sau reuniunea respectiv. Rezultatul este o valoare-stnga
care se refer la membrul denumit din structura sau reuniunea ctre
care indic expresia pointer.
Expresia E1>E2 este identic din punctul de vedere al
rezultatului cu ( *E1) . E2
Descriem n continuare operatorii limbajului C mpreun cu
expresiile care se pot constitui cu aceti operatori.
4.2. Operatori unari
Toi operatorii unari au aceeai preceden, iar expresiile unare se
grupeaz de la dreapta la stnga.
Expresie-unar:
* expresie
&valoare-stnga
expresie
! expresie
~expresie
++valoare-stnga
- - valoare-stnga
valoare-stnga ++
valoare-stnga - -
( nume-tip) expresie
si zeof ( nume-tip)
__________________________________________________________________________
30
Operatorul unar * este operatorul de indirectare. Expresia care-l
urmeaz trebuie s fie un pointer, iar rezultatul este o valoare-stnga
care se refer la obiectul ctre care indic expresia. Dac tipul
expresiei este pointer la ... atunci tipul rezultatului este .... Acest
operator trateaz operandul su ca o adres, face acces la ea i i
obine coninutul.
Exemplu: instruciunea y = *px; atribuie lui y coninutul
adresei ctre care indic px.
Operatorul unar & este operatorul de obinere a adresei unui
obiect sau de obinere a unui pointer la obiectul respectiv. Operandul
este o valoare-stnga iar rezultatul este un pointer la obiectul referit
de valoarea-stnga. Dac tipul valorii-stnga este ... atunci tipul
rezultatului este pointer la ....
Exemplu. Fie x o variabil de tip i nt i px un pointer creat ntr-
un anumit fel (vezi capitolul 9). Atunci prin instruciunea
px = &x;
se atribuie variabilei de tip pointer la i nt px adresa variabilei x;
putem spune acum c px indic spre x. Secvena:
px = &x; y = *px;
este echivalent cu
y = x;
Operatorul &poate fi aplicat numai la variabile i la elemente de
masiv. Construcii de forma &( x+1) i &3 nu snt admise. De
asemenea nu se admite ca variabila s fie de clas r egi st er .
Operatorul unar & ajut la transmiterea argumentelor de tip
adres n funcii.
Operatorul unar - este operatorul de negativare. Operandul su
este o expresie, iar rezultatul este negativarea operandului. n acest
caz snt aplicate conversiile aritmetice obinuite. Negativarea unui
ntreg de tip unsigned se face scznd valoarea sa din 2
n
, unde n este
numrul de bii rezervai tipului i nt .
Operatorul unar ! este operatorul de negare logic. Operandul
su este o expresie, iar rezultatul su este 1 sau 0 dup cum valoarea
operandului este 0 sau diferit de zero. Tipul rezultatului este i nt .
__________________________________________________________________________
31
Acest operator este aplicabil la orice expresie de tip aritmetic sau la
pointeri.
Operatorul unar ~(tilda) este operatorul de complementare la
unu. El convertete fiecare bit 1 la 0 i invers. El este un operator
logic pe bii.
Operandul su trebuie s fie de tip ntreg. Se aplic conversiile
aritmetice obinuite.
Operatorul unar ++este operatorul de incrementare. Operandul
su este o valoare-stnga. Operatorul produce incrementarea
operandului cu 1. Acest operator prezint un aspect deosebit deoarece
el poate fi folosit ca un operator prefix (naintea variabilei: ++n) sau
ca un operator postfix (dup variabil: n++). n ambele cazuri efectul
este incrementarea lui n. Dar expresia ++n incrementeaz pe n
nainte de folosirea valorii sale, n timp ce n++incrementeaz pe n
dup ce valoarea sa a fost utilizat. Aceasta nseamn c n contextul
n care se urmrete numai incrementarea lui n, oricare construcie
poate fi folosit, dar ntr-un context n care i valoarea lui n este
folosit ++n i n++furnizeaz dou valori distincte.
Exemplu: dac n este 5, atunci
x = n++ ; atribuie lui x valoarea 5
x = ++n ; atribuie lui x valoarea 6
n ambele cazuri n devine 6.
Rezultatul operaiei nu este o valoare-stnga, dar tipul su este
tipul valorii-stnga.
Operatorul unar - - este operatorul de decrementare. Acest
operator este analog cu operatorul ++doar c produce decrementarea
cu 1 a operandului.
Operatorul ( nume-tip) este operatorul de conversie de tip. Prin
nume-tip nelegem unul dintre tipurile fundamentale admise n C.
Operandul acestui operator este o expresie. Operatorul produce
conversia valorii expresiei la tipul denumit. Aceast construcie se
numete cast.
Operatorul si zeof furnizeaz dimensiunea n octei a
operandului su. Aplicat unui masiv sau structuri, rezultatul este
__________________________________________________________________________
32
numrul total de octei din masiv sau structur. Dimensiunea se
determin n momentul compilrii, din declaraiile obiectelor din
expresie. Semantic, aceast expresie este o constant ntreag care se
poate folosi n orice loc n care se cere o constant. Cea mai frecvent
utilizare o are n comunicarea cu rutinele de alocare a memoriei sau
rutinele I/O sistem.
Operatorul si zeof poate fi aplicat i unui nume-tip ntre
paranteze. n acest caz el furnizeaz dimensiunea n octei a unui
obiect de tipul indicat.
Construcia si zeof ( nume-tip) este luat ca o unitate, astfel c
expresia
si zeof ( nume-tip) - 2
este acelai lucru cu
( si zeof ( nume-tip) ) - 2
4.3. Operatori multiplicativi
Operatorii multiplicativi * / i %snt operatori aritmetici
binari i se grupeaz de la stnga la dreapta.
Expresie-multiplicativ:
expresie * expresie
expresie / expresie
expresie %expresie
Operatorul binar * indic nmulirea. Operatorul este asociativ,
dar n expresiile n care apar mai muli operatori de nmulire, ordinea
de evaluare nu se specific. Compilatorul rearanjeaz chiar i un
calcul cu paranteze. Astfel a*( b*c) poate fi evaluat ca ( a*b) *c.
Aceasta nu implic diferene, dar dac totui se dorete o anumit
ordine, atunci se vor introduce variabile temporare.
Operatorul binar / indic mprirea. Cnd se mpart dou
numere ntregi pozitive, trunchierea se face spre zero; dac unul
dintre operanzi este negativ atunci trunchierea depinde de sistemul de
calcul.
__________________________________________________________________________
33
Operatorul binar %furnizeaz restul mpririi primei expresii la
cea de a doua. Operanzii nu pot fi de tip f l oat . Restul are totdeauna
semnul dempritului. Totdeauna ( a/ b) *b+a%b este egal cu a
(dac b este diferit de 0). Snt executate conversiile aritmetice
obinuite.
4.4. Operatori aditivi
Operatorii aditivi + i - snt operatori aritmetici binari i se
grupeaz de la stnga la dreapta. Se execut conversiile aritmetice
obinuite,
Expresie-aditiv:
expresie +expresie
expresie - expresie
Operatorul binar + produce suma operanzilor si. El este
asociativ i expresiile care conin mai muli operatori pot fi rearanjate
la fel ca n cazul operatorului de nmulire.
Operatorul binar - produce diferena operanzilor si.
4.5. Operatori de deplasare
Operatorii de deplasare << i >> snt operatori logici pe bii.
Ei se grupeaz de la stnga la dreapta.
Expresie-deplasare:
expresie << expresie
expresie >> expresie
Operatorul << produce deplasarea la stnga a operandului din
stnga cu un numr de poziii binare dat de operandul din dreapta.
Operatorul >> produce deplasarea la dreapta a operandului din
stnga cu un numr de poziii binare dat de operandul din dreapta.
n ambele cazuri se execut conversiile aritmetice obinuite
asupra operanzilor, fiecare dintre ei trebuind s fie de tip ntreg.
Operandul din dreapta este convertit la i nt ; tipul rezultatului este cel
__________________________________________________________________________
34
al operandului din stnga. Rezultatul este nedefinit dac operandul
din dreapta este negativ sau mai mare sau egal cu lungimea
obiectului, n bii. Astfel valoarea expresiei E1<<E2 este E1
(interpretat ca i configuraie de bii) deplasat la stnga cu E2
poziii bit; biii eliberai devin zero. Expresia E1>>E2 este E1
deplasat la dreapta cu E2 poziii binare. Deplasarea la dreapta este
logic (biii eliberai devin 0) dac E1 este de tip unsigned; altfel ea
este aritmetic (biii eliberai devin copii ale bitului semn).
Exemplu: x<<2 deplaseaz pe x la stnga cu 2 poziii, biii
eliberai devin 0; aceasta este echivalent cu multiplicarea lui x cu 4.
4.6. Operatori relaionali
Operatorii relaionali <, >, <=, >=se grupeaz de la stnga la
dreapta.
Expresie-relaional:
expresie <expresie
expresie >expresie
expresie <=expresie
expresie >=expresie
Operatorii <(mai mic), >(mai mare), <=(mai mic sau egal) i
>=(mai mare sau egal) produc valoarea 0 dac relaia specificat este
fals i 1 dac ea este adevrat.
Tipul rezultatului este i nt . Se execut conversiile aritmetice
obinuite. Aceti operatori au precedena mai mic dect operatorii
aritmetici, astfel c expresia i <x- 1 se consider i <( x- 1) aa dup
cum ne ateptam.
__________________________________________________________________________
35
4.7. Operatori de egalitate
Expresie-egalitate:
expresie ==expresie
expresie ! =expresie
Operatorii ==(egal cu) i ! =(diferit de) snt analogi cu operatorii
relaionali, dar precedena lor este mai mic. Astfel a<b == c<d
este 1, dac a<b i c<d au aceeai valoare de adevr.
4.8. Operatorul I pe bii
Expresie-I:
expresie &expresie
Operatorul &este operatorul I logic pe bii. El este asociativ i
expresiile care conin operatorul &pot fi rearanjate. Rezultatul este
funcia logic I pe bii aplicat operanzilor si. Operatorul se
aplic numai la operanzi de tipuri ntregi. Legea dup care
funcioneaz este:
& 0 1
0 0 0
1 0 1
Operatorul & este deseori folosit pentru a masca o anumit
mulime de bii: de exemplu:
c = n & 0177;
pune pe zero toi biii afar de ultimii 7 bii de ordin inferior ai lui n,
fr a afecta coninutul lui n.
4.9. Operatorul SAU-exclusiv pe bii
Expresie-SAU-exclusiv:
expresie ^expresie
__________________________________________________________________________
36
Operatorul ^este operatorul SAU-exclusiv logic pe bii. El este
asociativ i expresiile care-l conin pot fi rearanjate. Rezultatul este
funcia logic SAU-exclusiv pe bii aplicat operanzilor si.
Operatorul se aplic numai la operanzi de tipuri ntregi. Legea dup
care funcioneaz este:
^ 0 1
0 0 1
1 1 0
4.10. Operatorul SAU-inclusiv pe bii
Expresie-SAU-inclusiv:
expresie | expresie
Operatorul | este operatorul SAU-inclusiv logic pe bii. El este
asociativ i expresiile care-l conin pot fi rearanjate. Rezultatul este
funcia logic SAU-inclusiv pe bii aplicat operanzilor si.
Operatorul se aplic numai la operanzi de tipuri ntregi. Legea dup
care funcioneaz este:
| 0 1
0 0 1
1 1 1
Operatorul | este folosit pentru a poziiona bii; de exemplu:
x = x | MASK;
pune pe 1 toi biii din x care corespund la bii poziionai pe 1 din
MASK. Se efectueaz conversiile aritmetice obinuite.
4.11. Operatorul I-logic
Expresie-I-logic:
expresie &&expresie
Operatorul &&este operatorul I-logic i el se grupeaz de la
stnga la dreapta. Rezultatul este 1 dac ambii operanzi snt diferii de
__________________________________________________________________________
37
zero i 0 n rest. Spre deosebire de operatorul I pe bii &, operatorul
I-logic &&garanteaz o evaluare de la stnga la dreapta; mai mult, al
doilea operand nu este evaluat dac primul operand este 0.
Operanzii nu trebuie s aib n mod obligatoriu acelai tip, dar
fiecare trebuie s aib unul dintre tipurile fundamentale sau pointer.
Rezultatul este totdeauna de tip i nt .
4.12. Operatorul SAU-logic
Expresie-SAU-logic:
expresie | | expresie
Operatorul | | este operatorul SAU-logic i el se grupeaz de la
stnga la dreapta. Rezultatul este 1 dac cel puin unul dintre operanzi
este diferit de zero i 0 n rest.
Spre deosebire de operatorul SAU-inclusiv pe bii | , operatorul
SAU-logic | | garanteaz o evaluare de la stnga la dreapta; mai mult,
al doilea operand nu este evaluat dac valoarea primului operand este
diferit de zero.
Operanzii nu trebuie s aib n mod obligatoriu acelai tip, dar
fiecare trebuie s aib unul dintre tipurile fundamentale sau pointer.
Rezultatul este totdeauna de tip i nt .
4.13. Operatorul condiional
Expresie-condiional:
expresie ? expresie : expresie
Operatorul condiional ? este un operator ternar. Prima expresie
se evalueaz i dac ea este diferit de zero sau adevrat, rezultatul
este valoarea celei de-a doua expresii, altfel rezultatul este valoarea
expresiei a treia. De exemplu expresia:
z = ( a>b) ? a : b;
calculeaz maximul dintre a i b i l atribuie lui z. Se evalueaz mai
nti prima expresie a>b. Dac ea este adevrat se evalueaz a doua
expresie i valoarea ei este rezultatul operaiei, aceast valoare se
__________________________________________________________________________
38
atribuie lui z. Dac prima expresie nu este adevrat atunci z ia
valoarea lui b.
Expresia condiional poate fi folosit peste tot unde sintaxa cere
o expresie.
Dac este posibil, se execut conversiile aritmetice obinuite
pentru a aduce expresia a doua i a treia la un tip comun; dac ambele
expresii snt pointeri de acelai tip, rezultatul are i el acelai tip; dac
numai o expresie este un pointer, cealalt trebuie sa fie constanta 0,
iar rezultatul este de tipul pointerului. ntotdeauna numai una dintre
expresiile a doua i a treia este evaluat.
Dac f este flotant i n ntreg, atunci expresia
( h>0) ? f : n
este de tip double indiferent dac n este pozitiv sau negativ.
Parantezele nu snt necesare deoarece precedena operatorului ?:
este mai mic, dar ele pot fi folosite pentru a face expresia
condiional mai vizibil.
4.14. Operatori de atribuire
Exist mai muli operatori de atribuire, care se grupeaz toi de la
dreapta la stnga. Operandul stng este o valoare-stnga, operandul
drept este o expresie. Tipul expresiei de atribuire este tipul
operandului stng.
Rezultatul este valoarea memorat n operandul stng dup ce
atribuirea a avut loc. Cele dou pri ale unui operator de atribuire
compus snt uniti lexicale (simboluri) distincte.
Expresie-atribuire:
valoare-stnga =expresie
valoare-stnga op=expresie
unde op poate fi unul din operatorii +, - , *, / , %, <<, >>, &, ^, | .
ntr-o atribuire simpl cu =, valoarea expresiei nlocuiete pe cea
a obiectului referit de valoare-stnga. Dac ambii operanzi au tip
aritmetic, atunci operandul drept este convertit la tipul operandului
stng nainte de atribuire.
__________________________________________________________________________
39
Expresiile de forma E1 op= E2 se interpreteaz ca fiind
echivalente cu expresiile de forma E1 =E1 op E2; totui E1 este
evaluat o singur dat.
Exemplu: expresia x *= y+1 este echivalent cu
x = x * ( y+1) i nu cu x = x * y + 1
Pentru operatorii +=i - =, operandul stng poate fi i un pointer,
n care caz operandul din dreapta este convertit la ntreg (vezi
capitolul 9). Toi operanzii din dreapta i toi operanzii din stnga
care nu snt pointeri trebuie s fie de tip aritmetic.
Atribuirea prescurtat este avantajoas n cazul cnd n membrul
stng avem expresii complicate, deoarece ele se evalueaz o singur
dat.
4.15. Operatorul virgul
Expresie-virgul:
expresie , expresie
O pereche de expresii separate prin virgul se evalueaz de la
stnga la dreapta i valoarea expresiei din stnga se neglijeaz. Tipul
i valoarea rezultatului snt cele ale operandului din dreapta. Aceti
operatori se grupeaz de la stnga la dreapta. n contextele n care
virgula are un sens special, (de exemplu ntr-o list de argumente
reale ale unei funcii i lista de iniializare), operatorul virgul descris
aici poate aprea numai n paranteze. De exemplu funcia:
f ( a, ( t =3, t +2) , c)
are trei argumente, dintre care al doilea are valoarea 5. Expresia
acestui argument este o expresie virgul. n calculul valorii lui se
evalueaz nti expresia din stnga i se obine valoarea 3 pentru t ,
apoi cu aceast valoare se evalueaz a doua expresie i se obine t =5.
Prima valoare a lui t se pierde.
__________________________________________________________________________
40
4.16. Precedena i ordinea de evaluare
Tabelul de la sfritul acestei seciuni constituie un rezumat al
regulilor de preceden i asociativitate ale tuturor operatorilor.
Operatorii din aceeai linie au aceeai preceden; liniile snt
scrise n ordinea descresctoare a precedenei, astfel de exemplu
operatorii *, / i %au toi aceeai preceden, care este mai mare
dect aceea a operatorilor +i - .
Dup cum s-a menionat deja, expresiile care conin unul dintre
operatorii asociativi sau comutativi (*, +, &, ^, | ) pot fi rearanjate de
compilator chiar dac conin paranteze. n cele mai multe cazuri
aceasta nu produce nici o diferen; n cazurile n care o asemenea
diferen ar putea aprea pot fi utilizate variabile temporare explicite,
pentru a fora ordinea de evaluare.
Limbajul C, ca i multe alte limbaje, nu specific n ce ordine snt
evaluai operanzii unui operator. De exemplu ntr-o instruciune de
forma:
x = f ( ) + g( ) ;
f poate fi evaluat nainte sau dup evaluarea lui g; dac f sau g
altereaz o variabil extern de care cealalt depinde, x poate
depinde de ordinea de evaluare. Din nou rezultate intermediare
trebuie memorate n variabile temporare pentru a asigura o secven
particular.
__________________________________________________________________________
41
Operator Asociativitate
( ) [ ] - > . stnga la dreapta
! ++ - - - ( tip) * & si zeof dreapta la stnga
* / % stnga la dreapta
+ - stnga la dreapta
<< >> stnga la dreapta
< <= > >= stnga la dreapta
== ! = stnga la dreapta
& stnga la dreapta
^ stnga la dreapta
| stnga la dreapta
&& stnga la dreapta
| | stnga la dreapta
?: dreapta la stnga
= op= dreapta la stnga
, stnga la dreapta
__________________________________________________________________________
42
5. Declaraii
Declaraiile se folosesc pentru a specifica interpretarea pe care
compilatorul trebuie s o dea fiecrui identificator.
Declaraie:
specificator-declaraie lista-declarator<opt>;
Specificator-declaraie:
specificator-tip specificator-declaraie<opt>
specificator-clas-memorie specificator-declaraie<opt>
Specificator-tip:
char
shor t
i nt
l ong
unsi gned
f l oat
doubl e
voi d
specificator-structur-sau-reuniune
t ypedef -nume
Specificator-clas-memorie:
aut o
st at i c
ext er n
r egi st er
const
t ypedef
Lista-declarator:
declarator-cu-iniializare
declarator-cu-iniializare, lista-declarator
Declarator-cu-iniializare:
declarator iniializator<opt>
Declarator:
__________________________________________________________________________
43
identificator
( declarator)
* declarator
declarator ( )
declarator [ expresie-constant<opt>]
Declaraiile listeaz toate variabilele care urmeaz a fi folosite
ntr-un program. O declaraie specific tipul, clasa de memorie i
eventual valorile iniiale pentru una sau mai multe variabile de
acelai tip. Declaraiile se fac sau n afara oricrei funcii sau la
nceputul unei funcii naintea oricrei instruciuni.
Nu orice declaraie rezerv i memorie pentru un anumit
identificator, de aceea deosebim:
declaraia de definiie a unei variabile, care se refer la locul
unde este creat variabila i unde i se aloc memorie;
declaraia de utilizare a unei variabile, care se refer la locul
unde numele variabilei este declarat pentru a anuna proprietile
variabilei care urmeaz a fi folosit.
5.1. Specificatori de clas de memorie
Specificatorii de clas de memorie snt:
aut o st at i c ext er n r egi st er
const t ypedef
Specificatorul t ypedef nu rezerv memorie i este denumit
specificator de clas de memorie numai din motive sintactice; el va
fi discutat n capitolul 10.
Semnificaia diferitelor clase de memorie a fost discutat deja n
paragraful 3.1.
Declaraiile cu specificatorii aut o, st at i c i r egi st er
determin i rezervarea unei zone de memorie corespunztoare.
Declaraia cu specificatorul ext er n presupune o definiie extern
pentru identificatorii dai, undeva n afara funciei sau fiierului n
care ei snt declarai.
__________________________________________________________________________
44
ntr-o declaraie poate s apar cel mult un specificator de clas
de memorie. Dac specificatorul de clas lipsete din declaraie, el se
consider implicit aut o n interiorul unei funcii i definiie extern n
afara funciei. Excepie fac funciile care nu snt niciodat
automatice. De exemplu liniile:
i nt sp;
doubl e val [ MAXVAL] ;
care apar ntr-un program n afara oricrei funcii, definesc
variabilele externe sp de tip i nt i val de tip masiv de doubl e.
Ele determin alocarea memoriei i servesc de asemenea ca declaraii
ale acestor variabile n tot restul fiierului surs. Pe de alt parte
liniile:
ext er n i nt sp;
ext er n doubl e val [ ] ;
declar pentru restul fiierului surs c variabilele sp i val snt
externe, sp este de tip i nt i val este un masiv de doubl e i c
ele au fost definite n alt parte, unde li s-a alocat i memorie. Deci
aceste declaraii nu creeaz aceste variabile i nici nu le aloc
memorie.
5.2. Specificatori de tip
Specificatorii de tip snt:
char shor t i nt l ong unsi gned
f l oat doubl e voi d
specificator-structur-sau-reuniune
t ypedef -nume
Cuvintele l ong, shor t i unsi gned pot fi considerate i ca
adjective; urmtoarele combinaii snt acceptate:
shor t i nt
l ong i nt
unsi gned i nt
unsi gned l ong i nt
l ong doubl e
__________________________________________________________________________
45
ntr-o declaraie se admite cel mult un specificator de tip, cu
excepia combinaiilor amintite mai sus. Dac specificatorul de tip
lipsete din declaraie, el se consider implicit i nt .
Specificatorii de structuri i reuniuni snt prezentai n seciunea
10.9, iar declaraiile cu t ypedef n seciunea 10.10.
5.3. Declaratori
Lista-declarator care apare ntr-o declaraie este o succesiune de
declaratori separai prin virgule, fiecare dintre ei putnd avea un
iniializator.
Declaratorii din lista-declarator snt identificatorii care trebuie
declarai.
Fiecare declarator este considerat ca o afirmaie care, atunci cnd
apare o construcie de aceeai form cu declaratorul, produce un
obiect de tipul i de clasa de memorie indicat. Fiecare declarator
conine un singur identificator. Gruparea declaratorilor este la fel ca
i la expresii.
Dac declaratorul este un identificator simplu, atunci el are tipul
indicat de specificatorul din declaraie.
Un declarator ntre paranteze este tot un declarator, dar legtura
declaratorilor compleci poate fi alterat de paranteze.
S considerm acum o declaraie de forma:
T D1
unde T este un specificator de tip (ca de exemplu i nt ) i D1 un
declarator. S presupunem c aceast declaraie face ca
identificatorul s aib tipul ...T unde ... este vid dac D1 este un
identificator simplu (aa cum tipul lui x n int x este i nt ). Dac D1
are forma:
*D
atunci tipul identificatorului pe care-l conine acest declarator este
pointer la T.
Dac D1 are forma:
D( )
atunci identificatorul pe care-l conine are tipul funcie care
returneaz T.
__________________________________________________________________________
46
Dac D1 are forma:
D[ expresie-constant] sau D[ ]
atunci identificatorul pe care-l conine are tipul masiv de T.
n primul caz expresia constant este o expresie a crei valoare
este determinabil la compilare i al crei tip este i nt . Cnd mai
muli identificatori masiv de T snt adiaceni, se creeaz un masiv
multidimensional; expresiile constante care specific marginile
masivelor pot lipsi numai pentru primul membru din secven.
Aceast omisiune este util cnd masivul este extern i definiia real
care aloc memoria este n alt parte (vezi seciunea 5.1). Prima
expresie constant poate lipsi de asemenea cnd declaratorul este
urmat de iniializare. n acest caz dimensiunea este calculat la
compilare din numrul elementelor iniiale furnizate.
Un masiv poate fi construit din obiecte de unul dintre tipurile de
baz, din pointeri, din reuniuni sau structuri, sau din alte masive
(pentru a genera un masiv multidimensional).
Nu toate posibilitile admise de sintaxa de mai sus snt permise.
Restriciile snt urmtoarele: funciile nu pot returna masive,
structuri, reuniuni sau funcii, dei ele pot returna pointeri la astfel de
obiecte; nu exist masive de funcii, dar pot fi masive de pointeri la
funcii. De asemenea, o structur sau reuniune nu poate conine o
funcie, dar ea poate conine un pointer la funcie. De exemplu,
declaraia
i nt i , *i p, f ( ) , *f i p( ) , ( *pf i ) ( ) ;
declar un ntreg i , un pointer i p la un ntreg, o funcie f care
returneaz un ntreg, o funcie f i p care returneaz un pointer la un
ntreg, un pointer pf i la o funcie care returneaz un ntreg. Prezint
interes compararea ultimilor doi declaratori.
Construcia *f i p( ) este *( f i p( ) ) , astfel c declaraia
sugereaz apelul funciei f i p i apoi utiliznd indirectarea prin
intermediul pointerului se obine un ntreg.
n declaratorul ( *pf i ) ( ) , parantezele externe snt necesare
pentru arta c indirectarea printr-un pointer la o funcie furnizeaz o
funcie, care este apoi apelat; ea returneaz un ntreg.
__________________________________________________________________________
47
Declaraiile de variabile pot fi explicite sau implicite prin
context. De exemplu declaraiile:
i nt a, b, c;
char d, m[ 100] ;
specific un tip i o list de variabile. Aici clasa de memorie nu este
declarat explicit, ea se deduce din context. Dac declaraia este
fcut n afara oricrei funcii atunci clasa de memorie este ext er n;
dac declaraia este fcut n interiorul unei funcii atunci implicit
clasa de memorie este aut o.
Variabilele pot fi distribuite n declaraii n orice mod; astfel
listele le mai sus pot fi scrise i sub forma:
i nt a;
i nt b;
i nt c;
char d;
char m[ 100] ;
Aceasta ultim form ocup mai mult spaiu dar este mai
convenabil pentru adugarea unui comentariu pentru fiecare
declaraie sau pentru modificri ulterioare.
5.4. Modificatorul const
Valoarea unei variabile declarate cu acest modificator nu poate fi
modificat.
Sintaxa:
const nume-variabil =valoare ;
nume-funcie ( ..., const tip *nume-variabil, ...)
n prima variant, modificatorul atribuie o valoare iniial unei
variabile care nu mai poate fi ulterior modificat de program. De
exemplu,
const i nt vi r st a = 39;
Orice atribuire pentru variabila vi r st a va genera o eroare de
compilare.
Atenie! O variabil declarat cu const poate fi indirect
modificat prin intermediul unui pointer:
i nt *p = &vi r st a;
__________________________________________________________________________
48
*p = 35;
n a doua variant modificatorul const este folosit mpreun
cu un parametru pointer ntr-o list de parametri ai unei funcii.
Funcia nu poate modifica variabila pe care o indic pointerul:
i nt pr i nt f ( const char *f or mat , . . . ) ;
5.5. Iniializare
Un declarator poate specifica o valoare iniial pentru
identificatorul care se declar. Iniializatorul este precedat de semnul
=i const dintr-o expresie sau o list de valori incluse n acolade.
Iniializator:
expresie
{list-iniializare}
List-iniializare:
expresie
list-iniializare, list-iniializare
{list-iniializare}
Toate expresiile dintr-un iniializator pentru variabile statice sau
externe trebuie s fie expresii constante (vezi seciunea 3.4) sau
expresii care se reduc la adresa unei variabile declarate anterior,
posibil offset-ul unei expresii constante. Variabilele de clas auto sau
register pot fi iniializate cu expresii oarecare, nu neaprat expresii
constante, care implic constante sau variabile declarate anterior sau
chiar funcii.
n absena iniializrii explicite, variabilele statice i externe snt
iniializate implicit cu valoarea 0. Variabilele auto i register au
valori iniiale nedefinite (reziduale).
Pentru variabilele statice i externe, iniializarea se face o singur
dat, n principiu nainte ca programul s nceap s se execute.
Pentru variabilele aut o i r egi st er , iniializarea este fcut
la fiecare intrare n funcie sau bloc.
Dac un iniializator se aplic unui scalar (un pointer sau un
obiect de tip aritmetic) el const dintr-o singur expresie, eventual n
__________________________________________________________________________
49
acolade. Valoarea iniial a obiectului este luat din expresie; se
efectueaz aceleai operaii ca n cazul atribuirii.
Pentru iniializarea masivelor i masivelor de pointeri vezi
seciunea 9.8. Pentru iniializarea structurilor vezi seciunea 10.3.
Dac masivul sau structura conine sub-masive sau sub-structuri
regula de iniializare se aplic recursiv la membrii masivului sau
structuri.
5.6. Nume-tip
n cele expuse mai sus furnizarea unui nume-tip a fost necesar n
dou contexte:
pentru a specifica conversii explicite de tip prin intermediul unui
cast (vezi seciunea 3.4);
ca argument al lui si zeof (vezi seciunea 4.2).
Un nume-tip este n esen o declaraie pentru un obiect de acest
tip, dar care omite numele obiectului.
Nume-tip:
specificator-tip declarator-abstract
Declarator-abstract:
vid
( declarator-abstract)
*declarator-abstract
declarator-abstract( )
declarator-abstract[ expresie-constant<opt>]
Pentru a evita ambiguitatea, n construcia:
( declarator-abstract)
declaratorul abstract se presupune a nu fi vid. Cu aceast restricie,
este posibil s identificm n mod unic locul ntr-un declarator-
abstract, unde ar putea aprea un identificator, dac aceast
construcie a fost un declarator ntr-o declaraie. Atunci tipul denumit
este acelai ca i tipul identificatorului ipotetic. De exemplu:
i nt
i nt *
__________________________________________________________________________
50
i nt *[ 3]
i nt ( *) [ 3]
i nt *( )
i nt ( *) ( )
denumete respectiv tipurile i nt , pointer la ntreg, masiv de 3
pointeri la ntregi, pointer la un masiv de 3 ntregi, funcie care
returneaz pointer la ntreg i pointer la o funcie care returneaz
ntreg.
__________________________________________________________________________
51
6. Instruciuni
ntr-un program scris n limbajul C instruciunile se execut
secvenial, n afar de cazul n care se indic altfel.
Instruciunile pot fi scrise cte una pe o linie pentru o lizibilitate
mai bun, dar nu este obligatoriu.
6.1. Instruciunea expresie
Cele mai multe instruciuni snt instruciuni expresie. O expresie
devine instruciune dac ea este urmat de punct i virgul.
Format:
expresie;
De obicei instruciunile expresie snt atribuiri sau apeluri de
funcie; de exemplu:
x = 0;
pr i nt f ( . . . ) ;
n limbajul C punct i virgula este un terminator de instruciune
i este obligatoriu.
6.2. Instruciunea compus sau blocul
Instruciunea compus este o grupare de declaraii i instruciuni
nchise ntre acolade. Ele au fost introduse cu scopul de a folosi mai
multe instruciuni acolo unde sintaxa cere o instruciune.
Instruciunea compus sau blocul snt echivalente sintactic cu o
singur instruciune.
Format:
Instruciune-compus:
{ list-declaratori<opt> list-instruciuni<opt> }
List-declaratori:
declaraie
declaraie list-declaratori
__________________________________________________________________________
52
List-instruciuni:
instruciune
instruciune list-instruciuni
Dac anumii identificatori din lista-declaratori au fost declarai
anterior, atunci declaraia exterioar este salvat pe durata blocului,
dup care i reia sensul su.
Orice iniializare pentru variabile aut o i r egi st er se
efectueaz la fiecare intrare n bloc. Iniializrile pentru variabilele
st at i c se execut numai o singur dat cnd programul ncepe s
se execute.
Un bloc se termin cu o acolad dreapt care nu este urmat
niciodat de punct i virgul.
6.3. Instruciunea condiional
Sintaxa instruciunii condiionale admite dou formate:
i f ( expresie)
instruciune-1
i f ( expresie)
instruciune-1
el se instruciune-2
Instruciunea condiional se folosete pentru a lua decizii. n
ambele cazuri se evalueaz expresia i dac ea este adevrat (deci
diferit de zero) se execut instruciune-1. Dac expresia este fals
(are valoarea zero) i instruciunea if are i parte de else atunci se
execut instruciune-2.
Una i numai una dintre cele dou instruciuni se execut.
Deoarece un i f testeaz pur i simplu valoarea numeric a unei
expresii, se admite o prescurtare i anume:
i f ( expresie)
n loc de:
i f ( expresie ! =0)
Deoarece partea el se a unei instruciuni i f este opional,
exist o ambiguitate cnd un el se este omis dintr-o secven de i f
__________________________________________________________________________
53
imbricat. Aceasta se rezolv asociind el se cu ultimul i f care nu
are el se.
Exemplu:
i f ( n>0)
i f ( a>b)
z = a;
el se
z = b;
Partea el se aparine i f -ului din interior. Dac nu dorim acest
lucru atunci folosim acoladele pentru a fora asocierea:
i f ( n>0) {
i f ( a>b)
z = a;
}
el se
z = b;
Instruciunea condiional admite i construcia else-if de forma:
i f ( expresie-1)
instruciune-1
el se i f ( expresie-2)
instruciune-2
el se i f ( expresie-3)
instruciune-3
el se
instruciune-4
Aceast secven de i f se folosete frecvent n programe, ca
mod de a exprima o decizie multipl.
Expresiile se evalueaz n ordinea n care apar; dac se ntlnete
o expresie adevrat, atunci se execut instruciunea asociat cu ea i
astfel se termin ntregul lan.
Oricare instruciune poate fi o instruciune simpl sau un grup de
instruciuni ntre acolade.
__________________________________________________________________________
54
` Instruciunea dup ultimul el se se execut n cazul n care nici
o expresie nu a fost adevrat.
Dac n acest caz nu exist nici o aciune explicit de fcut,
atunci partea
el se instruciune-4
poate s lipseasc.
Funcia bi nar y din seciunea 7.5 este un exemplu de decizie
multipl de ordinul 3.
Pot exista un numr arbitrar de construcii:
el se i f ( expresie)
instruciune
grupate ntre un i f iniial i un el se final.
ntotdeauna un el se se leag cu ultimul i f ntlnit.
6.4. Instruciunea while
Format:
whi l e ( expresie)
instruciune
Instruciunea se execut repetat atta timp ct valoarea expresiei
este diferit de zero. Testul are loc naintea fiecrei execuii a
instruciunii. Prin urmare ciclul este urmtorul: se testeaz condiia
din paranteze dac ea este adevrat, deci expresia din paranteze are
o valoare diferit de zero, se execut corpul instruciunii whi l e, se
verific din nou condiia, dac ea este adevrat se execut din nou
corpul instruciunii. Cnd condiia devine fals, adic valoarea
expresiei din paranteze este zero, se face un salt la instruciunea de
dup corpul instruciunii whi l e, deci instruciunea whi l e se
termin.
6.5. Instruciunea do
Format:
do instruciune whi l e
( expresie) ;
__________________________________________________________________________
55
Instruciunea se execut repetat pn cnd valoarea expresiei
devine zero. Testul are loc dup fiecare execuie a instruciunii.
6.6. Instruciunea for
Format:
f or ( expresie-1<opt>; expresie-2<opt>; expresie-3<opt>)
instruciune
Aceast instruciune este echivalent cu:
expresie-1;
whi l e ( expresie-2) {
instruciune;
expresie-3;
}
Expresie-1 constituie iniializarea ciclului i se execut o singur
dat naintea ciclului. Expresie-2 specific testul care controleaz
ciclul. El se execut naintea fiecrei iteraii. Dac condiia din test
este adevrat atunci se execut corpul ciclului, dup care se execut
expresie-3, care const de cele mai multe ori n modificarea valorii
variabilei de control al ciclului. Se revine apoi la reevaluarea
condiiei. Ciclul se termin cnd condiia devine fals.
Oricare dintre expresiile instruciunii for sau chiar toate pot lipsi.
Dac lipsete expresie-2, aceasta implic faptul c clauza
whi l e este echivalent cu while (1), ceea ce nseamn o condiie
totdeauna adevrat. Alte omisiuni de expresii snt pur i simplu
eliminate din expandarea de mai sus.
Instruciunile whi l e i f or permit un lucru demn de
observat i anume, ele execut testul de control la nceputul ciclului
i naintea intrrii n corpul instruciunii.
Dac nu este nimic de fcut, nu se face nimic, cu riscul de a nu
intra niciodat n corpul instruciunii.
__________________________________________________________________________
56
6.7. Instruciunea switch
Instruciunea swi t ch este o decizie multipl special i
determin transferul controlului unei instruciuni sau unui bloc de
instruciuni dintr-un ir de instruciuni n funcie de valoarea unei
expresii.
Format:
swi t ch ( expresie) instruciune
Expresia este supus la conversiile aritmetice obinuite dar
rezultatul evalurii trebuie s fie de tip i nt .
Fiecare instruciune din corpul instruciunii switch poate fi
etichetat cu una sau mai multe prefixe case astfel:
case expresie-constant:
unde expresie-constant trebuie s fie de tip i nt .
Poate exista de asemenea cel mult o instruciune etichetat cu
def aul t :
Cnd o instruciune swi t ch se execut, se evalueaz expresia
din paranteze i valoarea ei se compar cu fiecare constant din
fiecare case.
Dac se gsete o constant case egal cu valoarea expresiei,
atunci se execut instruciunea care urmeaz dup case-ul respectiv.
Dac nici o constant case nu este egal cu valoarea expresiei i
dac exist un prefix def aul t , atunci se execut instruciunea de
dup el, altfel nici o instruciune din swi t ch nu se execut.
Prefixele case i def aul t nu altereaz fluxul de control, care
continu printre astfel de prefixe.
Pentru ieirea din swi t ch se folosete instruciunea br eak
(vezi seciunea 6.8) sau r et ur n (vezi seciunea 6.10).
De obicei instruciunea care constituie corpul unui swi t ch este
o instruciune compus. La nceputul acestei instruciuni pot aprea i
declaraii, dar iniializarea variabilelor automatice i registru este
inefectiv.
na = nb = nc = 0;
whi l e ( c=s[ i ++] )
__________________________________________________________________________
57
swi t ch ( c) {
case ' 0' :
case ' 1' :
case ' 2' :
case ' 3' :
case ' 4' :
case ' 5' :
case ' 6' :
case ' T' :
case ' 8' :
case ' 9' :
nc[ c- ' 0' ] ++;
br eak;
case ' ' :
case ' \ r ' :
case ' \ t ' :
nb++;
br eak;
def aul t :
na++;
br eak;
}
pr i nt f ( "ci f r e: " ) ;
f or ( i =0; i <10; i ++)
pr i nt f ( " %d" , nc[ i ] ) ;
pr i nt f ( "\ nspat i i al be: %d, al t el e: %d\ n" ,
nb, na) ;
n acest exemplu se parcurg toate caracterele dintr-un ir, se
numr cifrele, spaiile albe i alte caractere i se afieaz aceste
numere nsoite de comentarii.
Instruciunea whi l e este cea care asigur parcurgerea irului
pn la sfrit. Pentru fiecare caracter se execut corpul instruciunii
whi l e care const dintr-o singur instruciune swi t ch.
Se evalueaz expresia ntreag din paranteze (n cazul nostru
caracterul c) i se compar valoarea sa cu toate constantele-case. n
__________________________________________________________________________
58
momentul cnd avem egalitate se ncepe execuia de la case-ul
respectiv.
Afiarea rezultatelor se face prin intermediul instruciunii f or i
a funciei pr i nt f (vezi capitolul 11).
6.8. Instruciunea break
Format:
br eak;
Aceast instruciune determin terminarea celei mai interioare
instruciuni whi l e, do, f or sau swi t ch care o conine. Controlul
trece la instruciunea care urmeaz dup instruciunea astfel
terminat.
6.9. Instruciunea continue
Format:
cont i nue;
Aceast instruciune determin trecerea controlului la poriunea
de continuare a ciclului celei mai interioare instruciuni whi l e, do
sau f or care o conine, adic la sfritul ciclului i reluarea
urmtoarei iteraii a ciclului. n whi l e i do se continu cu testul,
iar n f or se continu cu expresie-3.
Mai precis n fiecare dintre instruciunile:
whi l e ( . . . ) {
. . .
cont i n: ;
}
f or ( . . . ) {
. . .
cont i n: ;
}
do {
. . .
cont i n: ;
} whi l e ( . . . ) ;
dac apare o instruciune cont i nue aceasta este echivalent cu un
salt la eticheta cont i n. Dup cont i n: urmeaz o instruciune
vid (vezi seciunea 6.11).
Poriunea de program din exemplul urmtor prelucreaz numai
elementele pozitive ale unui masiv.
__________________________________________________________________________
59
f or ( i =0; i <n; i ++) {
i f ( a[ i ] <0) /* sare peste elementele negative */
cont i nue;
. . . /* prelucreaz elementele pozitive */
}
6.10. Instruciunea return
O instruciune r et ur n permite ieirea dintr-o funcie i
transmiterea controlului apelantului funciei. O funcie poate returna
valori apelantului su, prin intermediul unei instruciuni r et ur n.
Formate:
r et ur n;
r et ur n expresie;
n primul caz valoarea returnat nu este definit. n al doilea caz
valoarea expresiei este returnat apelantului funciei. Dac se cere,
expresia este convertit, ca ntr-o atribuire, la tipul funciei n care ea
apare.
6.11. Instruciunea vid
Format:
;
Instruciunea vid este util pentru a introduce o etichet naintea
unei acolade drepte, ntr-o instruciune compus, sau pentru a
introduce un corp nul ntr-o instruciune de ciclare care cere corp al
instruciunii, ca de exemplu whi l e sau f or .
Exemplu:
f or ( nc=0; s[ nc] ! =0; ++nc) ;
Aceast instruciune numr caracterele unui ir. Corpul lui f or
este vid, deoarece tot lucrul se face n partea de test i actualizare dar
sintaxa lui f or cere un corp al instruciunii. Instruciunea vid
satisface acest lucru.
__________________________________________________________________________
60
7. Funciile i structura unui program
Funciile snt elementele de baz ale unui program scris n C;
orice program, de orice dimensiune, const din una sau mai multe
funcii, care specific operaiile care trebuie efectuate.
O funcie ofer un mod convenabil de ncapsulare a anumitor
calcule ntr-o cutie neagr care poate fi utilizat apoi fr a avea grija
coninutului ei. Funciile snt ntr-adevr singurul mod de a face fa
complexitii programelor mari. Funciile permit desfacerea
programelor mari n unele mici i dau utilizatorului posibilitatea de a
dezvolta programe, folosind ceea ce alii au fcut deja n loc s o ia
de la nceput.
Limbajul C a fost conceput s permit definirea de funcii
eficiente i uor de mnuit. n general e bine s concepem programe
constituite din mai multe funcii mici dect din puine funcii de
dimensiuni mari. Un program poate fi mprit n mai multe fiiere
surs n mod convenabil, iar fiierele surs pot fi compilate separat.
Mai precis, un program C const dintr-o secven de definiii
externe. Acestea snt definiii de funcii i de date. Sintaxa definiiilor
externe este aceeai ca i a tuturor declaraiilor.
7.1. Definiia funciilor
Sintaxa definiiei unei funcii este urmtoarea:
Definiia-funciei:
tip-funcie<opt> nume-funcie( lista-parametri) corp-funcie
Lista-parametri:
declaraie-parametru
declaraie-parametru, lista-parametri
Corpul-funciei:
instruciune-compus
Dintre specificatorii de clas de memorie numai ext er n i
st at i c snt admii. Un declarator de funcie este similar cu un
__________________________________________________________________________
61
declarator pentru funcie care returneaz ... cu excepia c el
listeaz parametrii formali ai funciei care se definete.
Dup cum se observ, diferitele pri pot s lipseasc; o funcie
minim este:
dummy( ) {}
funcia care nu face nimic. Aceast funcie poate fi util n programe,
innd locul unei alte funcii n dezvoltarea ulterioar a programului.
Numele funciei poate fi precedat de un tip, dac funcia
returneaz altceva dect o valoare ntreag (vezi seciunea 7.2).
Numele funciei poate fi de asemenea precedat de clasa de
memorie ext er n sau st at i c. Dac nici un specificator de clas de
memorie nu este prezent, atunci funcia este automat declarat
extern, deoarece n C nu se admite ca o funcie s fie definit n
interiorul altei funcii.
Declaraia unei funcii se face implicit la apariia ei. Se
interpreteaz ca funcie orice identificator nedeclarat anterior i urmat
de o parantez ( . Pentru a evita surprize neplcute datorit
neconcordanei dintre tipurile de parametri aceast practic nu este
recomandat.
Dm mai jos un exemplu de funcie complet definit:
i nt max( i nt a, i nt b, i nt c) {
i nt m;
m= ( a>b) ? a : b;
r et ur n ( m>c) ? m: c;
}
Aceast funcie determin maximul dintre 3 numere date:
i nt este un specificator de tip care indic faptul c funcia max
returneaz o valoare ntreag;
max( i nt a, i nt b, i nt c) este declaraia funciei i a
parametrilor formali;
{ . . . } este corpul funciei.
S ilustrm mecanismul definirii unei funcii scriind funcia
putere power ( m, n) care ridic un ntreg m la o putere ntreag
pozitiv n. Astfel valoarea lui power ( 2, 5) este 32. Aceast funcie
__________________________________________________________________________
62
desigur nu este complet, deoarece calculeaz numai puteri pozitive
de ntregi mici.
Prezentm mai jos funcia power i un program principal care
o apeleaz, pentru a putea vedea i structura unui program.
power ( i nt x, i nt n) {
i nt i , p;
p = 1;
f or ( i =1; i <=n; ++i )
p = p * x;
r et ur n p;
}
mai n( ) {
i nt i ;
f or ( i =0; i <10; ++i )
pr i nt f ( " %d %d\ n" , i , power ( 2, i ) ,
power ( - 3, i ) ) ;
}
Funcia power este apelat n programul principal de dou ori.
Fiecare apel transmite funciei dou argumente.
Rezultatul este afiat de funcia de bibliotec pr i nt f (vezi
capitolul 11).
Numele funciei este urmat obligatoriu de paranteze, chiar dac
lista parametrilor este vid.
n general numele funciei este ales dup dorina utilizatorului.
Totui n fiecare program trebuie s existe o funcie cu numele impus
mai n; orice program i ncepe execuia cu funcia mai n. Celelalte
funcii snt apelate din interiorul funciei mai n. Unele dintre funciile
apelate snt definite n acelai program, altele snt coninute ntr-o
bibliotec de funcii.
Comunicarea ntre funcii se face prin argumente i valori
returnate de funcii. Comunicarea ntre funcii poate fi fcut i prin
intermediul variabilelor externe.
O funcie poate fi declarat i st at i c. n acest caz ea poate fi
apelat numai din fiierul unde a fost definit.
__________________________________________________________________________
63
Funciile n C pot fi folosite recursiv, deci o funcie se poate
apela pe ea nsi fie direct, fie indirect.
n general ns recursivitatea nu face economie de memorie,
deoarece trebuie meninut o stiv cu valorile de prelucrat.
7.2. Apelul funciilor
n limbajul C orice funcie este apelat prin numele ei, urmat de
lista real a argumentelor, nchis ntre paranteze.
Dac ntr-o expresie numele funciei nu este urmat imediat de o
parantez ( , adic funcia nu apare pe poziia de apel de funcie
atunci nu se realizeaz imediat apelul funciei respective ci se
genereaz un pointer la funcie (vezi seciunea 9.11).
7.3. Revenirea din funcii
Revenirea dintr-o funcie se face prin intermediul unei
instruciuni r et ur n.
Valoarea pe care o funcie o calculeaz poate fi returnat prin
instruciunea r et ur n, care dup cum am vzut are dou formate:
r et ur n;
r et ur n expresie;
Ca argument poate aprea orice expresie admis n C. Funcia
returneaz valoarea acestei expresii funciei apelante.
O funcie nu returneaz n mod obligatoriu o valoare. O
instruciune r et ur n, fr expresie ca parametru, cauzeaz numai
transferul controlului funciei apelante nu i o valoare util.
La rndul su funcia apelant poate ignora valoarea returnat.
De obicei o funcie returneaz o valoare de tip ntreg. Dac se
dorete ca funcia s returneze un alt tip, atunci numele tipului trebuie
s precead numele funciei, iar programul trebuie s conin o
declaraie a acestei funcii att n fiierul n care funcia este definit
ct i n fiierul unde funcia este apelat.
__________________________________________________________________________
64
Pentru a evita orice confuzie se recomand ca tipul valorii
returnate de funcie s fie ntotdeauna precizat, iar dac dorim n mod
expres ca funcia s nu returneze o valoare s folosim tipul voi d.
De exemplu, funcia at of ( s) din biblioteca asociat
compilatorului convertete irul s de cifre n valoarea sa n dubl
precizie. Vom declara funcia sub forma:
doubl e at of ( char s[ ] ) ;
sau mpreun cu alte variabile de tip doubl e:
doubl e sum, at of ( char s[ ] ) ;
Funciile nu pot returna masive, structuri, reuniuni sau funcii.
Dac o funcie returneaz o valoare de tip char , nu este nevoie
de nici o declaraie de tip din cauza conversiilor implicite. Totdeauna
tipul char este convertit la i nt n expresii.
7.4. Argumentele funciei i transmiterea
parametrilor
O metod de a comunica datele ntre funcii este prin argumente.
Parantezele mici care urmeaz dup numele funciei nchid lista
argumentelor.
n limbajul C argumentele funciilor snt transmise prin valoare.
Aceasta nseamn c n C funcia apelat primete valorile
argumentelor sale ntr-o copie particular de variabile temporare (n
realitate pe stiv).
Funcia apelat nu poate altera dect variabilele sale particulare,
adic copiile temporare.
Apelul prin valoare este o posibilitate, dar nu i o obligativitate.
Apelul prin valoare conduce la programe mai compacte cu mai puine
variabile externe, deoarece argumentele pot fi tratate ca variabile
locale, i care pot fi modificate convenabil n rutina apelat.
Ca exemplu, prezentm o versiune a funciei putere care face uz
de acest fapt:
power ( i nt x, i nt n) {
i nt p;
__________________________________________________________________________
65
f or ( p=1; n>0; - - n)
p = p * x;
r et ur n p;
}
Argumentul n este utilizat ca o variabil temporar i este
decrementat pn devine zero; astfel nu este nevoie de nc o
variabil i . Orice operaii s-ar face asupra lui n n interiorul funciei,
ele nu au efect asupra argumentului pentru care funcia a fost apelat.
Dac totui se dorete alterarea efectiv a unui argument al
funciei apelante, acest lucru se realizeaz cu ajutorul pointerilor sau
a variabilelor declarate externe.
n cazul pointerilor, funcia apelant trebuie s furnizeze adresa
variabilei de modificat (tehnic printr-un pointer la aceast variabil),
iar funcia apelat trebuie s declare argumentul corespunztor ca
fiind un pointer. Referirea la variabila de modificat se face prin
adresare indirect (vezi capitolul 9).
Printre argumentele funciei pot aprea i nume de masive. n
acest caz valoarea transmis funciei este n realitate adresa de
nceput a masivului (elementele masivului nu snt copiate). Prin
indexarea acestei valori funcia poate avea acces i poate modifica
orice element din masiv.
7.5. Funcii cu numr variabil de parametri
Pentru funciile cu numr variabil de parametri standardul ANSI
definete o construcie special . . . , numit elips. Acesta este de
exemplu cazul funciilor de citire i scriere cu format (familiile
...scanf i ...printf - a se vedea capitolul 11). n acest caz, parametrii
fici snt verificai la compilare, iar cei variabili snt transmii ca i
cnd nu ar exista prototip de funcie.
Pentru simplitatea expunerii am pstrat conveniile definite de
standardele mai vechi ale limbajului, pstrate i de standardul ANSI
C. O funcie poate fi declarat fr nici un parametru, compilatorul
deducnd de aici c funcia respectiv poate avea orici parametri de
orice tip. Nu se recomand totui o asemenea practic deoarece este
__________________________________________________________________________
66
posibil s se creeze confuzii care conduc la erori n execuia
programelor, erori care se corecteaz foarte greu n cazul
programelor mari.
7.6. Exemple de funcii i programe
1. Acest exemplu este un program de cutare i imprimare a
tuturor liniilor dintr-un text care conin o anumit configuraie de
caractere, de exemplu cuvntul englezesc " t he". Structura de baz a
acestui program este:
whi l e ( mai exist linii surs)
i f ( linia conine configuraia de caractere)
imprim linia
n scopul scrierii acestui program vom scrie trei funcii. Funcia
get l i ne citete o linie din textul surs i returneaz lungimea ei.
Aceast funcie are dou argumente. Argumentul s indic numele
masivului unde se va citi linia, iar argumentul l i mreprezint
lungimea maxim a unei linii care poate fi citit.
Funcia i ndex caut configuraia de caractere dorit n interiorul
liniei i furnizeaz poziia sau indexul n irul s, unde ncepe irul t ,
sau 1 dac linia nu conine irul t . Argumentele funciei snt s care
reprezint adresa irului de studiat i t care reprezint configuraia
cutat.
Funcia care realizeaz afiarea este pr i nt f din bibliotec.
Programul care realizeaz structura de mai sus este:
#def i ne MAXLI NE 1000
#def i ne EOF - 1
get l i ne( char s[ ] , i nt l i m) {
/* citete o linie n s; returneaz lungimea */
i nt c, i ;
i = 0;
whi l e ( - - l i m>0 && ( c=get char ( ) ) ! = EOF &&
c! =' \ n' )
__________________________________________________________________________
67
s[ i ++] =c;
i f ( c==' \ n' ) s[ i ++] =c;
s[ i ] =' \ 0' ;
r et ur n i ;
}
i ndex( char s[ ] , char t [ ] ) {
/* returneaz poziia din irul s unde ncepe irul t, sau 1 */
i nt i , j , k;
f or ( i =0; s[ i ] ! =' \ 0' ; i ++) {
f or ( j =i , k=0; t [ k] ! =' \ 0' &&
s[ j ] ==t [ k] ; j ++, k++)
;
i f ( t [ k] ==' \ 0' )
r et ur n i ;
}
r et ur n - 1;
}
mai n( ) {
/* imprim toate liniile care conin cuvntul the */
char l i ne [ MAXLI NE] ;
whi l e ( get l i ne( l i ne, MAXLI NE) >0)
i f ( i ndex( l i ne, " t he" ) >=0)
pr i nt f ( " %s" , l i ne) ;
}
2. Funcia at of convertete un ir de cifre din formatul ASCII
ntr-un numr flotant n precizie dubl.
doubl e at of ( char s[ ] ) {
/* convertete irul s n dubl precizie */
doubl e val , power ;
i nt i , si gn;
f or ( i =0; s[ i ] ==' ' | | s[ i ] ==' \ n' | |
s[ i ] ==' \ t ' ; i ++) ; /* sare peste spaii albe */
__________________________________________________________________________
68
si gn = 1;
i f ( s[ i ] ==' +' | | s[ i ] ==' - ' )
si gn = ( s[ i ++] ==' +' ) ? 1 : - 1;
f or ( val =0; s[ i ] >=' 0' && s[ i ] <=' 9' ; i ++)
val = 10 * val + s[ i ] - ' 0' ;
i f ( s[ i ] == ' . ' ) /* punct zecimal */
i ++;
f or ( power =1; s[ i ] >=' 0' && s[ i ] <=' 9' ; i ++)
{
val = 10 * val +s[ i ] - ' 0' :
power *= 10;
}
r et ur n si gn * val / power ;
}
3. Funcia at oi convertete un ir de cifre n echivalentul lor
numeric ntreg.
at oi ( char s[ ] ) { /* convertete irul s la un ntreg */
i nt i , n;
n = 0;
f or ( i =0; s[ i ] >=' 0' && s[ i ] <=' 9' ; i ++)
n = 10 * n +s[ i ] - ' 0' ;
r et ur n n;
}
4. Funcia l ower convertete literele mari din setul de caractere
ASCII n litere mici. Dac lower primete un caracter care nu este o
liter mare atunci l returneaz neschimbat.
l ower ( i nt c) {
i f ( c>=' A' && c<=' Z' )
r et ur n c + ' a' - ' A' ;
el se
r et ur n c;
}
__________________________________________________________________________
69
5. Funcia bi nar y realizeaz cutarea valorii x ntr-un masiv
sortat v, care are n elemente.
bi nar y( i nt x, i nt v[ ] , i nt n) {
/* caut x n v
0
, v
1
, ..., v
n- 1
*/
i nt l ow, hi gh, mi d;
l ow = 0;
hi gh = n - 1;
whi l e ( l ow<=hi gh) {
mi d = ( l ow + hi gh) / 2;
i f ( x < v[ mi d] )
hi gh = mi d - 1;
el se i f ( x > v[ mi d] )
l ow = mi d + 1;
el se /* s-a gsit o intrare */
r et ur n( mi d) ;
}
r et ur n - 1;
}
Funcia returneaz poziia lui x (un numr ntre 0 i n1), dac x
apare n v sau 1 altfel.
Exemplul ilustreaz o decizie tripl, dac x este mai mic, mai
mare sau egal cu elementul din mijlocul irului v[ mi d] .
__________________________________________________________________________
70
8. Liniile de control ale compilatorului
Limbajul C ofer o facilitate de extensie a limbajului cu ajutorul
unui preprocesor de macro-operaii simple. Folosirea liniilor de
forma:
#def i ne
#i ncl ude
este cea mai uzual metod pentru extensia limbajului, caracterul #
(diez) indicnd faptul c aceste linii vor fi tratate de ctre preprocesor.
Liniile precedate de caracterul # au o sintax independent de restul
limbajului i pot aprea oriunde n fiierul surs, avnd efect de la
punctul de definiie pn la sfritul fiierului.
8.l. nlocuirea simbolurilor; substituii macro
O definiie de forma:
#def i ne identificator ir-simboluri
determin ca preprocesorul s nlocuiasc toate apariiile ulterioare
ale identificatorului cu ir-simboluri dat (ir-simboluri nu se termin
cu ; deoarece n urma substituiei identificatorului cu acest ir ar
aprea prea multe caractere ; ).
ir-simboluri sau textul de nlocuire este arbitrar. n mod normal
el este tot restul liniei ce urmeaz dup identificator. O definiie ns
poate fi continuat pe mai multe linii, introducnd ca ultim caracter n
linia de continuat caracterul \ (backslash).
Un identificator care este subiectul unei linii #def i ne poate fi
redefinit ulterior n program printr-o alt linie #def i ne. n acest
caz, prima definiie are efect numai pn la definiia urmtoare.
Substituiile nu se realizeaz n cazul n care identificatorii snt
ncadrai ntre ghilimele. De exemplu fie definiia:
#def i ne ALFA 1
Oriunde va aprea n programul surs identificatorul ALFA el va
fi nlocuit cu constanta 1, cu excepia unei situaii de forma:
__________________________________________________________________________
71
pr i nt f ( "ALFA" ) ;
n care se va tipri chiar textul ALFA i nu constanta 1, deoarece
identificatorul este ncadrat ntre ghilimele.
Aceast facilitate este deosebit de valoroas pentru definirea
constantelor simbolice ca n:
#def i ne TABSI ZE 100
i nt t ab[ TABSI ZE] ;
deoarece ntr-o eventual modificare a dimensiunii tabelului t ab se
va modifica doar o singur linie n fiierul surs. O linie de forma:
#def i ne identif( identif-1,..., identif-n) ir-simboluri
n care nu exist spaiu ntre primul identificator i caracterul ( este
o definiie pentru o macro-operaie cu argumente, n care textul de
nlocuire (ir-simboluri) depinde de modul n care se apeleaz macro-
ul respectiv. Ca un exemplu s definim o macro-operaie numit max
n felul urmtor:
#def i ne max( a, b) ( ( a) >( b) ? ( a) : ( b) )
atunci linia dintr-un program surs:
x = max( p+q, r +s) ;
va fi nlocuit cu:
x = ( ( p+q) >( r +s) ? ( p+q) : ( r +s) ) ;
Aceast macro-definiie furnizeaz o funcie maximum care se
expandeaz n cod, n loc s se realizeze un apel de funcie. Acest
macro va servi pentru orice tip de date, nefiind nevoie de diferite
tipuri de funcii maximum pentru diferite tipuri de date, aa cum este
necesar n cazul funciilor propriu-zise.
Dac se examineaz atent expandarea lui max se pot observa
anumite probleme ce pot genera erori, i anume: expresiile fiind
evaluate de dou ori, n cazul n care ele conin operaii ce genereaz
efecte colaterale (apelurile de funcii, operatorii de incrementare) se
pot obine rezultate total eronate.
De asemenea, trebuie avut mare grij la folosirea parantezelor
pentru a face sigur ordinea evalurii dorite. De exemplu macro-
operaia squar e( x) definit prin:
#def i ne squar e( x) x*x
care se apeleaz n programul surs sub forma:
z = squar e( z+1) ;
__________________________________________________________________________
72
va produce un rezultat, altul dect cel scontat, datorit prioritii mai
mari a operatorului * fa de cea a operatorului +.
8.2. Includerea fiierelor
O linie de forma:
#i ncl ude " nume-fiier"
realizeaz nlocuirea liniei respective cu ntregul coninut al fiierului
nume-fiier. Fiierul denumit este cutat n primul rnd n directorul
fiierului surs curent i apoi ntr-o succesiune de locuri standard,
cum ar fi biblioteca I/O standard asociat compilatorului. Alternativ,
o linie de forma:
#i ncl ude <nume-fiier>
caut fiierul nume-fiier numai n biblioteca standard i nu n
directorul fiierului surs.
Deseori, o linie sau mai multe linii, de una sau ambele forme apar
la nceputul fiecrui fiier surs pentru a include definiii comune
(prin declaraii #def i ne i declaraii externe pentru variabilele
globale).
Facilitatea de includere a unor fiiere ntr-un text surs este
deosebit de util pentru gruparea declaraiilor unui program mare. Ea
va asigura faptul c toate fiierele surs vor primi aceleai definiii i
declaraii de variabile, n felul acesta eliminndu-se un tip particular
de erori. Dac se modific un fiier inclus printr-o linie #i ncl ude,
toate fiierele care depind de el trebuie recompilate.
8.3. Compilarea condiionat
O linie de control a compilatorului de forma:
#i f expresie-constant
verific dac expresia constant este evaluat la o valoare diferit de
zero.
O linie de control de forma:
#i f def identificator
__________________________________________________________________________
73
verific dac identificatorul a fost subiectul unei linii de control de
forma #def i ne.
O linie de control de forma:
#i f ndef identificator
verific dac identificatorul este nedefinit n preprocesor.
Toate cele trei forme de linii de control precedente pot fi urmate
de un numr arbitrar de linii care, eventual, pot s conin o linie de
control forma:
#el se
i apoi de o linie de control de forma:
#endi f
Dac condiia supus verificrii este adevrat, atunci orice linie
ntre #el se i #endi f este ignorat. Dac condiia este fals atunci
toate liniile ntre testul de verificare i un #el se sau n lipsa unui
#el se pn la #endi f snt ignorate.
Toate aceste construcii pot fi imbricate.
8.4. Utilizarea dirctivelor de compilare
Prezentm n continuare un exemplu didactic de utilizare a
directivelor de compilare n dezvoltarea unor proiecte.
Se citete de la tastatur o pereche de numere naturale p i q. S
se determine dac fiecare din cele dou numere este prim sau nu, i
s se calculeze cel mai mare divizor comun.
S rezolvm aceast problem folosind dou fiiere surs. Primul
conine funcia mai n i apeleaz dou funcii: epr i mi cmmdc. Al
doilea fiier implementeaz cele dou funcii.
Prezentm mai nti un fiier header (numer e. h) care conine
definiiile celor dou funcii.
#i f ndef _Numer e_H
#def i ne _Numer e_H
unsi gned epr i m( unsi gned n) ;
unsi gned cmmdc( unsi gned p, unsi gned q) ;
#endi f
Fiierul surs pr i nc. c este foarte scurt.
__________________________________________________________________________
74
#i ncl ude <st di o. h>
#i ncl ude " numer e. h"
st at i c voi d ci t i r e( unsi gned *n) {
scanf ( " %u" , n) ;
i f ( epr i m( *n) )
pr i nt f ( " %u e pr i m\ n" , *n) ;
el se
pr i nt f ( " %u nu e pr i m\ n" , *n) ;
}
i nt mai n( ) {
unsi gned p, q, k;
ci t i r e( &p) ;
ci t i r e( &q) ;
k=cmmdc( p, q) ;
pr i nt f ( "Cmmdc: %u\ n" , k) ;
r et ur n 0;
}
Fiierul surs numer e. c este prezentat n continuare.
#i ncl ude " numer e. h"
unsi gned epr i m( unsi gned n) {
unsi gned i , a, r ;
i f ( n==0) r et ur n 0;
i f ( n<4) r et ur n 1;
i f ( ( n&1) ==0) r et ur n 0;
f or ( i =3; ; i +=2) {
r =n%i ;
i f ( r ==0) r et ur n 0;
a=n/ i ;
i f ( a<=i ) r et ur n 1;
}
}
unsi gned cmmdc( unsi gned p, unsi gned q) {
whi l e ( ( p>0) && ( q>0) )
i f ( p>q) p%=q;
el se q%=p;
__________________________________________________________________________
75
i f ( p==0) r et ur n q;
el se r et ur n p;
}
Pentru a obine un program executabil din aceste dou fiiere se
poate utiliza o singur comand de compilare:
Cc pr i nc. c numer e. c opiuni-de-compilare
unde Cc este numele compilatorului folosit (exemplu: bcc, gcc).
Opiunile de compilare (atunci cnd snt necesare) snt specifice
mediului de programare folosit.
Proiectele programe de dimensiuni mari snt compuse de cele
mai multe ori din module care se elaboreaz i se pun la punct
separat. Uneori pot fi identificate funcii care prezint un interes
general mai mare, i care pot fi utilizate n elaborarea unor tipuri de
aplicaii foarte diverse. Acestea snt dezvoltate separat i, dup ce au
fost puse la punct n totalitate, se depun ntr-o bibliotec de funcii n
format obiect. n momentul n care acestea snt incluse n proiecte nu
se mai pierde timp cu compilarea modulelor surs. n schimb
modulele care le apeleaz au nevoie de modul cum snt descrise
aceste funcii, i acesta se pstreaz n fiiere header.
__________________________________________________________________________
76
9. Pointeri i masive
Un pointer este o variabil care conine adresa unei alte variabile.
Pointerii snt foarte mult utilizai n programe scrise n C, pe de o
parte pentru c uneori snt unicul mijloc de a exprima un calcul, iar
pe de alt parte pentru c ofer posibilitatea scrierii unui program mai
compact i mai eficient dect ar putea fi obinut prin alte ci.
9.1. Pointeri i adrese
Deoarece un pointer conine adresa unui obiect, cu ajutorul lui
putem avea acces, n mod indirect, la acea variabil (obiect).
S presupunem c x este o variabil de tip ntreg i px un pointer
la aceast variabil. Atunci aplicnd operatorul unar & lui x,
instruciunea:
px = &x;
atribuie variabilei px adresa variabilei x; n acest fel spunem c px
indic (pointeaz) spre x.
Invers, dac px conine adresa variabilei x, atunci instruciunea:
y = *px;
atribuie variabilei y coninutul locaiei pe care o indic px.
Evident toate variabilele care snt implicate n aceste instruciuni
trebuie declarate. Aceste declaraii snt:
i nt x, y;
i nt *px;
Declaraiile variabilelor x i y snt deja cunoscute. Declaraia
pointerului px este o noutate. A doua declaraie indic faptul c o
combinaie de forma *px este un ntreg, iar variabila px care apare n
contextul *px este echivalent cu un pointer la o variabil de tip
ntreg. n locul tipului ntreg poate aprea oricare dintre tipurile
admise n limbaj i se refer la obiectele pe care le indic px.
Pointerii pot aprea i n expresii, ca de exemplu n expresia
urmtoare:
__________________________________________________________________________
77
y = *px + 1;
unde variabilei y i se atribuie coninutul variabilei x plus 1.
Instruciunea:
d = sqr t ( ( doubl e) *px) ;
are ca efect convertirea coninutului variabilei x pe care o indic px
n tip doubl e i apoi depunerea rdcinii ptrate a valorii astfel
convertite n variabila d.
Referiri la pointeri pot aprea de asemenea i n partea stng a
atribuirilor. Dac, de exemplu, px indic spre x, atunci:
*px = 0;
atribuie variabilei x valoarea zero, iar:
*px += 1;
incrementeaz coninutul variabilei x cu 1, ca i n expresia:
( *px) ++;
n acest ultim exemplu parantezele snt obligatorii deoarece, n
lipsa lor, expresia ar incrementa pe px n loc de coninutul variabilei
pe care o indic (operatorii unari *, ++au aceeai preceden i snt
evaluai de la dreapta spre stnga).
9.2 Pointeri i argumente de funcii
Deoarece n limbajul C transmiterea argumentelor la funcii se
face prin valoare (i nu prin referin), funcia apelat nu are
posibilitatea de a altera o variabil din funcia apelant. Problema
care se pune este cum procedm dac totui dorim s schimbm un
argument?
De exemplu, o rutin de sortare poate schimba ntre ele dou
elemente care nu respect ordinea dorit, cu ajutorul unei funcii
swap. Fie funcia swap definit astfel:
swap( i nt x, i nt y) { /* greit */
i nt t emp;
t emp = x;
x = y;
y = t emp;
}
__________________________________________________________________________
78
Funcia swap apelat prin swap( a, b) nu va realiza aciunea
dorit deoarece ea nu poate afecta argumentele a i b din rutina
apelant.
Exist ns o posibilitate de a obine efectul dorit, dac funcia
apelant transmite ca argumente pointeri la valorile ce se doresc
interschimbate. Atunci n funcia apelant apelul va fi:
swap( &a, &b) ;
iar forma corect a lui swapeste:
swap( i nt *px, i nt *py) {/* interschimb *px i *py */
i nt t emp;
t emp = *px;
*px = *py;
*py = t emp;
}
9.3. Pointeri i masive
n limbajul C exist o strns legtur ntre pointeri i masive.
Orice operaie care poate fi realizat prin indicarea masivului poate fi
de asemenea fcut prin pointeri, care, n plus, conduce i la o
accelerare a operaiei. Declaraia:
i nt a[ 10] ;
definete un masiv de dimensiune 10, care reprezint un bloc de 10
obiecte consecutive numite a[ 0] , ... a[ 9] . Notaia a[ i ] reprezint
al i -lea element al masivului sau elementul din poziia i +1, ncepnd
cu primul element. Dac pa este un pointer la un ntreg declarat sub
forma:
i nt *pa;
atunci atribuirea:
pa = &a[ 0] ;
ncarc variabila pa cu adresa primului element al masivului a.
Atribuirea:
x = *pa;
copiaz coninutul lui a[ 0] n x.
__________________________________________________________________________
79
Dac pa indic un element particular al unui masiv a, atunci prin
definiie pa+i indic un element cu i poziii dup elementul pe care
l indic pa, dup cum pa- i indic un element cu i poziii nainte
de cel pe care indic pa. Astfel, dac variabila pa indic pe a[ 0]
atunci *( pa+i ) se refer la coninutul lui a[ i ] .
Aceste observaii snt adevrate indiferent de tipul variabilelor
din masivul a.
ntreaga aritmetic cu pointeri are n vedere faptul c expresia
pa+i nseamn de fapt nmulirea lui i cu lungimea elementului pe
care l indic pa i adunarea apoi la pa, obinndu-se astfel adresa
elementului de indice i al masivului.
Corespondena dintre indexarea ntr-un masiv i aritmetica de
pointeri este foarte strns. De fapt, o referire la un masiv este
convertit de compilator ntr-un pointer la nceputul masivului.
Efectul este c un nume de masiv este o expresie pointer, deoarece
numele unui masiv este identic cu numele elementului de indice zero
din masiv.
Atribuirea:
pa = &a[ 0] ;
este identic cu:
pa = a;
De asemenea, expresiile a[ i ] i *( a+i ) snt identice. Aplicnd
operatorul & la ambele pri obinem &a[ i ] identic cu a+i . Pe de
alt parte, dac pa este un pointer, expresiile pot folosi acest pointer
ca un indice: pa[ i ] este identic cu *( pa+i ) . Pe scurt orice
expresie de masiv i indice poate fi scris ca un pointer i un
deplasament i invers, chiar n aceeai instruciune.
Exist ns o singur diferen ntre un nume de masiv i un
pointer la nceputul masivului. Un pointer este o variabil, deci
pa = a i pa++ snt instruciuni corecte. Dar un nume de masiv
este o constant i deci construcii de forma a = pa, a++ sau
p = &a snt ilegale.
Cnd se transmite unei funcii un nume de masiv, ceea ce se
transmite de fapt este adresa primului element al masivului. Aadar,
un nume de masiv, argument al unei funcii, este n realitate un
__________________________________________________________________________
80
pointer, adic o variabil care conine o adres. Fie de exemplu
funcia st r l en care calculeaz lungimea irului s:
st r l en( char *s) { /* returneaz lungimea irului */
i nt n;
f or ( n=0; *s! =' \ 0' ; s++)
n++;
r et ur n n;
}
Incrementarea lui s este legal deoarece s este o variabil
pointer. s++nu afecteaz irul de caractere din funcia care apeleaz
pe st r l en, ci numai copia adresei irului din funcia st r l en.
Este posibil s se transmit unei funcii, ca argument, numai o
parte a unui masiv, printr-un pointer la nceputul sub-masivului
respectiv. De exemplu, dac a este un masiv, atunci:
f ( &a[ 2] )
f ( a+2)
transmit funciei f adresa elementului a[ 2] , deoarece &a[ 2] i
a+2 snt expresii pointer care, ambele, se refer la al treilea element
al masivului a. n cadrul funciei f argumentul se poate declara astfel:
f ( i nt ar r [ ] ) { }
sau
f ( i nt *ar r ) { }
Declaraiile i nt ar r [ ] i i nt *ar r snt echivalente,
opiunea pentru una din aceste forme depinznd de modul n care vor
fi scrise expresiile n interiorul funciei.
9.4. Aritmetica de adrese
Dac p este un pointer, atunci p += i incrementeaz pe p
pentru a indica cu i elemente dup elementul pe care l indic n
prealabil p. Aceast construcie i altele similare snt cele mai simple
i comune formule ale aritmeticii de adrese, care constituie o
caracteristic puternic a limbajului C. S ilustrm cteva din
__________________________________________________________________________
81
proprietile aritmeticii de adrese scriind un alocator rudimentar de
memorie. Fie rutina al l oc( n) care returneaz un pointer p la o
zon de n caractere consecutive care vor fi folosite de rutina apelant
pentru memorarea unui ir de caractere. Fie rutina f r ee( p) care
elibereaz o zon ncepnd cu adresa indicat de pointerul p pentru a
putea fi refolosit mai trziu. Zona de memorie folosit de rutinele
al l oc i f r ee este o stiv funcionnd pe principiul ultimul intrat
primul ieit, iar apelul la f r ee trebuie fcut n ordine invers cu
apelul la al l oc. S considerm c funcia al l oc va gestiona stiva
ca pe elementele unui masiv pe care l vom numi al l ocbuf . Vom
mai folosi un pointer la urmtorul element liber din masiv, pe care-l
vom numi al l ocp. Cnd se apeleaz rutina al l oc pentru n
caractere, se verific dac exist suficient spaiu liber n masivul
al l ocbuf . Dac da, al l oc va returna valoarea curent a lui
al l ocp, adic adresa de nceput a blocului cerut, dup care va
incrementa pe al l ocp cu n pentru a indica urmtoarea zon liber.
f r ee( p) actualizeaz al l ocp cu valoarea p, dac p indic n
interiorul lui al l ocbuf .
#def i ne NULL 0
/* valoarea pointerului pentru semnalizarea erorii */
#def i ne ALLOCSI ZE 1000
/* dimensiunea spaiului disponibil */
st at i c char al l ocbuf [ ALLOCSI ZE] ;
/* memoria pentru alloc */
st at i c char *al l ocp = al l ocbuf ;
/* urmtoarea poziie liber */
char *al l oc( i nt n) {
/* returneaz pointer la n caractere */
i f ( al l ocp+n<=al l ocbuf +ALLOCSI ZE) {
al l ocp += n; /* dimensiunea satisfcut */
r et ur n al l ocp- n; /* vechea valoare */
}
el se
r et ur n NULL; /* nu este spaiu suficient */
__________________________________________________________________________
82
}
f r ee( char *p) { /* elibereaz memoria indicat de p */
i f ( p>=al l ocbuf && p<al l ocbuf +ALLOCSI ZE)
al l ocp = p;
}
Testul i f ( al l ocp+n<=al l ocbuf +ALLOCSI ZE)
verific dac exist spaiu suficient pentru satisfacerea cererii de
alocare a n caractere. Dac cererea poate fi satisfcut, alloc revine
cu un pointer la zona de n caractere consecutive. Dac nu, alloc
trebuie s semnaleze lipsa de spaiu pe care o face returnnd valoarea
constantei simbolice NULL. Limbajul C garanteaz c nici un pointer
care indic corect o dat nu va conine zero, prin urmare o revenire
cu valoarea zero poate fi folosit pentru semnalarea unui eveniment
anormal (n cazul nostru, lipsa de spaiu). Atribuirea valorii zero unui
pointer este deci un caz special.
Observm de asemenea c variabilele al l ocbuf i al l ocp
snt declarate static cu scopul ca ele s fie locale numai fiierului
surs care conine funciile al l oc i f r ee.
Exemplul de mai sus demonstreaz cteva din facilitile
aritmeticii de adrese (pointeri). n primul rnd, pointerii pot fi
comparai n anumite situaii. Dac p i q snt pointeri la membri
unui acelai masiv, atunci relaiile <, <=, >, >=, ==, ! =snt
valide. Relaia p<q, de exemplu, este adevrat dac p indic un
element mai apropiat de nceputul masivului dect elementul indicat
de pointerul q. Comparrile ntre pointeri pot duce ns la rezultate
imprevizibile, dac ei se refer la elemente aparinnd la masive
diferite.
Se observ c pointerii i ntregii pot fi adunai sau sczui.
Construcia de forma:
p+n
nseamn adresa celui de-al n-lea element dup cel indicat de p,
indiferent de tipul elementului pe care l indic p. Compilatorul C
aliniaz valoarea lui n conform dimensiunii elementelor pe care le
__________________________________________________________________________
83
indic p, dimensiunea fiind determinat din declaraia lui p (scara de
aliniere este 1 pentru char , 2 pentru i nt etc).
Dac p i q indic elemente ale aceluiai masiv, p- q este
numrul elementelor dintre cele pe care le indic p i q. S scriem o
alt versiune a funciei st r l en folosind aceast ultim observaie:
st r l en( char *s) { /* returneaz lungimea unui ir */
char *p;
p = s;
whi l e ( *p ! = ' \ 0' )
p++;
r et ur n p- s;
}
n acest exemplu s rmne constant cu adresa de nceput a
irului, n timp ce p avanseaz la urmtorul caracter de fiecare dat.
Diferena p- s dintre adresa ultimului element al irului i adresa
primului element al irului indic numrul de elemente.
n afar de operaiile binare menionate (adunarea sau scderea
pointerilor cu ntregi i scderea sau compararea a doi pointeri),
celelalte operaii cu pointeri snt ilegale. Nu este permis adunarea,
nmulirea, mprirea sau deplasarea pointerilor, dup cum nici
adunarea lor cu constante de tip doubl e sau f l oat .
Snt admise de asemenea incrementrile i decrementrile
precum i alte combinaii ca de exemplu *++p i *- - p.
9.5. Pointeri la caracter i funcii
O constant ir, de exemplu:
"Buna di mi neat a"
este un masiv de caractere, care n reprezentarea intern este terminat
cu caracterul ' \ 0' , astfel nct programul poate depista sfritul lui.
Lungimea acestui ir n memorie este astfel cu 1 mai mare dect
numrul de caractere ce apar efectiv ntre ghilimelele de nceput i
sfrit de ir.
__________________________________________________________________________
84
Cea mai frecvent apariie a unei constante ir este ca argument
la funcii, caz n care accesul la ea se realizeaz prin intermediul unui
pointer.
n exemplul:
pr i nt f ( "Buna di mi neat a\ n" ) ;
funcia pr i nt f primete de fapt un pointer la masivul de caractere.
n prelucrarea unui ir de caractere snt implicai numai pointeri,
limbajul C neoferind nici un operator care s trateze irul de caractere
ca o unitate de informaie.
Vom prezenta cteva aspecte legate de pointeri i masive
analiznd dou exemple. S considerm pentru nceput funcia
st r cpy( s, t ) care copiaz irul t peste irul s. O prim versiune
a programului ar fi urmtoarea:
st r cpy( char s[ ] , char t [ ] ) {/* copiaz t peste s */
i nt t ;
i = 0;
whi l e ( ( s[ i ] =t [ i ] ) ! = ' \ 0' )
i ++;
}
O a doua versiune cu ajutorul pointerilor este urmtoarea:
st r cpy( char *s, char *t ) {
/* versiune cu pointeri */
whi l e ( ( *s++=*t ++) ! = ' \ 0' ) ;
}
Aceast versiune cu pointeri modific prin incrementare pe s i t
n partea de test. Valoarea lui *t ++ este caracterul indicat de
pointerul t , nainte de incrementare. Notaia postfix ++asigur c t
va fi modificat dup depunerea coninutului indicat de el, la vechea
poziie a lui s, dup care i s se incrementeaz. Efectul este c se
copiaz caracterele irului t n irul s pn la caracterul terminal
' \ 0' inclusiv.
__________________________________________________________________________
85
Am mai putea face o observaie legat de redundana comparrii
cu caracterul ' \ 0' , redundan care rezult din structura
instruciunii whi l e.
i atunci forma cea mai prescurtat a funciei st r cpy( s, t )
este:
st r cpy( char *s, char *t ) {
whi l e ( *s++ = *t ++) ;
}
S considerm, ca al doilea exemplu, funcia st r cmp( s, t )
care compar caracterele irurilor s i t i returneaz o valoare
negativ, zero sau pozitiv, dup cum irul s este lexicografic mai
mic, egal sau mai mare ca irul t . Valoarea returnat se obine prin
scderea caracterelor primei poziii n care s i t difer.
O prim versiune a funciei st r cmp( s, t ) este urmtoarea:
st r cmp( char s, char t ) {/* compar irurile s i t */
i nt i ;
i = 0;
whi l e ( s[ i ] ==t [ i ] )
i f ( s[ i ++] ==' \ 0' )
r et ur n 0;
r et ur n s[ i ] - t [ i ] ;
}
Versiunea cu pointeri a aceleiai funcii este:
st r cmp( char *s, char *t ) {
f or ( ; *s==*t ; s++, t ++)
i f ( *s==' \ 0' )
r et ur n 0;
r et ur n *s- *t ;
}
n final prezentm funcia st r sav care copiaz un ir dat prin
argumentul ei ntr-o zon obinut printr-un apel la funcia al l oc.
Ea returneaz un pointer la irul copiat sau NULL, dac nu mai exist
suficient spaiu pentru memorarea irului.
__________________________________________________________________________
86
char *st r sav( char *s) { /* copiaz irul s */
char *p;
p = al l oc( st r l en( s) +1) ;
i f ( p! =NULL)
st r cpy( p, s) ;
r et ur n p;
}
9.6. Masive multidimensionale
Limbajul C ofer facilitatea utilizrii masivelor
multidimensionale, dei n practic ele snt folosite mai puin dect
masivele de pointeri.
S considerm problema conversiei datei, de la zi din lun, la zi
din an i invers, innd cont de faptul c anul poate s fie bisect sau
nu. Definim dou funcii care s realizeze cele dou conversii.
Funcia day_of _year convertete ziua i luna n ziua anului i
funcia mont h_day convertete ziua anului n lun i zi.
Ambele funcii au nevoie de aceeai informaie i anume un tabel
cu numrul zilelor din fiecare lun. Deoarece numrul zilelor din lun
difer pentru anii biseci de cele pentru anii nebiseci este mai uor s
considerm un tabel bidimensional n care prima linie s corespund
numrului de zile ale lunilor pentru anii nebiseci, iar a doua linie s
corespund numrului de zile pentru anii biseci. n felul acesta nu
trebuie s inem o eviden n timpul calculului a ceea ce se ntmpl
cu luna februarie. Atunci masivul bidimensional care conine
informaiile pentru cele dou funcii este urmtorul:
st at i c i nt day_t ab[ 2] [ 13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
Masivul day_t ab trebuie s fie declarat extern pentru a putea fi
folosit de ambele funcii.
__________________________________________________________________________
87
n limbajul C, prin definiie, un masiv cu dou dimensiuni este n
realitate un masiv cu o dimensiune ale crui elemente snt masive. De
aceea indicii se scriu sub forma [ i ] [ j ] n loc de [ i , j ] , cum se
procedeaz n cele mai multe limbaje. Un masiv bidimensional poate
fi tratat n acelai fel ca i n celelalte limbaje, n sensul c elementele
snt memorate pe linie, adic indicele cel mai din dreapta variaz cel
mai rapid.
Un masiv se iniializeaz cu ajutorul unei liste de iniializatori
nchii ntre acolade; fiecare linie a unui masiv bidimensional se
iniializeaz cu ajutorul unei subliste de iniializatori. n cazul
exemplului nostru, masivul day_t ab ncepe cu o coloan zero,
pentru ca numerele lunilor s fie ntre 1 i 12 i nu ntre 0 i 11,
aceasta pentru a nu face modificri n calculul indicilor.
i atunci funciile care realizeaz conversiile cerute de exemplul
nostru snt:
day_of _year ( i nt year , i nt mont h, i nt day)
{ /* ziua anului din lun i zi */
i nt i , l eap;
l eap = ( year %4==0) && ( year %100! =0) | |
( year %400==0) ;
f or ( i =1; i <mont h; i ++)
day += day_t ab[ l eap] [ i ] ;
r et ur n day;
}
Deoarece variabila l eap poate lua ca valori numai zero sau unu
dup cum expresia:
( year %4==0) && ( year %100! =0) | |
( year %400==0)
este fals sau adevrat, ea poate fi folosit ca indice de linie n
tabelul day_t ab care are doar dou linii n exemplul nostru.
mont h_day( i nt year , i nt year day,
i nt *pmont h, i nt *pday) {
i nt i , l eap;
l eap = ( year %4==0) && ( year %100! =0) | |
__________________________________________________________________________
88
( year %400==0) ;
f or ( i =1; year day>day_t ab[ l eap] [ i ] ; i ++)
year day - = day_t ab[ l eap] [ i ] ;
*pmont h = i ;
*pday = year day;
}
Deoarece aceast ultim funcie returneaz dou valori,
argumentele lun i zi vor fi pointeri.
Exemplu: mont h_day( 1984, 61, &m, &d) va ncrca pe mcu
3, iar pe d cu 1 (adic 1 martie).
Dac un masiv bidimensional trebuie transmis unei funcii,
declaraia argumentelor funciei trebuie s includ dimensiunea
coloanei. Dimensiunea liniei nu este necesar s apar n mod
obligatoriu, deoarece ceea ce se transmite de fapt este un pointer la
masive de cte 13 ntregi, n cazul exemplului nostru. Astfel, dac
masivul day_t ab trebuie transmis unei funcii f , atunci declaraia
lui f poate fi:
f ( i nt ( *day_t ab) [ 13] )
unde declaraia ( *day_t ab) [ 13] ) indic faptul c argumentul lui
f este un pointer la un masiv de 13 ntregi.
n general deci, un masiv d-dimensional a[ i ] [ j ] . . . [ p] de
rangul i *j *. . . *p este un masiv d1 - dimensional de rangul
j *k*. . . *p ale crui elemente, fiecare, snt masive d2 -
dimensionale de rang k*. . . *p ale crui elemente, fiecare, snt
masive d3 - dimensionale .a.m.d. Oricare dintre expresiile a[ i ] ,
a[ i ] [ j ] ..., a[ i ] [ j ] . . . [ p] pot aprea n expresii. Prima are
tipul masiv, ultima are tipul i nt , de exemplu, dac masivul este de
tipul i nt . Vom mai reveni asupra acestei probleme cu detalii.
9.7. Masive de pointeri i pointeri la pointeri
Deoarece pointerii snt variabile, are sens noiunea de masiv de
pointeri. Vom ilustra modul de lucru cu masive de pointeri pe un
exemplu.
__________________________________________________________________________
89
S scriem un program care s sorteze lexicografic liniile de
lungimi diferite ale unui text, linii care spre deosebire de ntregi nu
pot fi comparate sau schimbate printr-o singur operaie.
Dac memorm liniile textului una dup alta ntr-un masiv lung
de caractere (gestionat de funcia al l oc), atunci fiecare linie poate
fi accesibil cu ajutorul unui pointer la primul ei caracter. Pointerii
tuturor liniilor, la rndul lor, pot fi memorai sub forma unui masiv.
Atunci dou linii de text pot fi comparate transmind pointerii lor
funciei strcmp. Dac dou linii care nu respect ordinea trebuie s
fie schimbate, se schimb doar pointerii lor din masivul de pointeri i
nu textul efectiv al liniilor.
Procesul de sortare l vom realiza n trei pai:
1) se citesc toate liniile textului de la intrare;
2) se sorteaz liniile n ordine lexicografic;
3) se tipresc liniile sortate n noua ordine.
Vom scrie programul prin funciile sale, fiecare funcie realiznd
unul din cei trei pai de mai sus. O rutin principal va controla cele
trei funcii. Ea are urmtorul cod:
#def i ne LI NES 100 /* nr maxim de linii de sortat */
mai n( ) { /* sorteaz liniile de la intrare */
char *l i nept r [ LI NES] ; /* pointeri la linii */
i nt nl i nes; /* nr linii intrare citite */
i f ( ( nl i nes=r eadl i nes( l i nept r , LI NES) ) >=0)
{
sor t ( l i nept r , nl i nes) ;
wr i t el i nes( l i nept r , nl i nes) ;
}
el se pr i nt f
( " I nt r ar ea pr ea mar e pent r u sor t \ n" ) ;
}
Cele 3 funcii care realizeaz ntregul proces snt: r eadl i nes,
sor t i wr i t el i nes.
__________________________________________________________________________
90
Rutina de intrare r eadl i nes trebuie s memoreze caracterele
fiecrei linii i s construiasc un masiv de pointeri la liniile citite.
Trebuie, de asemenea, s numere liniile din textul de la intrare,
deoarece aceast informaie este necesar n procesul de sortare i de
imprimare. ntruct funcia de intrare poate prelucra numai un numr
finit de linii de intrare, ea poate returna un numr ilegal, cum ar fi 1,
spre a semnala c numrul liniilor de intrare este prea mare pentru
capacitatea de care dispune.
Atunci funcia r eadl i nes care citete liniile textului de la
intrare este urmtoarea:
#def i ne MAXLEN 1000
#def i ne NULL 0
#def i ne EOF - 1
r eadl i nes( char *l i nept r [ ] , i nt maxl i nes) {
/* citete liniile */
i nt l en, nl i nes;
char *p, *al l oc( ) , l i ne[ MAXLEN] ;
nl i nes = 0;
whi l e ( ( l en=get l i ne( l i ne, MAXLEN) ) >0)
i f ( nl i nes>=maxl i nes)
r et ur n - 1;
el se i f ( ( p=al l oc( l en) ) ==NULL)
r et ur n - 1;
el se {
l i ne[ l en- 1] = ' \ 0' ;
st r cpy( p, l i ne) ;
l i nept r [ nl i nes++] = p;
}
r et ur n nl i nes;
}
Instruciunea l i ne[ l en- 1] = ' \ 0' ; terge caracterul
<LF> de la sfritul fiecrei linii ca s nu afecteze ordinea n care snt
sortate liniile i depune n locul lui caracterul ' \ 0' ca marc de
sfrit de ir.
__________________________________________________________________________
91
Rutina care tiprete liniile n noua lor ordine este wr i t el i nes
i are urmtorul cod:
wr i t el i nes( char *l i nept r [ ] , i nt nl i nes) {
/* scrie liniile sortate */
i nt i ;
f or ( i =0; i <nl i nes; i ++)
pr i nt f ( " %s\ n" , l i nept r [ i ] ) ;
}
Declaraia nou care apare n aceste programe este:
char *l i nept r [ LI NES] ;
care indic faptul c l i nept r este un masiv de LI NES elemente,
fiecare element al masivului fiind un pointer la un caracter. Astfel
l i nept r [ i ] este un pointer la un caracter, iar *lineptr[i] permite
accesul la caracterul respectiv.
Deoarece l i nept r este el nsui un masiv, care se transmite ca
argument funciei wr i t el i nes, el va fi tratat ca un pointer (vezi
seciunea 9.3) i atunci funcia wr i t el i nes mai poate fi scris i
astfel:
wr i t el i nes( char *l i nept r [ ] , i nt nl i nes) {
whi l e ( - - nl i nes>=0)
pr i nt f ( " %s\ n" , *l i nept r ++) ;
}
n funcia pr i nt f , l i nept r indic iniial prima linie de
imprimat; fiecare incrementare avanseaz pe *l i nept r la
urmtoarea linie de imprimat, n timp ce nl i nes se micoreaz
dup fiecare tiprire a unei linii cu 1.
Funcia care realizeaz sortarea efectiv a liniilor se bazeaz pe
algoritmul de njumtire i are urmtorul cod:
#def i ne NULL 0
#def i ne LI NES 100 /* nr maxim de linii de sortat */
sor t ( char *v[ ] , i nt n) {
/* sorteaz irurile v
0
, v
1
, ... v
n- 1
n ordine cresctoare */
i nt gap, i , j ;
__________________________________________________________________________
92
char *t emp;
f or ( gap=n/ 2; gap>0; gap/ =2)
f or ( i =gap; i <n; i ++)
f or ( j =i - gap; j >=0; j - =gap) {
i f ( st r cmp( v[ j ] , v[ j +gap] ) <=0)
br eak;
t emp = v[ j ] ;
v[ j ] = v[ j +gap] ;
v[ j +gap] = t emp;
}
}
Deoarece fiecare element al masivului v (care este de fapt
masivul l i nept r ) este un pointer la primul caracter al unei linii,
variabila t emp va fi i ea un pointer la un caracter, deci operaiile de
atribuire din ciclu dup variabila j snt admise i ele realizeaz
reinversarea pointerilor la linii dac ele nu snt n ordinea cerut.
S reinem deci urmtoarele lucruri legate de masive i pointeri.
De cte ori apare ntr-o expresie un identificator de tip masiv el este
convertit ntr-un pointer la primul element al masivului. Prin
definiie, operatorul de indexare [ ] este interpretat astfel nct
E1[ E2] este identic cu *( ( E1) +( E2) ) . Dac E1 este un masiv, iar
E2 un ntreg, atunci E1[ E2] se refer la elementul de indice E2 al
masivului E1.
O regul corespunztoare se aplic i masivelor multi-
dimensionale. Dac E1 este un masiv d-dimensional, de rangul
i *j *. . . *k, atunci ori de cte ori e1 apare ntr-o expresie, e1 va fi
convertit ntr-un pointer la un masiv d1 - dimensional de rangul
j *. . . *k, ale crui elemente snt masive. Dac operatorul * se
aplic acestui pointer, rezultatul este masivul d1 - dimensional, care
se va converti imediat ntr-un pointer la un masiv d2 - dimensional
.a.m.d. Raionamentul se poate aplica n mod inductiv pn cnd, n
final, ca urmare a aplicrii operatorului * se obine ca rezultat un
ntreg, de exemplu, dac masivul a fost declarat de tipul i nt .
S considerm, de exemplu, masivul:
__________________________________________________________________________
93
i nt x[ 3] [ 5] ;
x este un masiv de ntregi, de rangul 3*5. Cnd x apare ntr-o
expresie, el este convertit ntr-un pointer la (primul din cele trei)
masive de 5 ntregi.
n expresia x[ i ] , care este echivalent cu expresia *( x+i ) , x
este convertit ntr-un pointer la un masiv, ale crui elemente snt la
rndul lor masive de 5 elemente; apoi i se convertete la tipul x, adic
indicele i se nmulete cu lungimea elementului pe care l indic x
(adic 5 ntregi) i apoi rezultatele se adun. Se aplic operatorul *
pentru obinerea masivului i (de 5 ntregi) care la rndul lui este
convertit ntr-un pointer la primul ntreg din cei cinci.
Se observ deci c primul indice din declaraia unui masiv nu
joac rol n calculul adresei.
9.8. Iniializarea masivelor i masivelor de
pointeri
Iniializatorul unei variabile declarate masiv const dintr-o list
de iniializatori separai prin virgul i nchii ntre acolade,
corespunztori tuturor elementelor masivului. Ei snt scrii n ordinea
cresctoare a indicilor masivului. Dac masivul conine sub-masive
atunci regula se aplic recursiv membrilor masivului. Dac n lista de
iniializare exist mai puini iniializatori dect elementele masivului,
restul elementelor neiniializate se iniializeaz cu zero. Nu se admite
iniializarea unui masiv de clas cu automatic.
Acoladele { i } se pot omite n urmtoarele situaii:
dac iniializatorul ncepe cu o acolad stng ({), atunci lista de
iniializatori, separai prin virgul, va iniializa elementele masivului;
nu se accept s existe mai muli iniializatori dect numrul
elementelor masivului;
__________________________________________________________________________
94
dac ns iniializatorul nu ncepe cu acolad stng ({), atunci se
iau din lista de iniializatori atia iniializatori ci corespund
numrului de elemente ale masivului, restul iniializatorilor vor
iniializa urmtorul membru al masivului, care are ca parte (sub-
masiv) masivul deja iniializat.
Un masiv de caractere poate fi iniializat cu un ir, caz n care
caracterele succesive ale irului iniializeaz elementele masivului.
Exemple:
1) i nt x[ ] = {1, 3, 5};
Aceast declaraie definete i iniializeaz pe x ca un masiv
unidimensional cu trei elemente, n ciuda faptului c nu s-a specificat
dimensiunea masivului. Prezena iniializatorilor nchii ntre acolade
determin dimensiunea masivului.
2) Declaraia
i nt y[ 4] [ 3] ={
{1, 3, 5},
{2, 4, 6},
{3, 5, 7},
};
este o iniializare complet nchis ntre acolade. Valorile 1,3,5
iniializeaz prima linie a masivului y[ 0] i anume pe y[ 0] [ 0] ,
y[ 0] [ 1] , y[ 0] [ 2] . n mod analog urmtoarele dou linii
iniializeaz pe y[ 1] i y[ 2] . Deoarece iniializatorii snt mai putini
dect numrul elementelor masivului, linia y[ 3] se va iniializa cu
zero, respectiv elementele y[ 3] [ 0] , y[ 3] [ 1] , y[ 3] [ 2] vor avea
valorile zero.
3) Acelai efect se poate obine din declaraia:
i nt y[ 4] [ 3] = {1, 3, 5, 2, 4, 6, 3, 5, 7};
unde iniializatorul masivului y ncepe cu acolada stng n timp ce
iniializatorul pentru masivul y[ 0] nu, fapt pentru care primii trei
iniializatori snt folosii pentru iniializarea lui y[ 0] , restul
iniializatorilor fiind folosii pentru iniializarea masivelor y[ 1] i
respectiv y[ 2] .
__________________________________________________________________________
95
4) Declaraia:
i nt y[ 4] [ 3] = {
{1}, {2, }, {3, }, {4}
};
iniializeaz masivul y[ 0] cu (1,0,0), masivul y[ 1] cu (2,0,0),
masivul y[ 2] cu (3,0,0) i masivul y[ 4] cu (4,0,0).
5) Declaraia:
st at i c char msg[ ] = " Er oar e de si nt axa" ;
iniializeaz elementele masivului de caractere msg cu caracterele
succesive ale irului dat.
n ceea ce privete iniializarea unui masiv de pointeri s
considerm urmtorul exemplu.
Fie funcia mont h_name care returneaz un pointer la un ir de
caractere care indic numele unei luni a anului. Funcia dat conine
un masiv de iruri de caractere i returneaz un pointer la un astfel de
ir, cnd ea este apelat.
Codul funciei este urmtorul:
char *mont h_name( i nt n) {
/* returneaz numele lunii a n-a */
st at i c char *name[ ] = {
" l una i l egal a" , " i anuar i e" ,
" f ebr uar i e" , "mar t i e" , "apr i l i e" ,
" mai ", " i uni e" , " i ul i e" , " august " ,
" sept embr i e" , " oct ombr i e" , " noi embr i e" ,
" decembr i e"
}
r et ur n ( ( n<1) | | ( n>12) ) ? name[ 0] :
name[ n] ;
}
n acest exemplu, name este un masiv de pointeri la caracter, al
crui iniializator este o list de iruri de caractere. Compilatorul
aloc o zon de memorie pentru memorarea acestor iruri i
genereaz cte un pointer la fiecare din ele pe care apoi i introduce n
masivul name. Deci name[ i ] va conine un pointer la irul de
__________________________________________________________________________
96
caractere avnd indice i al iniializatorului. Dimensiunea masivului
name nu este necesar a fi specificat deoarece compilatorul o
calculeaz numrnd iniializatorii furnizai i o completeaz n
declaraia masivului.
9.9. Masive de pointeri i masive
multidimensionale
Adesea se creeaz confuzii n ceea ce privete diferena dintre un
masiv bidimensional i un masiv de pointeri. Fie date declaraiile:
i nt a[ 10] [ 10] ;
i nt *b[ 10] ;
n aceast declaraie a este un masiv de ntregi cruia i se aloc
spaiu pentru toate cele 100 de elemente, iar calculul indicilor se face
n mod obinuit pentru a avea acces la oricare element al masivului.
Pentru masivul b, declaraia aloc spaiu numai pentru zece
pointeri, fiecare trebuind s fie ncrcat cu adresa unui masiv de
ntregi.
Presupunnd c fiecare pointer indic un masiv de zece elemente
nseamn c ar trebui alocate nc o sut de locaii de memorie pentru
elementele masivelor.
n aceast accepiune, folosirea masivelor a i b poate fi similar
n sensul c a[ 5] [ 5] i b[ 5] [ 5] , de exemplu, se refer ambele la
unul i acelai ntreg (dac fiecare element b[ i ] este iniializat cu
adresa masivului a[ i ] ).
Astfel, masivul de pointeri utilizeaz mai mult spaiu de memorie
dect masivele bidimensionale i pot cere un pas de iniializare
explicit. Dar masivele de pointeri prezint dou avantaje, i anume:
accesul la un element se face cu adresare indirect, prin intermediul
unui pointer, n loc de procedura obinuit folosind nmulirea i apoi
adunarea, iar al doilea avantaj const n aceea c dimensiunea
masivelor pointate poate fi variabil. Acest lucru nseamn c un
element al masivului de pointeri b poate indica un masiv de zece
__________________________________________________________________________
97
elemente, altul un masiv de dou elemente i altul de exemplu poate
s nu indice nici un masiv.
Cu toate c problema prezentat n acest paragraf am descris-o n
termenii ntregilor, ea este cel mai frecvent utilizat n memorarea
irurilor de caractere de lungimi diferite (ca n funcia mont h_name
prezentat mai sus).
9.10. Argumentele unei linii de comand
n sistemul de calcul care admite limbajul C trebuie s existe
posibilitatea ca n momentul execuiei unui program scris n acest
limbaj s i se transmit acestuia argumente sau parametri prin linia de
comand. Cnd un program este lansat n execuie i funcia mai n
este apelat, apelul va conine dou argumente. Primul argument
(numit convenional ar gc) reprezint numrul de argumente din
linia de comand care a lansat programul. Al doilea argument
(ar gv) este un pointer la un masiv de pointeri la iruri de caractere
care conin argumentele, cte unul pe ir.
S ilustrm acest mod dinamic de comunicare ntre utilizator i
programul su printr-un exemplu.
Fie programul numit pr i care dorim s imprime la terminal
argumentele lui luate din linia de comand, imprimarea fcndu-se pe
o linie, iar argumentele imprimate s fie separate prin spaii.
Comanda:
pr i succes col egi
va avea ca rezultat imprimarea la terminal a textului
succes colegi
Prin convenie, ar gv[ 0] este un pointer la numele pr i al
programului apelat, astfel c ar gc, care specific numrul de
argumente din linia de comand este cel puin 1.
n exemplul nostru, ar gc este 3, iar ar gv[ 0] , ar gv[ 1] i
ar gv[ 2] snt pointeri la " pr i ", " succes" i respectiv
" col egi ". Primul argument real este ar gv[ 1] iar ultimul este
ar gv[ ar gc- 1] . Dac ar gc este 1, nseamn c linia de comand
nu are nici un argument dup numele programului.
__________________________________________________________________________
98
Atunci programul pr i are urmtorul cod:
mai n( i nt ar gc, char *ar gv[ ] ) {
/* tiprete argumentele */
i nt i ;
f or ( i =1; i <ar gc; i ++)
pr i nt f ( " %s%%c", ar gv[ i ] , ( i <ar gc- 1) ?
' ' : ' \ n' ) ;
}
Deoarece ar gv este un pointer la un masiv de pointeri, exist
mai multe posibiliti de a scrie acest program. S mai scriem dou
versiuni ale acestui program.
mai n( i nt ar gc, char *ar gv[ ] ) {
/* versiunea a doua */
whi l e ( - - ar gc>0)
pr i nt f ( " %s%c" , *++ar gv, ( ar gv>1) ?
' ' : ' \ n' ) ;
}
Deoarece ar gv este un pointer la un masiv de pointeri,
incrementndu-l, ( ++ar gv) , el va pointa la ar gv[ 1] n loc de
ar gv[ 0] . Fiecare incrementare succesiv poziioneaz pe ar gv la
urmtorul argument, iar *ar gv este pointerul la argumentul irului
respectiv. n acelai timp ar gc este decrementat pn devine zero,
moment n care nu mai snt argumente de imprimat.
Alternativ:
mai n( i nt ar gc, char *ar gv[ ] ) {
/* versiunea a treia */
whi l e ( - - ar gc>0)
pr i nt f ( ( ar gc>1) ? " %s " : " %s\ n" , *++ar gv) ;
}
__________________________________________________________________________
99
Aceast versiune arat c argumentul funciei pr i nt f poate fi o
expresie ca oricare alta, cu toate c acest mod de utilizare nu este
foarte frecvent.
Ca un al doilea exemplu, s reconsiderm programul din
seciunea 7.5, care imprim fiecare linie a unui text care conine un
ir specificat de caractere (schem).
Dorim acum ca aceast schem s poat fi modificat dinamic,
de la execuie la execuie. Pentru aceasta o specificm printr-un
argument n linia de comand.
i atunci programul care caut schema dat de primul argument
al liniei de comand este:
#def i ne MAXLI NE 1000
mai n( i nt ar gc, char *ar gv[ ] ) {
/* gsete schema din primul argument */
char l i ne[ MAXLI NE] ;
i f ( ar gc! =2)
pr i nt f ( " Li ni a de comanda er onat a\ n") ;
el se
whi l e ( get l i ne( l i ne, MAXLI NE) >0)
i f ( i ndex( l i ne, ar gv[ 1] ) >=0)
pr i nt f ( " %s" , l i ne) ;
}
unde linia de comand este de exemplu: " f i nd l i mbaj " n care
" f i nd" este numele programului, iar " l i mbaj " este schema
cutat. Rezultatul va fi imprimarea tuturor liniilor textului de intrare
care conin cuvntul " l i mbaj ".
S elaborm acum modelul de baz, legat de linia de comand i
argumentele ei.
S presupunem c dorim s introducem n linia de comand dou
argumente opionale: unul care s tipreasc toate liniile cu excepia
acelora care conin schema, i al doilea care s precead fiecare linie
tiprit cu numrul ei de linie.
O convenie pentru programele scrise n limbajul C este ca
argumentele dintr-o linie de comand care ncep cu un semn '- ' s
introduc un parametru opional. Dac alegem, de exemplu, - x
__________________________________________________________________________
100
pentru a indica cu excepia i - n pentru a cere numrarea
liniilor, atunci comanda:
f i nd - x - n l a
avnd intrarea:
la miezul stinselor lumini
s-ajung victorios,
la temelii, la rdcini,
la mduv, la os.
va produce tiprirea liniei a doua, precedat de numrul ei, deoarece
aceast linie nu conine schema " l a".
Argumentele opionale snt permise n orice ordine n linia de
comand. Analizarea i prelucrarea argumentelor unei linii de
comand trebuie efectuat n funcia principal mai n, iniializnd n
mod corespunztor anumite variabile. Celelalte funcii ale
programului nu vor mai ine evidena acestor argumente.
Este mai comod pentru utilizator dac argumentele opionale snt
concatenate, ca n comanda:
f i nd - xn l a
Caracterele ' x' respectiv ' n' indic doar absena sau prezena
acestor opiuni (switch) i nu snt tratate din punct de vedere al
valorii lor.
Fie programul care caut schema " l a" n liniile de la intrare i
le tiprete pe acelea, care nu conin schema, precedate de numrul
lor de linie. Programul trateaz corect, att prima form a liniei de
comand ct i a doua.
#def i ne MAXLI NE 1000
mai n( i nt ar gc, char *ar gv[ ] ) {
/* caut schema */
char l i ne[ MAXLI NE] , *s;
l ong l i ne0;
i nt except , number ;
l i ne0 = 0;
__________________________________________________________________________
101
number = 0;
whi l e ( - - ar gc>0 && ( *++ar gv) [ 0] ==' - ' )
f or ( s=ar gv[ 0] +1; *s! =' \ 0' ; s++)
swi t ch( *s) {
case ' x' : except = 1; br eak;
case ' n' : number = 1; br eak;
def aul t :
pr i nt f
( " f i nd: opt i une i l egal a %c\ n" ,
*s) ;
ar gc = 0;
br eak;
}
i f ( ar gc! =1)
pr i nt f
( " Nu exi st a ar gument e sau schema\ n" ) ;
el se
whi l e ( get l i ne( l i ne, MAXLI NE) >0) {
l i ne0++;
i f ( ( i ndex( l i ne, *ar gv) >=0) ! =except )
{
i f ( number )
pr i nt f ( " %d: " , l i ne0) ;
pr i nt f ( " %s" , l i ne) ;
}
}
}
Dac nu exist erori n linia de comand, atunci la sfritul
primului ciclu while ar gc trebuie s fie 1, iar *ar gv conine adresa
schemei. *++ar gv este un pointer la un ir argument, iar
( *++ar gv) [ 0] este primul caracter al irului. n aceast ultim
expresie parantezele snt necesare deoarece fr ele expresia
nseamn *++( ar gv[ 0] ) ceea ce este cu totul altceva (i greit): al
doilea caracter din numele programului. O alternativ corect pentru
( *++ar gv[ 0] ) este **++ar gv.
__________________________________________________________________________
102
9.11. Pointeri la funcii
n limbajul C o funcie nu este o variabil, dar putem defini un
pointer la o funcie, care apoi poate fi prelucrat, transmis unor alte
funcii, introdus ntr-un masiv i aa mai departe. Relativ la o funcie
se pot face doar dou operaii: apelul ei i considerarea adresei ei.
Dac numele unei funcii apare ntr-o expresie, fr a fi urmat
imediat de o parantez stng, deci nu pe poziia unui apel la ea,
atunci se genereaz un pointer la aceast funcie. Pentru a transmite o
funcie unei alte funcii, ca argument, se poate proceda n felul
urmtor:
i nt f ( ) ;
g( f ) ;
unde funcia f este un argument pentru funcia g. Definiia funciei g
va fi:
g( i nt ( *f uncpt ) ( ) ) {
( *f uncpt ) ( ) ;
}
Funcia f trebuie declarat explicit n rutina apelant (i nt
f ( ) ; ), deoarece apariia ei n g( f ) nu a fost urmat de parantez
stng ( . n expresia g( f ) f nu apare pe poziia de apel de funcie.
n acest caz, pentru argumentul funciei g se genereaz un pointer la
funcia f . Deci g apeleaz funcia f printr-un pointer la ea.
Declaraiile din funcia g trebuie studiate cu grij.
i nt ( *f uncpt ) ( ) ;
spune c f uncpt este un pointer la o funcie care returneaz un
ntreg. Primul set de paranteze este necesar, deoarece fr el
i nt *f uncpt ( ) ;
nseamn c f uncpt este o funcie care returneaz un pointer la un
ntreg, ceea ce este cu totul diferit fa de sensul primei expresii.
Folosirea lui f uncpt n expresia:
( *f uncpt ) ( ) ;
indic faptul c f uncpt este un pointer la o funcie, *f uncpt este
funcia, iar ( *f uncpt ) ( ) este apelul funciei.
O form echivalent simplificat de apel este urmtoarea:
__________________________________________________________________________
103
f uncpt ( ) ;
Ca un exemplu, s considerm procedura de sortare a liniilor de
la intrare, descris n seciunea 9.7, dar modificat n sensul ca dac
argumentul opional - n apare n linia de comand, atunci liniile se
vor sorta nu lexicografic ci numeric, liniile coninnd grupe de
numere.
O sortare const adesea din trei pri: o comparare care determin
ordinea oricrei perechi de elemente, un schimb care inverseaz
ordinea elementelor implicate i un algoritm de sortare care face
comparrile i inversrile pn cnd elementele snt aduse n ordinea
cerut. Algoritmul de sortare este independent de operaiile de
comparare i inversare, astfel nct transmind diferite funcii de
comparare i inversare funciei de sortare, elementele de intrare se
pot aranja dup diferite criterii.
Compararea lexicografic a dou linii se realizeaz prin funciile
st r cmp i swap. Mai avem nevoie de o rutin numcmp care s
compare dou linii pe baza valorilor numerice i care s returneze
aceiai indicatori ca i rutina st r cmp.
Declarm aceste trei funcii n funcia principal mai n, iar
pointerii la aceste funcii i transmitem ca argumente funciei sor t ,
care la rndul ei va apela aceste funcii prin intermediul pointerilor
respectivi.
Funcia principal mai n va avea atunci urmtorul cod:
#def i ne LI NES 100 /* nr maxim de linii de sortat */
mai n ( i nt ar gc, char *ar gv[ ] ) {
char *l i nept r [ LI NES] ; /* pointeri la linii text */
i nt nl i nes; /* numr de linii citite */
i nt st r cmp( ) , numcmp( ) ; /* funcii de comparare */
i nt swap ( ) ; /* funcia de inversare */
i nt numer i c;
numer i c = 0; /* 1 dac sort numeric */
i f ( ar gc>1 && ar gv[ 1] [ 0] ==' - ' &&
ar gv[ 1] [ 1] ==' n' )
numer i c = 1;
__________________________________________________________________________
104
i f ( ( nl i nes=r eadl i nes( l i nept r , LI NES) ) >=0)
{
i f ( numer i c)
sor t ( l i nept r , nl i nes, numcmp, swap) ;
el se
sor t ( l i nept r , nl i nes, st r cmp, swap) ;
wr i t el i nes ( l i nept r , nl i nes) ;
}
el se
pr i nt f
( " Nr de l i ni i de i nt r ar e pr ea mar e\ n" ) ;
}
n apelul funciei sor t , argumentele st r cmp, numcmp i
swap snt adresele funciilor respective. Deoarece ele au fost
declarate funcii care returneaz un ntreg, operatorul & nu este
necesar s precead numele funciilor, compilatorul fiind cel care
gestioneaz transmiterea adreselor funciilor.
Funcia sor t care aranjeaz liniile n ordinea cresctoare se va
modifica astfel:
sor t ( char *v[ ] , i nt n, i nt ( *comp) ( ) ,
i nt ( *exch) ( ) ) { /* sorteaz v
0
, v
1
, ... , v
n1
*/
i nt gap, i , j ;
f or ( gap=n/ 2; gap>0; gap/ =2)
f or ( i =gap; i <n; i ++)
f or ( j =i - gap; j >=0; j - =gap) {
i f ( comp( v[ j ] , v[ j +gap] ) <=0)
br eak;
exch( v+j , v+j +gap) ;
}
}
S studiem declaraiile din aceast funcie.
i nt ( *comp) ( ) , ( *exch) ( ) ;
indic faptul c comp i exch snt pointeri la funcii care returneaz
un ntreg (primul set de paranteze este necesar).
__________________________________________________________________________
105
i f ( comp( v[ j ] , v[ j +gap] ) <=0)
nseamn apelul funciei comp (adic st r cmp sau numcmp),
deoarece comp este un pointer la funcie, *comp este funcia, iar
comp( v[ j ] , v[ j +gap] )
este apelul funciei.
exch( v+j , v+j +gap)
este apelul funciei swap, de inversare a dou linii, inversare care
realizeaz interschimbarea adreselor liniilor implicate (vezi seciunea
9.2). Funcia numcmp este urmtoarea:
numcmp( char *s1, char *s2) {
/* compar s1 i s2 numeric */
doubl e at of ( ) , v1, v2;
v1 = at of ( s1) ;
v2 = at of ( s2) ;
i f ( v1<v2)
r et ur n - 1;
el se
i f ( v1>v2)
r et ur n 1;
el se
r et ur n 0;
}
Pentru ca programul nostru s fie complet s mai prezentm i
codul funciei swap, care schimb ntre ei pointerii a dou linii.
swap( char *px[ ] , char *py[ ] ) {
char *t emp;
t emp = *px;
*px = *py;
*py = t emp;
}
__________________________________________________________________________
106
10. Structuri i reuniuni
O structur este o colecie de una sau mai multe variabile, de
acelai tip sau de tipuri diferite, grupate mpreun sub un singur
nume pentru a putea fi tratate mpreun (n alte limbaje, structurile se
numesc articole).
Structurile ajut la organizarea datelor complicate, n special n
programele mari, deoarece permit unui grup de variabile legate s fie
tratate ca o singur entitate. Vom ilustra n acest capitol modul de
utilizare a structurilor.
10.1. Elemente de baz
S revenim asupra rutinei de conversie a datei prezentat n
capitolul 9.
O dat const din zi, lun i an, eventual numrul zilei din an i
numele lunii. Aceste cinci variabile pot fi grupate ntr-o singur
structur astfel:
st r uct dat e {
i nt day;
i nt mont h;
i nt year ;
i nt year day;
char mon_name[ 4] ;
};
Cuvntul cheie st r uct introduce o declaraie de structur care
este o list de declaraii nchise n acolade.
Cuvntul st r uct poate fi urmat opional de un nume, numit
marcaj de structur sau etichet de structur, cum este n exemplul
nostru numele dat e. Acest marcaj denumete acest tip de structur
i poate fi folosit n continuare ca o prescurtare pentru declaraia de
structur detaliat creia i este asociat.
__________________________________________________________________________
107
Elementele sau variabilele menionate ntr-o structur se numesc
membri ai structurii. Un membru al structurii sau o etichet i o
variabil oarecare, nemembru, pot avea acelai nume fr a genera
conflicte, deoarece ele vor fi ntotdeauna deosebite una de alta din
context.
Acolada dreapt care ncheie o list de membri ai unei structuri
poate fi urmat de o list de variabile, la fel ca i n cazul tipurilor de
baz. De exemplu:
st r uct {. . .} x, y, z;
este din punct de vedere sintactic analog cu:
i nt x, y, z;
n sensul c fiecare declaraie declar pe x, y i z ca variabile de
tipul numit (structur n primul caz i ntreg n al doilea) i cauzeaz
alocarea de spaiu pentru ele.
O declaraie de structur care nu este urmat de o list de
variabile nu aloc memorie; ea descrie numai un ablon, o form de
structur. Dac structura este marcat sau etichetat, atunci marcajul
ei poate fi folosit mai trziu pentru definirea unor alte variabile de tip
structur, cu acelai ablon ca structura marcat. De exemplu, fiind
dat declaraia:
st r uct dat e d;
ea definete variabila d, ca o structur de acelai fel (ablon) ca
structura date.
O structur extern sau static poate fi iniializat, atand dup
definiia ei o list de iniializatori pentru componente, de exemplu:
st r uct dat e d = {4, 7, 1984, 185, " i ul i e" };
Un membru al unei structuri este referit printr-o expresie de
forma:
nume-structur. membru
n care operatorul membru de structur . leag numele membrului
de numele structurii. Ca exemplu fie atribuirea:
l eap = ( d. year %4==0) && ( d. year %100! =0)
| | ( d. year %400==0) ;
sau verificarea numelui lunii:
i f ( st r cmp( d. mon_name, " august " ) ==0) . . .
__________________________________________________________________________
108
Structurile pot fi imbricate; o nregistrare de stat de plat, de
exemplu, poate fi de urmtoarea form:
st r uct per son {
char name[ NAMESI ZE] ;
char addr ess[ ADRSI ZE] ;
l ong zi pcode;
l ong ss_number ;
doubl e sal ar y;
st r uct dat e bi r t hdat e;
st r uct dat e hi r edat e;
};
Structura person conine dou structuri de ablon date.
Declaraia:
st r uct per son emp;
definete i aloc o structur cu numele emp de acelai ablon ca i
per son. Atunci:
emp. bi r t hdat e. mont h
se refer la luna de natere. Operatorul de membru de structur .
este asociativ de la stnga la dreapta.
10.2. Structuri i funcii
Exist un numr de restricii asupra structurilor n limbajul C.
Singurele operaii care se pot aplica unei structuri snt accesul la un
membru al structurii i considerarea adresei ei cu ajutorul
operatorului &. Acest lucru implic faptul c structurile nu pot fi
atribuite sau copiate ca entiti i c ele nu pot fi transmise ca
argumente la funcii i nici returnate din funcii. Structurile de clas
automatic, ca i masivele de aceeai clas, nu pot fi iniializate; pot fi
iniializate numai structurile externe i statice, regulile de iniializare
fiind aceleai ca pentru masive.
Pointerii la structuri nu se supun ns acestor restricii, motiv
pentru care structurile i funciile pot coexista i conlucra prin
intermediul pointerilor.
__________________________________________________________________________
109
Ca un exemplu, s rescriem programul de conversie a datei, care
calculeaz ziua anului, din lun i zi.
day_of _year ( st r uct dat e *pd) {
/* calculul zilei anului */
i nt i , day, l eap;
day = pd- >day;
l eap = ( pd- >year %4==0) &&
( pd- >year %100! ==0) | |
( pd- >year %400==0) ;
f or ( i =1; i <pd- >mont h; i ++)
day += day_t ab[ l eap] [ i ] ;
r et ur n day;
}
Declaraia:
st r uct dat e * pd;
indic faptul c pd este un pointer la o structur de ablonul lui date.
Notaia:
pd- >year
indic faptul c se refer membrul "year " al acestei structuri. n
general, dac p este un pointer la o structur p- >membru-structur
se refer la un membru particular (operatorul - > se formeaz din
semnul minus urmat de semnul mai mare).
Deoarece pd este pointer la o structur, membrul year poate fi de
asemenea referit prin:
( *pd) . year
Notaia "- >" se impune ca un mod convenabil de prescurtare. n
notaia ( *pd) . year , parantezele snt necesare deoarece precedena
operatorului membru de structur . este mai mare dect cea a
operatorului *.
Ambii operatori . i - > snt asociativi de la stnga la dreapta,
astfel nct:
p- >q- >membru
emp. bi r t hdat e. mont h
snt de fapt:
__________________________________________________________________________
110
( p- >q) - >membru
( emp. bi r t hdat e) . mont h
Operatorii - > i . ai structurilor, mpreun cu ( ) pentru
listele de argumente i [ ] pentru indexare se gsesc n vrful listei de
preceden (vezi seciunea 4.16), fiind din acest punct de vedere
foarte apropiai. Astfel, fiind dat declaraia:
st r uct {
i nt x;
i nt *y; } *p;
unde p este un pointer la o structur, atunci expresia:
++p- >x
incrementeaz pe x, nu pointerul p, deoarece operatorul - > are o
preceden mai mare dect ++. Parantezele pot fi folosite pentru a
modifica ordinea operatorilor dat de precedena. Astfel:
( ++p) - >x
incrementeaz mai nti pe p i apoi acceseaz elementul x, din
structura nou pointat.
n expresia ( p++) - >x se acceseaz mai nti x, apoi se
incrementeaz pointerul p.
n mod analog, *p- >y indic coninutul adresei pe care o indic
y. Expresia *p- >y++acceseaz mai nti ceea ce indic y i apoi
incrementeaz pe y. Expresia ( *p- >y) ++incrementeaz ceea ce
indic y. Expresia *p++- >y acceseaz ceea ce indic y i apoi
incrementeaz pointerul p.
10.3. Masive de structuri
Structurile snt n mod special utile pentru tratarea masivelor de
variabile legate prin context. Pentru exemplificare vom considera un
program care numr intrrile fiecrui cuvnt cheie dintr-un text.
Pentru aceasta avem nevoie de un masiv de iruri de caractere, pentru
pstrarea numelor cuvintelor cheie i un masiv de ntregi pentru a
memora numrul apariiilor. O posibilitate este de a folosi dou
masive paralele keywor d i keycount declarate prin:
char *keywor d[ NKEYS] ;
__________________________________________________________________________
111
i nt keycount [ NKEYS] ;
respectiv unul de pointeri la iruri de caractere i cellalt de ntregi.
Fiecrui cuvnt cheie i corespunde perechea:
char *keywor d;
i nt keycount ;
astfel nct putem considera cele dou masive ca fiind un masiv de
perechi. Atunci, declaraia de structur:
st r uct key {
char *keywor d;
i nt keycount ;
} keyt ab[ NKEYS] ;
definete un masiv keyt ab de structuri de acest tip i aloc memorie
pentru ele. Fiecare element al masivului keyt ab este o structur de
acelai ablon ca i structura key.
Definiia masivului keyt ab poate fi scris i sub forma:
st r uct key {
char *keywor d;
i nt keycount ;
};
st r uct key keyt ab[ NKEYS] ;
Deoarece masivul de structuri keyt ab conine, n cazul nostru,
o mulime constant de cuvinte cheie, este mai uor de iniializat o
dat pentru totdeauna chiar n locul unde este definit. Iniializarea
structurilor este o operaie analoag cu iniializarea unui masiv n
sensul c definiia este urmat de o list de iniializatori nchii n
acolade.
Atunci iniializarea masivului de structuri keyt ab va fi
urmtoarea:
st r uct key {
char * keywor d;
i nt keycount ;
} keyt ab[ ] = {
" br eak" , 0,
" case" , 0,
__________________________________________________________________________
112
" char " , 0,
/* ... */
" whi l e" , 0};
Iniializatorii snt perechi care corespund la membrii structurii.
Iniializarea ar putea fi fcut i incluznd iniializatorii fiecrei
structuri din masiv n acolade ca n:
{" br eak", 0}, {" case" , 0}. . . .
dar parantezele interioare nu snt necesare dac iniializatorii snt
variabile simple sau iruri de caractere i dac toi iniializatorii snt
prezeni.
Compilatorul va calcula, pe baza iniializatorilor, dimensiunea
masivului de structuri keyt ab motiv pentru care, la iniializare, nu
este necesar indicarea dimensiunii masivului.
Programul de numrare a cuvintelor cheie ncepe cu definirea
masivului de structuri keyt ab. Rutina principal mai n citete
textul de la intrare prin apel repetat la o funcie get wor d, care
extrage din intrare cte un cuvnt la un apel. Fiecare cuvnt este apoi
cutat n tabelul keyt ab cu ajutorul unei funcii de cutare
bi nar y, descris n seciunea 7.5. Lista cuvintelor cheie trebuie s
fie n ordine cresctoare pentru ca funcia bi nar y s lucreze corect.
Dac cuvntul cercetat este un cuvnt cheie atunci funcia bi nar y
returneaz numrul de ordine al cuvntului n tabelul cuvintelor cheie,
altfel returneaz 1.
#def i ne MAXWORD 20
bi nar y( char *wor d, st r uct key t ab[ ] ,
i nt n) {
i nt l ow, hi gh, mi d, cond;
l ow = 0;
hi gh = n - 1;
whi l e ( l ow<=hi gh) {
mi d =( l ow + hi gh) / 2;
i f (
( cond=st r cmp( wor d, t ab[ mi d] . keywor d) )
<0)
__________________________________________________________________________
113
hi gh = mi d - 1;
el se
i f ( cond>0) l ow = mi d + 1;
el se
r et ur n mi d;
}
r et ur n - 1;
}
mai n( ) { /* numr cuvintele cheie */
i nt n, t ;
char wor d[ MAXWORD] ;
whi l e ( ( t =get wor d( wor d, MAXWORD) ) ! =EOF)
i f ( t ==LETTER)
i f (
( n=bi nar y( wor d, keyt ab, NKEYS) )
>=0)
keyt ab[ n] . keycount ++;
f or ( n=0; n<NKEYS; n++)
i f ( keyt ab[ n] . keycount >0)
pr i nt f ( " %4d %s\ n" ,
keyt ab[ n] . keycount ,
keyt ab[ n] . keywor d) ;
}
nainte de a scrie funcia get wor d este suficient s spunem c
ea returneaz constanta simbolic LETTER de fiecare dat cnd
gsete un cuvnt n textul de intrare i copiaz cuvntul n primul ei
argument.
Cantitatea NKEYS este numrul cuvintelor cheie din keyt ab
(dimensiunea masivului de structuri). Dei putem calcula acest numr
manual, este mai simplu i mai sigur s-o facem cu calculatorul, mai
ales dac lista cuvintelor cheie este supus modificrilor. O
posibilitate de a calcula NKEYS cu calculatorul este de a termina
lista iniializatorilor cu un pointer NULL i apoi prin ciclare pe
keyt ab s detectm sfritul lui. Acest lucru este mai mult dect
necesar deoarece dimensiunea masivului de structuri este perfect
__________________________________________________________________________
114
determinat n momentul compilrii. Numrul de intrri se determin
mprind dimensiunea masivului la dimensiunea structurii key.
Operatorul si zeof descris n seciunea 4.2 furnizeaz
dimensiunea n octei a argumentului su.
n cazul nostru, numrul cuvintelor cheie este dimensiunea
masivului keyt ab mprit la dimensiunea unui element de masiv.
Acest calcul este fcut ntr-o linie #def i ne pentru a da o valoare
identificatorului NKEYS:
#def i ne NKEYS ( si zeof ( keyt ab) /
si zeof ( st r uct key) )
S revenim acum la funcia get wor d. Programul pe care-l vom
da pentru aceast funcie este mai general dect este necesar n
aplicaia noastr, dar nu este mult mai complicat.
Funcia get wor d citete cuvntul urmtor din textul de intrare,
printr-un cuvnt nelegndu-se fie un ir de litere i cifre, cu primul
caracter liter, fie un singur caracter. Funcia returneaz constanta
simbolic LETTER dac a gsit un cuvnt, EOF dac a detectat
sfritul fiierului sau caracterul nsui, dac el nu a fost alfabetic.
get wor d( char *w, i nt l i m) {/* citete un cuvnt */
i nt t ;
whi l e ( - - l i m>0) {
t = t ype( *w++=get char ( ) ) ;
i f ( t ==EOF)
r et ur n EOF;
i f ( t ! =LETTER && t ! =DI GI T)
br eak;
}
*( w- 1) = ' \ 0' ;
r et ur n LETTER;
}
Funcia get wor d apeleaz funcia t ype pentru identificarea
tipului fiecrui caracter citit la intrare cu ajutorul funciei get char .
Versiunea funciei t ype pentru caractere ASCII este:
t ype( i nt c) { /* returneaz tipul caracterului */
__________________________________________________________________________
115
i f ( c>=' a' && c<=' z' | | c>=' A' && c<=' Z' )
r et ur n LETTER;
i f ( c>=' 0' && c<=' 9' )
r et ur n DI GI T;
r et ur n c;
}
Constantele simbolice LETTER i DI GI T pot avea orice valoare
care nu vine n contradicie cu caracterele nealfabetice i EOF. Valori
posibile pot fi:
#def i ne LETTER ' a'
#def i ne DI GI T ' 0'
10.4. Pointeri la structuri
Pentru a ilustra modul de corelare dintre pointeri i masivele de
structuri, s rescriem programul de numrare a cuvintelor cheie dintr-
un text, de data aceasta folosind pointeri, n loc de indici de masiv.
Declaraia extern a masivului de structuri keyt ab nu necesit
modificri, n timp ce funciile mai n i bi nar y da. Prezentm, n
continuare, aceste funcii modificate.
#def i ne MAXWORD 20
st r uct key *bi nar y( char *wor d, st r uct key
t ab[ ] , i nt n) {
/* caut cuvnt */
i nt cond;
st r uct key * l ow;
st r uct key *hi gh;
st r uct key *mi d;
l ow = &t ab[ 0] ;
hi gh = &t ab[ n- 1] ;
whi l e ( l ow<=hi gh) {
mi d = l ow + ( hi gh- l ow) / 2;
i f ( ( cond=st r cmp( wor d, mi d- >keywor d) ) <0)
hi gh = mi d - 1;
el se i f ( cond>0)
__________________________________________________________________________
116
l ow = mi d + 1;
el se r et ur n mi d;
}
r et ur n NULL;
}
mai n( ) {/* numr cuvintele cheie, versiune cu pointeri */
i nt t ;
char wor d[ MAXWORD] ;
st r uct key *bi nar y( ) , *p;
whi l e ( ( t =get wor d( wor d. MAXWORD) ) ! =EOF)
i f ( t ==LETTER)
i f ( ( p=bi nar y( wor d, keyt ab, NKEYS) )
! = NULL)
p- >keycount ++;
f or ( p=keyt ab; p<keyt ab+NKEYS; p++)
i f ( p- >keycount >0)
pr i nt f ( " %4d %s\ n" , p- >keycount ,
p- >keywor d) ;
}
S observm cteva lucruri importante n acest exemplu. n
primul rnd, declaraia funciei bi nar y trebuie s indice c ea
returneaz un pointer la o structur de acelai ablon cu structura
key, n loc de un ntreg. Acest lucru este declarat att n funcia
principal mai n ct i n funcia bi nar y. Dac bi nar y gsete un
cuvnt n structura key, atunci returneaz un pointer la el; dac nu-1
gsete, returneaz NULL. n funcie de aceste dou valori returnate,
funcia mai n semnaleaz gsirea cuvntului prin incrementarea
cmpului keycount corespunztor cuvntului sau citete urmtorul
cuvnt.
n al doilea rnd, toate operaiile de acces la elementele masivului
de structuri keyt ab se fac prin intermediul pointerilor. Acest lucru
determin o modificare semnificativ n funcia bi nar y. Calculul
elementului mijlociu nu se mai poate face simplu prin:
mi d = ( l ow + hi gh) / 2
__________________________________________________________________________
117
deoarece adunarea a doi pointeri este o operaie ilegal, nedefinit.
Aceast instruciune trebuie modificat n:
mi d = l ow + ( hi gh- l ow) / 2
care face ca mi d s pointeze elementul de la jumtatea distanei
dintre l owi hi gh.
S mai observm iniializarea pointerilor l owi hi gh, care este
perfect legal, deoarece este posibil iniializarea unui pointer cu o
adres a unui element deja definit.
n funcia mai n avem urmtorul ciclu:
f or ( p=keyt ab; p<keyt ab+NKEYS; p++) . . .
Dac p este un pointer la un masiv de structuri, orice operaie
asupra lui p ine cont de dimensiunea unei structuri, astfel nct p++
incrementeaz pointerul p la urmtoarea structur din masiv, adunnd
la p dimensiunea corespunztoare a unei structuri. Acest lucru nu
nseamn c dimensiunea structurii este egal cu suma dimensiunilor
membrilor ei deoarece din cerine de aliniere a unor membri se pot
genera goluri ntr-o structur.
n sfrit, cnd o funcie returneaz un tip complicat i are o list
complicat de argumente, ca n:
st r uct key *bi nar y( char *wor d, st r uct key
t ab, i nt n)
funcia poate fi mai greu vizibil i detectabil cu un editor de texte.
Din acest motiv, se poate opta i pentru urmtoarea form:
st r uct key *bi nar y( wor d, t ab, n)
char *wor d; st r uct key t ab; i nt n;
unde nainte de acolada de deschidere se precizeaz tipul fiecrui
parametru.
Alegei forma care v convine i care vi se pare mai sugestiv.
10.5. Structuri auto-referite
S considerm problema mai general, a numrului apariiilor
tuturor cuvintelor dintr-un text de intrare. Deoarece lista cuvintelor
nu este cunoscut dinainte, n-o putem sorta folosind algoritmul de
cutare binar, ca n paragraful precedent. Nu putem utiliza nici o
__________________________________________________________________________
118
cutare liniar pentru fiecare cuvnt, pe msura apariiei lui pentru a
vedea dac a mai fost prezent sau nu, pentru c timpul de execuie al
programelor ar crete ptratic cu numrul cuvintelor de la intrare.
Un mod de a organiza datele pentru a lucra eficient cu o list de
cuvinte arbitrare este de a pstra mulimea de cuvinte, tot timpul
sortat, plasnd fiecare nou cuvnt din intrare pe o poziie
corespunztoare, relativ la intrrile anterioare. Dac am realiza acest
lucru prin deplasarea cuvintelor ntr-un masiv liniar, programul ar
dura, de asemenea, foarte mult. De aceea, pentru rezolvarea eficient
a acestei probleme vom folosi o structur de date numit arbore
binar.
Fiecare nod al arborelui va reprezenta un cuvnt distinct din
intrare i va conine urmtoarea informaie:
- un pointer la cuvnt;
- un contor pentru numrul de apariii;
- un pointer la descendentul stng al cuvntului;
- un pointer la descendentul drept al cuvntului. Nici un nod al
arborelui nu va avea mai mult dect doi descendeni dar poate avea un
descendent sau chiar nici unul.
Arborele se construiete astfel nct pentru orice nod, sub-
arborele stng al su conine numai cuvintele care snt mai mici dect
cuvntul din nod, iar sub-arborele drept conine numai cuvinte, care
snt mai mari dect cuvntul din nod, compararea fcndu-se din punct
de vedere lexicografic.
Pentru a ti dac un cuvnt nou din intrare exist deja n arbore se
pornete de la nodul rdcin i se compar noul cuvnt cu cuvntul
memorat n nodul rdcin. Dac ele coincid se incrementeaz
contorul de numrare a apariiilor pentru nodul rdcin i se va citi
un nou cuvnt din intrare.
Dac noul cuvnt din intrare este mai mic dect cuvntul memorat
n nodul rdcin, cutarea continu cu descendentul stng, altfel se
investigheaz descendentul drept. Dac nu exist nici un descendent
pe direcia cerut, noul cuvnt nu exist n arbore i va fi inserat pe
poziia descendentului corespunztor. Se observ c acest proces de
cutare este recursiv, deoarece cutarea din fiecare nod utilizeaz o
cutare ntr-unul dintre descendenii si.
__________________________________________________________________________
119
Prin urmare se impune de la sine ca rutinele de inserare n arbore
i de imprimare s fie recursive.
Revenind la descrierea unui nod, el apare ca fiind o structur cu
patru componente:
st r uct t node { /* nodul de baz */
char *wor d; /* pointer la cuvnt */
i nt count ; /* numrtor de apariii */
st r uct t node *l ef t ; /* descendent stng */
st r uct t node *r i ght ; /* descendent drept */
};
Aceast declaraie recursiv a unui nod este perfect legal,
deoarece o structur nu poate conine ca i component o intrare a ei
nsi dar poate conine un pointer la o structur de acelai ablon cu
ea.
Declaraia:
st r uct t node *l ef t ;
declar pe l ef t ca fiind un pointer la structur (nod) i nu o
structur nsi.
n program vom folosi rutinele get wor d, pentru citirea unui
cuvnt din intrare, al l oc pentru rezervarea de spaiu necesar
memorrii unui cuvnt i alte cteva rutine pe care le cunoatem deja.
Rutina principal mai n citete prin intermediul rutinei getword un
cuvnt, i l plaseaz n arbore prin rutina t r ee.
#def i ne MAXWORD 20
mai n( ) { /* contorizare apariii cuvinte */
st r uct t node *r oot , *t r ee( ) ;
char wor d[ MAXWORD] ;
i nt t ;
r oot = NULL;
whi l e ( ( t =get wor d( wor d, MAXWORD) ) ! =EOF)
i f ( t ==LETTER)
r oot = t r ee( r oot , wor d) ;
t r eepr i nt ( r oot ) ;
}
__________________________________________________________________________
120
Rutina mai n gestioneaz fiecare cuvnt din intrare ncepnd cu
cel mai nalt nivel al arborelui (rdcina). La fiecare pas, cuvntul din
intrare este comparat cu cuvntul asociat rdcinii i este apoi
transmis n jos, fie descendentului stng, fie celui drept, printr-un apel
recursiv la rutina t r ee. n acest proces, cuvntul fie exist deja,
undeva n arbore, caz n care contorul lui de numrare a apariiilor se
incrementeaz, fie cutarea continu pn la ntlnirea unui pointer
NULL, caz n care nodul trebuie creat i adugat arborelui. Cnd se
creeaz un nod nou, rutina t r ee returneaz un pointer la el, care
apoi este introdus n nodul de origine (adic n nodul al crui
descendent este noul nod) n cmpul l ef t sau r i ght dup cum
noul cuvnt este mai mic sau mai mare fa de cuvntul origine.
Rutina t r ee, care returneaz un pointer la o structur de ablon
t node are urmtorul cod:
st r uct t node *t r ee( st r uct t node *p,
char *w) { /* introduce cuvntul w n nodul p */
st r uct t node *t al l oc( i nt n) ;
char *st r sav( char *s) ;
i nt cond;
i f ( p==NULL) { /* a sosit un nou cuvnt */
p = t al l oc( ) ; /* creeaz un nod nou */
p- >wor d = st r sav( w) ;
p- >count = 1;
p- >l ef t = p- >r i ght = NULL;
}
el se
i f ( ( cond=st r cmp( w, p- >wor d) ) ==0)
p- >count ++;
el se
i f ( cond<0) /* noul cuvnt mai mic */
p- >l ef t = t r ee( p- >l ef t , w) ;
el se /* noul cuvnt mai mare */
p- >r i ght = t r ee( p- >r i ght , w) ;
r et ur n p;
__________________________________________________________________________
121
}
Memoria pentru noul nod se aloc de ctre rutina t al l oc, care
este o adaptare a rutinei al l oc, pe care am vzut-o deja. Ea
returneaz un pointer la un spaiu liber, n care se poate nscrie noul
nod al arborelui. Vom discuta rutina t al l oc mai trziu. Noul cuvnt
se copiaz n acest spaiu cu ajutorul rutinei st r sav, care returneaz
un pointer la nceputul cuvntului, contorul de apariii se iniializeaz
la 1 i pointerii ctre cei doi descendeni se fac NULL. Aceast parte
de cod se execut numai cnd se adaug un nou nod.
Rutina t r eepr i nt tiprete arborele astfel nct pentru fiecare
nod se imprim sub-arborele lui stng, adic toate cuvintele mai mici
dect cuvntul curent, apoi cuvntul curent i la sfrit sub-arborele
drept, adic toate cuvintele mai mari dect cuvntul curent. Rutina
t r eepr i nt este una din cele mai tipice rutine recursive.
t r eepr i nt ( st r uct t node *p) {
/* tiprete arborele p recursiv */
i f ( p! =NULL) {
t r eepr i nt ( p- >l ef t ) ;
pr i nt f ( " %5d %s\ n" , p- >count , p- >wor d) ;
t r eepr i nt ( p- >r i ght ) ;
}
}
Este important de reinut faptul c n algoritmul de cutare n
arbore, pentru a ajunge la un anumit nod, se parcurg toate nodurile
precedente, pe ramura respectiv (stng sau dreapt), ncepnd
ntotdeauna cu nodul rdcin. Dup fiecare ieire din rutina t r ee,
din cauza recursivitii, se parcurge acelai drum, de data aceasta de
la nodul gsit spre rdcina arborelui, refcndu-se toi pointerii
drumului parcurs.
Dac considerai ca nu ai neles suficient de bine recursivitatea,
desenai-v un arbore i imprimai-l cu ajutorul rutinei t r eepr i nt ,
avnd grij s memorai fiecare ieire din t r ee i t r eepr i nt .
O observaie legat de acest exemplu: dac arborele este
nebalansat, adic cuvintele nu sosesc n ordine aleatoare din punct
__________________________________________________________________________
122
de vedere lexicografic, atunci timpul de execuie al programului
poate deveni foarte mare. Cazul limit n acest sens este acela n care
cuvintele de la intrare snt deja n ordine, (cresctoare sau
descresctoare), caz n care programul nostru simuleaz o cutare
liniar ntr-un mod destul de costisitor.
S ne oprim puin asupra alocrii de memorie. Cu toate c se
aloc diferite tipuri de obiecte, este de preferat s existe un singur
alocator de memorie ntr-un program. Relativ la acest alocator de
memorie se pun doua probleme: n primul rnd cum poate satisface el
condiiile de aliniere ale obiectelor de un anumit tip (de exemplu
ntregii trebuie alocai la adrese pare); n al doilea rnd cum se poate
declara c alocatorul returneaz pointeri la tipuri diferite de obiecte.
Cerinele de aliniere pot fi n general rezolvate cu uurin pe
seama unui spaiu care se pierde, dar care este nesemnificativ ca
dimensiune. De exemplu, alocatorul al l oc returneaz totdeauna un
pointer la o adres par. n cazul n care cererea de alocare poate fi
satisfcut i de o adres impar (pentru iruri de caractere, de
exemplu) se pierde un caracter.
n ceea ce privete declararea tipului alocatorului al l oc (adic a
tipului de obiect pe care l indic pointerul returnat de al l oc), un
foarte bun procedeu n limbajul C este de a declara c funcia al l oc
returneaz un pointer la char i apoi s convertim explicit acest
pointer la tipul dorit printr-un cast. Astfel dac p este declarat n
forma:
char *p;
atunci:
( st r uct t node *) p;
convertete pe p dintr-un pointer la char ntr-un pointer la o
structur de ablon t node, dac el apare ntr-o expresie. i atunci, o
versiune a alocatorului t al l oc poate fi urmtoarea:
st r uct t node *t al l oc( ) {
char *al l oc( ) ;
r et ur n ( st r uct t node *) al l oc
( si zeof ( st r uct t node) ) ;
}
__________________________________________________________________________
123
10.6. Cutare n tabele
O alt problem legat de definirea i utilizarea structurilor este
cutarea n tabele. Cnd se ntlnete de exemplu, o linie de forma:
#def i ne YES 1
simbolul YES i textul de substituie 1 se memoreaz ntr-o tabel.
Mai trziu, ori de cte ori textul YES va aprea n instruciuni, el se va
nlocui cu constanta 1.
Crearea i gestionarea tabelelor de simboluri este o problem de
baz n procesul de compilare. Exist dou rutine principale care
gestioneaz simbolurile i textele lor de substituie. Prima,
i nst al l ( s, t ) nregistreaz simbolul s i textul de substituie t
ntr-o tabel, s i t fiind iruri de caractere. A doua, l ookup( s)
caut irul s n tabel i returneaz fie un pointer la locul unde a fost
gsit, fie NULL dac irul s nu figureaz n tabel.
Algoritmul folosit pentru crearea i gestionarea tabelei de
simboluri este o cutare pe baz de hashing. Fiecrui simbol i se
calculeaz un cod hash astfel: se adun codurile ASCII ale
caracterelor simbolului i se ia restul provenit din mprirea
numrului obinut din adunare i dimensiunea tabelului. Astfel,
fiecrui simbol i se asociaz un cod hash Hcare verific relaia:
0<=H<0x100 (n hexazecimal)
Codul hash astfel obinut va fi folosit apoi ca un indice ntr-o
tabel de pointeri. Un element al acestei tabele (masiv) indic
nceputul unui lan de blocuri care descriu simboluri cu acelai cod
hash. Dac un element al tabelei este NULL nseamn c nici un
simbol nu are valoarea respectiv de hashing.
Un bloc dintr-un lan indicat de un element al tabelei este o
structur care conine un pointer la simbol, un pointer la textul de
substituie i un pointer la urmtorul bloc din lan. Un pointer NULL
la urmtorul bloc din lan indic sfritul lanului.
ablonul unei structuri (nod) este urmtorul:
st r uct nl i st {
char *name;
char *def ;
__________________________________________________________________________
124
st r uct nl i st *next ; / * urmtoarea intrare n lan */
};
Tabelul de pointeri care indic nceputurile lanului de blocuri ce
descriu simboluri de acelai cod hash este:
#def i ne HASHSI ZE 0x100
st at i c st r uct nl i st *hasht ab[ HASHSI ZE] ;
Algoritmul de hashing pe care-l prezentm nu este cel mai bun
posibil, dar are meritul de a fi extrem de simplu:
hash( char *s) {
/* formeaz valoarea hash pentru irul s */
i nt hashval ;
f or ( hashval =0; *s! =' \ 0' ; )
hashval += *s++;
r et ur n hashval %HASHSI ZE;
}
Algoritmul de hashing produce un indice n masivul de pointeri
hasht ab. n procesul de cutare a unui simbol, dac el exist, el
trebuie s fie n lanul de blocuri care ncepe la adresa coninut de
elementul din hasht ab cu indicele respectiv.
Cutarea n tabela de simboluri hasht ab se realizeaz cu
funcia l ookup. Dac simbolul cutat este prezent undeva n lan,
funcia returneaz un pointer la el; altfel returneaz NULL.
st r uct nl i st *l ookup( char *s) {
/* caut irul s n hasht ab */
st r uct nl i st *np;
f or ( np=hasht ab[ hash( s) ] ; np! =NULL;
np=np- >next )
i f ( st r cmp( s, np- >name) ==0)
r et ur n np; /* s-a gsit s */
r et ur n NULL; /* nu s-a gsit s */
}
__________________________________________________________________________
125
Rutina i nst al l folosete funcia l ookup pentru a determina
dac simbolul nou care trebuie introdus n lan este deja prezent sau
nu. Dac mai exist o definiie anterioar pentru acest simbol, ea
trebuie nlocuit cu definiia nou. Altfel, se creeaz o intrare nou
pentru acest simbol, care se introduce la nceputul lanului. Funcia
i nst al l returneaz NULL, dac din anumite motive nu exist
suficient spaiu pentru crearea unui bloc unu.
st r uct nl i st *i nst al l ( char *name, char
*def ) { /* scrie (nume, def ) n ht ab */
st r uct nl i st *np, *l ookup( ) ;
char *st r sav( ) , *al l oc( ) ;
i nt hashval ;
i f ( ( np=l ookup( name) ) ==NULL) { /* nu s-a gsit */
np = ( st r uct nl i st *) al l oc( si zeof ( *np) ) ;
i f ( np==NULL)
r et ur n NULL; /* nu exist spaiu */
i f ( ( np- >name=st r sav( name) ) ==NULL)
r et ur n NULL;
hashval = hash( np- >name) ;
np- >next = hasht ab[ hashval ] ;
hasht ab[ hashval ] = np;
}
el se /* nodul exist deja */
f r ee( np- >def ) ; /* elibereaz definiia veche */
i f ( ( np- >def =st r sav( def ) ) ==NULL)
r et ur n NULL;
r et ur n np;
}
Deoarece apelurile la funciile al l oc i f r ee pot aprea n
orice ordine i deoarece alinierea conteaz, versiunea simpl a
funciei alloc, prezentat n capitolul 9 nu este adecvat aici. n
biblioteca standard exist funcii de alocare fr restricii, care se
apeleaz implicit sau explicit de ctre utilizator dintr-un program
scris n C pentru a obine spaiul de memorie necesar. Deoarece i
alte aciuni dintr-un program pot cere spaiu de memorie ntr-o
__________________________________________________________________________
126
manier asincron, spaiul de memorie gestionat de funcia al l oc
poate s fie necontiguu. Astfel, spaiul liber de memorie este pstrat
sub forma unui lan de blocuri libere, fiecare bloc coninnd o
dimensiune, un pointer la urmtorul bloc i spaiul liber propriu-zis.
Blocurile snt pstrate n ordinea cresctoare a adreselor iar, ultimul
bloc, de adresa cea mai mare, indic primul bloc, prin pointerul lui la
blocul urmtor din lan, astfel nct lanul este circular.
Cnd se lanseaz o cerere, se examineaz lista spaiului liber, pn
se gsete un bloc suficient de mare pentru cererea respectiv. Dac
blocul are exact dimensiunea cerut, el se elibereaz din lanul
blocurilor libere i este returnat utilizatorului. Dac blocul este mai
mare se descompune, astfel nct partea cerut se transmite
utilizatorului, iar partea rmas se introduce napoi n lista de spaiu
liber. Dac nu se gsete un bloc suficient de mare pentru cererea
lansat se caut un alt bloc de memorie.
Eliberarea unei zone de memorie prin intermediul rutinei f r ee
cauzeaz, de asemenea, o cutare n lista de spaiu liber, pentru a gsi
locul corespunztor de inserare a blocului de memorie eliberat. Dac
blocul de memorie eliberat este adiacent cu un bloc din lista de spaiu
liber la orice parte a sa, el este alipit la acel bloc, crendu-se un bloc
mai mare, astfel ca memoria s nu devin prea fragmentat.
Determinarea adiacenei este uurat de faptul c lista de spaiu liber
se pstreaz n ordinea cresctoare a adreselor de memorie.
Exemplul de utilizare a acestor funcii iniializeaz elementele
masivului hasht ab cu NULL. n continuare se ateapt de la
tastatur introducerea unui nume i a unei definiii pentru acest nume.
Dac numele introdus nu exist n lista hasht ab atunci se afieaz
un mesaj corespunztor, altfel se afieaz vechea definiie care este
apoi nlocuit de noua definiie introdus.
mai n( ) {
char num[ 30] , def [ 30] ;
i nt i ;
st r uct nl i st *np;
f or ( i =0; i <HASHSI ZE; i ++)
hasht ab[ i ] = NULL;
__________________________________________________________________________
127
do {
get wor d( num) ; get wor d( def ) ;
i f ( ( np=l ookup( num) ) ==NULL)
pr i nt f ( " New name\ n" ) ;
el se
pr i nt f ( " Ol d def i ni t i on: %s\ n" ,
np- >def ) ;
i nst al l ( num, def ) ;
} whi l e ( 1) ;
}
10.7. Cmpuri
Un cmp se definete ca fiind o mulime de bii consecutivi dintr-
un cuvnt sau ntreg. Adic din motive de economie a spaiului de
memorie, este util mpachetarea unor obiecte ntr-un singur cuvnt
main. Un caz frecvent de acest tip este utilizarea unui set de flaguri,
fiecare pe un bit, pentru tabela de simboluri a unui compilator.
Fiecare simbol dintr-un program are anumite informaii asociate
lui, cum snt de exemplu, clasa de memorie, tipul, dac este sau nu
cuvnt cheie .a.m.d. Cel mai compact mod de a codifica aceste
informaii este folosirea unui set de flaguri, de cte un bit, ntr-un
singur ntreg sau caracter.
Modul cel mai uzual pentru a face acest lucru este de a defini un
set de mti, fiecare masc fiind corespunztoare poziiei bitului m
interiorul caracterului sau cuvntului. De exemplu:
#def i ne KEYWORD 01
#def i ne EXTERNAL 02
#def i ne STATI C 04
definesc mtile KEYWORD, EXTERNAL i STATI Ccare se refer la
biii 0, 1 i respectiv 2 din caracter sau cuvnt. Atunci accesarea
acestor bii se realizeaz cu ajutorul operaiilor de deplasare, mascare
i complementare, descrii ntr-un capitol anterior. Numerele trebuie
s fie puteri ale lui 2.
Expresii de forma:
__________________________________________________________________________
128
f l ags | = EXTERNAL | STATI C;
apar frecvent i ele seteaz biii 1 i 2 din caracterul sau ntregul
f l ags (n exemplul nostru)
n timp ce expresia:
f l ags &= ( EXTERNAL | STATI C) ;
selecteaz biii 1 i 2 din flags.
Expresia:
i f ( f l ags & ( EXTERNAL | STATI C) ) . . .
este adevrat cnd cel puin unul din biii 1 sau 2 din flags este unu.
Expresia:
i f ( ! ( f l ags & ( EXTERNAL | STATI C) ) ) . . .
este adevrat cnd biii 1 i 2 din flags snt ambii zero.
Limbajul C ofer aceste expresii, ca o alternativ, pentru
posibilitatea de a defini i de a accesa biii dintr-un cuvnt, n mod
direct, folosind operatorii logici pe bii.
Sintaxa definiiei cmpului i a accesului la el se bazeaz pe
structuri. De exemplu construciile #def i ne din exemplul de mai
sus pot fi nlocuite prin definirea a trei cmpuri:
st r uct {
unsi gned i s_keywor d: 1;
unsi gned i s_ext er nal : 1;
unsi gned i s_st at i c: 1;
} f l ags;
Aceast construcie definete variabila f l ags care conine 3
cmpuri, fiecare de cte un bit. Numrul care urmeaz dup :
reprezint lungimea cmpului n bii. Cmpurile snt declarate
unsi gned pentru a sublinia c ele snt cantiti fr semn. Pentru a
ne referi la un cmp individual din variabila f l ags folosim o notaie
similar cu notaia folosit pentru membrii structurilor.
f l ags. i s_keywor d
f l ags. i s_st at i c
__________________________________________________________________________
129
Cmpurile se comport ca nite ntregi mici fr semn i pot
participa n expresii aritmetice ca orice ali ntregi. Astfel, expresiile
anterioare pot fi scrise mai natural sub forma urmtoare:
f l ags. i s_ext er n = f l ags. i s_st at i c = 1;
pentru setarea biilor 1 i 2 din variabila f l ags,
f l ags. i s_ext er n = f l ags. i s_st at i c = 0;
pentru tergerea biilor, iar:
i f ( f l ags. i s_ext er n==0 &&
f l ags. i s_st at i c==0)
pentru testarea lor.
Un cmp nu trebuie s depeasc limitele unui cuvnt. n caz
contrar, cmpul se aliniaz la limita urmtorului cuvnt. Cmpurile nu
necesit s fie denumite. Un cmp fr nume, descris numai prin
caracterul : i lungimea lui n bii este folosit pentru a rezerva
spaiu n vederea alinierii urmtorului cmp. Lungimea zero a unui
cmp poate fi folosit pentru forarea alinierii urmtorului cmp la
limita unui nou cuvnt, el fiind presupus a conine tot cmpuri i nu
un membru obinuit al structuri, deoarece n acest ultim caz, alinierea
se face n mod automat. Nici un cmp nu poate fi mai lung dect un
cuvnt. Cmpurile se atribuie de la dreapta la stnga.
Cmpurile nu pot constitui masive, nu au adrese, astfel nct
operatorul '&' nu se poate aplica asupra lor.
10.8. Reuniuni
O reuniune este o variabil care poate conine, la momente
diferite, obiecte de diferite tipuri i dimensiuni; compilatorul este cel
care ine evidena dimensiunilor i aliniamentului.
Reuniunile ofer posibilitatea ca mai multe tipuri diferite de date
s fie tratate ntr-o singur zon de memorie, fr a folosi n program
vreo informaie dependent de main.
S relum exemplul tabelei de simboluri a unui compilator,
presupunnd c constantele pot fi de tip i nt , f l oat sau iruri de
caractere.
__________________________________________________________________________
130
Valoarea unei constante particulare trebuie memorat ntr-o
variabil de tip corespunztor, cu toate c este mai convenabil, pentru
gestiunea tabelei de simboluri, ca valoarea s fie memorat n aceeai
zon de memorie, indiferent de tipul ei i s ocupe aceeai cantitate
de memorie. Acesta este scopul unei reuniuni: de a furniza o singur
variabil care s poat conine oricare dintre valorile unor tipuri de
date. Ca i n cazul cmpurilor, sintaxa definiiei i accesului la o
reuniune se bazeaz pe structuri. Fie definiia:
uni on u_t ag. { i nt i val ;
f l oat f val ;
char *pval ;
} uval ;
Variabila uval va fi suficient de mare ca s poat pstra pe cea
mai mare dintre cele trei tipuri de componente. Oricare dintre tipurile
de mai sus poate fi atribuit variabilei uval i apoi folosit n expresii
n mod corespunztor, adic tipul n uval este tipul ultim atribuit.
Utilizatorul este cel care ine evidena tipului curent memorat ntr-o
reuniune.
Sintactic, membrii unei reuniuni snt accesibili printr-o
construcie de forma:
nume-reuniune. membru
sau
pointer-la-reuniune- >membru
Dac variabila ut ype este utilizat pentru a ine evidena tipului
curent memorat n uval , atunci fie urmtorul cod:
i f ( ut ype==I NT)
pr i nt f ( " %d\ n" , uval . i val ) ;
el se i f ( ut ype== FLOAT)
pr i nt f ( " %f \ n" , uval . f val ) ;
el se i f ( ut ype==STRI NG)
pr i nt f ( " %s\ n" , uval . pval ) ;
el se
pr i nt f ( " t i p i ncor ect %d i n ut ype\ n",
ut ype) ;
__________________________________________________________________________
131
Reuniunile pot aprea n structuri i masive i invers. Sintaxa
pentru accesarea unui membru al unei reuniuni, dintr-o structur, sau
invers este identic cu cea pentru structurile imbricate. Pe exemplu,
n masivul de structuri symt ab[ NSYM] definit de:
st r uct {
char * name;
i nt f l ags;
i nt ut ype;
uni on {
i nt i val ;
f l oat f val ;
char *pval ;
} uval ;
} symt ab[ NSYM] ;
variabila i val se refer prin:
symt ab[ i ] . uval . i val
iar primul caracter al irului pointat de pval prin:
*symt ab[ i ] . uval . pval
De fapt, o reuniune este o structur n care toi membrii au
deplasamentul zero, structura fiind suficient de mare pentru a putea
pstra pe cel mai mare membru. Alinierea este corespunztoare
pentru toate tipurile reuniunii. Ca i la structuri, singurele operaii
permise cu reuniuni snt accesul la un membru al reuniunii i
considerarea adresei ei.
Reuniunile nu pot fi atribuite, transmise la funcii sau returnate
de ctre acestea. Pointerii la reuniuni pot fi folosii n mod similar cu
pointerii la structuri.
10.9. Declaraii de structuri, reuniuni i cmpuri
Deoarece specificatorii de structuri, reuniuni i cmpuri au
aceeai form vom prezenta sintaxa lor general n acest paragraf.
Specificator-structur-sau-reuniune:
struct-sau-union { lista-declaraiilor }
__________________________________________________________________________
132
struct-sau-union identificator { lista-declaraiilor }
struct-sau-union identificator
Struct-sau-union:
st r uct
uni on
Lista-declaraiilor este o secven de declaraii pentru membrii
structurii sau reuniunii.
Lista-declaraiilor:
declaraie-structur
declaraie-structur, lista-declaraiilor
Declaraie-structur:
specificator-tip, lista-declarator;
Lista-declarator:
declarator-structur
declarator-structur, lista-declarator
n mod obinuit, un declarator-structur este chiar un declarator
pentru un membru al structurii sau reuniunii. Un membru al structurii
poate fi constituit dintr-un numr specificat de bii, caz n care avem
de-a face cu un cmp. Lungimea lui se separ de nume prin caracterul
: Atunci:
Declarator-structur:
declarator
declarator : expresie-constant
: expresie-constant
ntr-o structur fiecare membru care nu este un cmp ncepe la o
adres corespunztoare tipului su. Astfel ntr-o structur pot exista
zone fr nume neutilizate, rezultate din motive de aliniere.
Limbajul C nu introduce restricii privind tipurile obiectelor care
pot fi declarate cmpuri.
Un specificator-structur-sau-reuniune de forma a doua declar
un identificator ca fiind eticheta (marcajul) structurii sau reuniunii.
Atunci o declaraie ulterioar poate folosi forma a treia a unui
specificator-structur-sau-reuniune.
__________________________________________________________________________
133
Etichetele de structuri permit definirea structurilor auto-referite;
de asemenea permit ca partea de declaraie a corpului structurii s fie
dat o singur dat i folosit de mai multe ori. Este interzis
declararea recursiv a unei structuri sau reuniuni, dar o structur sau
o reuniune poate conine un pointer la ea.
Dou structuri pot partaja o secven iniial comun de membri;
adic acelai membru poate aprea n dou structuri diferite dac el
are acelai tip n ambele structuri i dac toi membri precedeni lui
snt identici n cele dou structuri.
10.10. Typedef
Limbajul C ofer o facilitate numit typedef pentru a crea noi
nume de tipuri de date. Specificatorul de tip typedef-nume are
sintaxa:
typedef-nume:
declarator
ntr-o declaraie implicnd t ypedef fiecare identificator care
apare ca parte a unui declarator devine sintactic echivalent cu
cuvntul cheie rezervat pentru tipul asociat cu identificatorul. De
exemplu, declaraia:
t ypedef i nt LENGTH;
l face pe LENGTHsinonim cu i nt . Tipul LENGTHpoate fi folosit
ulterior n declaraii n acelai mod ca i tipul i nt .
LENGTH l en, maxl en;
LENGTH *l engt h[ ] ;
n mod similar, declaraia:
t ypedef char *STRI NG;
l face pe STRI NGsinonim cu char *, adic pointer la caracter, care
apoi poate fi utilizat n declaraii de tipul:
STRI NG p, l i nept r [ LI NES] , al l oc( ) ;
Se observ c tipul care se declar prin t ypedef apare pe
poziia numelui de variabil nu imediat dup cuvntul rezervat
t ypedef . Sintactic t ypedef este sinonim cu clasele de memorie
__________________________________________________________________________
134
ext er n, st at i c etc, dar nu rezerv memorie pentru variabilele
respective.
Ca un exemplu mai complicat s relum declaraia unui nod al
unui arbore, de data aceasta folosind t ypedef pentru a crea un nou
nume pentru un tip structur (vezi seciunea 10.5).
t ypedef st r uct t node {
char *wor d; /* pointer la text */
i nt count ; /* numr apariii */
st r uct t node *l ef t ; /* descendent stng */
st r uct t node *r i ght ; /* descendent drept */
} TREENODE, *TREEPTR;
Aceast declaraie creeaz dou nume noi de tipuri, numite
TREENODE, care este o structur i TREEPTR, care este un pointer la
o structur. Atunci rutina t al l oc poate fi scris sub forma:
TREEPTR t al l oc( ) {
char *al l oc( ) ;
r et ur n ( TREEPTR) al l oc( si zeof ( TREENODE) ) ) ;
}
Trebuie subliniat faptul c declaraia t ypedef nu creeaz noi
tipuri n nici un caz; ea adaug doar sinonime pentru anumite tipuri
de date, deja existente. Variabilele declarate n acest fel au exact
aceleai proprieti ca i cele declarate explicit. De fapt, t ypedef se
aseamn cu #def i ne, cu excepia faptului c n timp ce #def i ne
este tratat de preprocesor, t ypedef este tratat de ctre compilator.
De exemplu:
t ypedef i nt ( *PFI ) ( ) ;
creeaz numele PFI pentru pointer la o funcie care returneaz un
ntreg, tip care poate fi folosit ulterior ntr-un context de tipul:
PFI st r cmp, numcmp, swap;
n programul de sortare din capitolul 9.
Exist dou motive principale care impun folosirea declaraiilor
t ypedef . Primul este legat de problemele de portabilitate. Cnd se
folosesc declaraii t ypedef pentru tipuri de date care snt
__________________________________________________________________________
135
dependente de main, atunci pentru o compilare pe un alt sistem de
calcul este necesar modificarea doar a acestor declaraii nu i a
datelor din program.
Al doilea const n faptul c prin crearea de noi nume de tipuri se
ofer posibilitatea folosirii unor nume mai sugestive n program, deci
o mai rapid nelegere a programului.
__________________________________________________________________________
136
11. Intrri / ieiri
ntruct limbajul C nu a fost dezvoltat pentru un sistem particular
de operare i datorit faptului c s-a dorit realizarea unei portabiliti
ct mai mari, att a unui compilator C, ct i a programelor scrise n
acest limbaj, el nu posed faciliti de intrare / ieire.
Exist totui un sistem de intrare / ieire (sistemul I/O) constituit
dintr-un numr de subprograme care realizeaz funcii de intrare /
ieire pentru programe scrise n C, dar care nu fac parte din limbajul
C. Aceste subprograme se gsesc n biblioteca C.
Scopul acestui capitol este de a descrie cele mai utilizate
subprograme de intrare / ieire i interfaa lor cu programele scrise n
limbajul C.
11.1. Intrri i ieiri standard; fiiere
Sistemul I/O ofer utilizatorului trei "fiiere" standard de lucru.
Cuvntul fiier a fost pus ntre ghilimele, deoarece limbajul nu
definete acest tip de dat i pentru c fiierele reprezint mai
degrab nite fluxuri de intrare / ieire standard puse la dispoziia
utilizatorului. Aceste fiiere snt:
fiierul standard de intrare (st di n);
fiierul standard de ieire (st dout );
fiierul standard de afiare a mesajelor (st der r ).
Toate aceste trei fiiere snt secveniale i n momentul execuiei
unui program C snt implicit definite i deschise.
st di n i st dout snt asociate n mod normal terminalului de
la care a fost lansat programul n execuie. Sistemul I/O permite
redirectarea acestor fiiere pe alte periferice sau nchiderea lor dup
lansarea programului. Redirectarea fiierului st di n se specific prin
construcia:
<specificator-fiier
n linia de comand prin care a fost lansat programul.
__________________________________________________________________________
137
Redirectarea fiierului st dout se specific prin construcia:
>specificator-fiier
n linia de comand prin care a fost lansat programul.
Redirectarea fiierului st dout pe un alt periferic, n scopul
efecturii unei operaii de adugare (append) se specific prin
construcia :
>>specificator-fiier
st der r este ntotdeauna asociat terminalului de la care a fost
lansat programul n execuie i nu poate fi redirectat.
Pentru a se putea face o referire la aceste fiiere orice program C
trebuie s conin fiierul st di o. h, care se include printr-o linie de
forma:
#i ncl ude <st di o. h>
dac acest fiier se afl n biblioteca standard.
Pentru claritatea i lizibilitatea programelor scrise n C, ct i
pentru crearea unei imagini sugestive asupra lucrului cu fiiere, n
fiierul de definiii standard st di o. h s-a definit un nou nume de tip
de dat i anume FI LE care este o structur. Pentru a referi un fiier,
este necesar o declaraie de forma:
FI LE *f p;
unde fp va fi numele de dat cu care se va referi fiierul n orice
operaie de intrare / ieire asociat. Iat cteva informaii pstrate de
structura FI LE:
un identificator de fiier pe care sistemul de operare l asociaz
fluxului pe durata prelucrrii; acesta poate fi aflat cu ajutorul funciei
f i l eno;
adresele zonelor tampon asociate; poziia curent n aceste zone;
indicatorii de sfrit de fiier i de eroare;
alte informaii.
__________________________________________________________________________
138
11.2. Accesul la fiiere; deschidere i nchidere
Nume
fopen - deschide un flux
Declaraie
FI LE *f open( const char *pat h,
const char *mode) ;
Descriere
Funcia f open deschide fiierul al crui nume este un ir indicat
de pat h i i asociaz un flux.
Argumentul mode indic un ir care ncepe cu una din secvenele
urmtoare:
r deschide un fiier pentru citire;
r+ deschide pentru citire i scriere;
w trunchiaz fiierul la lungime zero sau creeaz un fiier pentru
scriere;
w+ deschide pentru adugare la sfrit, n citire i scriere; fiierul este
creat dac nu exist, altfel este trunchiat;
a deschide pentru adugare la sfrit, n scriere; fiierul este creat
dac nu exist;
a+ deschide pentru adugare la sfrit, n citire i scriere; fiierul este
creat dac nu exist;
Dup deschidere, n primele patru cazuri indicatorul poziiei n
flux este la nceputul fiierului, n ultimele dou la sfritul acestuia.
irul mode include de asemenea litera b (deschide un fiier
binar) sau t (deschide un fiier text) fie pe ultima poziie fie pe cea
din mijloc.
Operaiile de citire i scriere pot alterna n cazul fluxurilor read /
write n orice ordine. S reinem c standardul ANSI C cere s existe
o funcie de poziionare ntre o operaie de intrare i una de ieire, sau
ntre o operaie de ieire i una de intrare, cu excepia cazului cnd o
operaie de citire detecteaz sfritul de fiier. Aceast operaie poate
__________________________________________________________________________
139
fi inefectiv - cum ar fi f seek( f l ux, 0L, SEEK_CUR) apelat
cu scop de sincronizare.
Valori returnate
n caz de succes se returneaz un pointer de tip FI LE. n caz de
eroare se returneaz NULL i variabila global er r no indic codul
erorii.
Nume
fclose - nchide un flux
Declaraie
i nt f cl ose( FI LE *f l ux) ;
Descriere
Funcia f cl ose nchide fiierul asociat fluxului f l ux. Dac
f l ux a fost deschis pentru ieire, orice date aflate n zone tampon
snt scrise n fiier n prealabil cu un apel f f l ush.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global er r no indic codul erorii.
Nume
tmpfile - creeaz un fiier temporar
Declaraie
FI LE *t mpf i l e( ) ;
Descriere
Funcia t mpf i l e genereaz un nume unic de fiier temporar.
Acesta este deschis n mod binar pentru scriere / citire ("wb+" ).
Fiierul va fi ters automat la nchidere sau la terminarea
programului.
Valoare returnat
Funcia returneaz un descriptor de flux n caz de succes, sau
NULL dac nu poate fi generat un nume unic de fiier sau dac
__________________________________________________________________________
140
fiierul nu poate fi deschis. n caz de eroare variabila global er r no
indic codul erorii.
Nume
fflush - foreaz scrierea n flux
Declaraie
i nt f f l ush( FI LE *f l ux) ;
Descriere
Funcia f f l ush foreaz o scriere a tuturor datelor aflate n zone
tampon ale fluxului f l ux. Fluxul rmne deschis.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global er r no indic codul erorii.
Nume
fseek, ftell, rewind - repoziioneaz un flux
Declaraie
i nt f seek( FI LE *f l ux, l ong of f set ,
i nt r eper ) ;
l ong f t el l ( FI LE *f l ux) ;
voi d r ewi nd( FI LE *f l ux) ;
Descriere
Funcia f seek seteaz indicatorul de poziie pentru fiierul
asociat fluxului f l ux. Noua poziie, dat n octei, se obine adunnd
of f set octei la poziia specificat de r eper . Dac r eper este
SEEK_SET, SEEK_CUR, sau SEEK_END, of f set este relativ la
nceputul fiierului, poziia curent a indicatorului, respectiv sfritul
fiierului. Funcia f seek terge indicatorul de sfrit de fiier.
Funcia f t el l obine valoarea curent a indicatorului de poziie
pentru fiierul asociat fluxului f l ux.
__________________________________________________________________________
141
Funcia r ewi nd poziioneaz indicatorul de poziie pentru
fiierul asociat fluxului f l ux la nceputul fiierului. Este echivalent
cu:
( voi d) f seek( f l ux, 0L, SEEK_SET)
cu completarea c funcia r ewi nd terge i indicatorul de eroare al
fluxului.
Valori returnate
Funcia r ewi nd nu returneaz nici o valoare. n caz de succes,
f seek returneaz 0, i f t el l returneaz offset-ul curent. n caz de
eroare se returneaz EOF i variabila global er r no indic codul
erorii.
11.3. Citire i scriere fr format
Nume
fgets - citete un ir de caractere dintr-un flux text
Declaraie
char *f get s( char *s, i nt si ze, FI LE *f l ux) ;
Descriere
Funcia f get s cel mult si ze- 1 caractere din f l ux i le
memoreaz n zona indicat de s. Citirea se oprete la detectarea
sfritului de fiier sau new-line. Dac se citete caracterul new-line
acesta este memorat n s. Dup ultimul caracter se memoreaz null.
Apeluri ale acestei funcii pot fi combinate cu orice apeluri ale
altor funcii de intrare din bibliotec (f scanf , de exemplu) pentru
un acelai flux de intrare.
Valori returnate
Funcia returneaz adresa s n caz de succes, sau NULL n caz de
eroare sau la ntlnirea sfritului de fiier dac nu s-a citit nici un
caracter.
__________________________________________________________________________
142
Nume
fputs - scrie un ir de caractere ntr-un flux text
Declaraie
i nt f put s( const char *s, FI LE *f l ux) ;
Descriere
Funcia f put s scrie irul s n flux fr caracterul terminator
null.
Apeluri ale acestei funcii pot fi combinate cu orice apeluri ale
altor funcii de ieire din bibliotec (f pr i nt f , de exemplu) pentru
un acelai flux de ieire.
Valori returnate
Funcia returneaz o valoare non-negativ n caz de succes, sau
EOF n caz de eroare.
Nume
fread, fwrite - intrri / ieiri pentru fluxuri binare
Declaraie
unsi gned f r ead( voi d *pt r , unsi gned si ze,
unsi gned nel , FI LE *f l ux) ;
unsi gned f wr i t e( const voi d *pt r , unsi gned
si ze, unsi gned nel , FI LE *f l ux) ;
Descriere
Funcia f r ead citete nel elemente, fiecare avnd mrimea
si ze octei, din fluxul indicat de f l ux, i le memoreaz n zona
indicat de pt r .
Funcia f wr i t e scrie nel elemente, fiecare avnd mrimea
si ze octei, din fluxul indicat de f l ux, pe care le ia din zona
indicat de pt r .
Valori returnate
Funciile returneaz numrul de elemente citite sau scrise cu
succes (i nu numrul de caractere). Dac apare o eroare sau se
__________________________________________________________________________
143
ntlnete sfritul de fiier, valoarea returnat este mai mic dect
nel (posibil zero).
11.4. Citire cu format
Nume
scanf, fscanf, sscanf - citire cu format
Declaraie
i nt scanf ( const char *f or mat , . . . ) ;
i nt f scanf ( FI LE *f l ux, const char *f or mat ,
. . . ) ;
i nt sscanf ( const char *st r , const char
*f or mat , . . . ) ;
Descriere
Familia de funcii scanf scaneaz intrarea n concordan cu irul
de caractere f or mat dup cum se descrie mai jos. Acest format
poate conine specificatori de conversie; rezultatele unor astfel de
conversii (dac se efectueaz) se memoreaz prin intermediul
argumentelor pointer. Funcia scanf citete irul de intrare din
fluxul standard st di n, f scanf din f l ux, i sscanf din irul
indicat de st r .
Fiecare argument pointer trebuie s corespund n ordine ca tip
cu fiecare specificator de conversie (dar a se vedea suprimarea mai
jos). Dac argumentele nu snt suficiente comportamentul
programului este imprevizibil. Toate conversiile snt introduse de
caracterul %. irul format poate conine i alte caractere. Spaii albe
(blanc, tab, sau new-line) din irul format se potrivesc cu orice spaiu
alb n orice numr (inclusiv nici unul) din irul de intrare. Orice alte
caractere trebuie s se potriveasc exact. Scanarea se oprete atunci
cnd un caracter din irul de intrare nu se potrivete cu cel din format.
Scanarea se oprete de asemenea atunci cnd o conversie nu se mai
poate efectua (a se vedea mai jos).
__________________________________________________________________________
144
Conversii
Dup caracterul % care introduce o conversie poate urma un
numr de caractere indicatori, dup cum urmeaz:
* Suprim atribuirea. Conversia care urmeaz se face n mod
obinuit, dar nu se folosete nici un argument pointer; rezultatul
conversiei este pur i simplu abandonat.
h Conversia este de tip dioux sau n i argumentul asociat este un
pointer la shor t (n loc de i nt ).
l Conversia este de tip dioux sau n i argumentul asociat este un
pointer la l ong (n loc de i nt ), sau conversia este de tip efg i
argumentul asociat este un pointer la doubl e (n loc de
f l oat ).
L Conversia este de tip efg i argumentul asociat este un pointer la
l ong doubl e.
n completare la aceti indicatori poate exista o mrime w
maxim opional pentru cmp, exprimat ca un ntreg zecimal, ntre
caracterul % i cel de conversie, i naintea indicatorului. Dac nu este
dat o mrime maxim se folosete mrimea implicit infinit (cu o
excepie la conversia de tip c); n caz contrar se scaneaz cel mult un
numr de w caractere n timpul conversiei. nainte de a ncepe o
conversie, majoritatea conversiilor ignor spaiile albe; acestea nu
snt contorizate n mrimea cmpului.
Snt disponibile urmtoarele conversii:
% Potrivire cu un caracter %. Cu alte cuvinte, %% n irul format
trebuie s se potriveasc cu un caracter %. Nu se efectueaz nici o
conversie i nici o atribuire.
d Potrivire cu un ntreg zecimal (eventual cu semn); argumentul
asociat trebuie s fie un pointer la i nt .
i Potrivire cu un ntreg (eventual cu semn); argumentul asociat
trebuie s fie un pointer la i nt . Valoarea ntreag este citit n
__________________________________________________________________________
145
baza 16 dac ncepe cu 0x sau 0X, n baza 8 dac ncepe cu 0, i
n baza 10 n caz contrar. Snt folosite numai caracterele care
corespund bazei respective.
o Potrivire cu un ntreg octal fr semn; argumentul asociat trebuie
s fie un pointer la unsi gned.
u Potrivire cu un ntreg zecimal fr semn; argumentul asociat
trebuie s fie un pointer la unsi gned.
x Potrivire cu un ntreg hexazecimal fr semn; argumentul asociat
trebuie s fie un pointer la unsi gned.
f Potrivire cu un numr n virgul mobil (eventual cu semn);
argumentul asociat trebuie s fie un pointer la f l oat .
e,g Echivalent cu f .
s Potrivire cu o secven de caractere diferite de spaiu alb;
argumentul asociat trebuie s fie un pointer la char , i zona
trebuie s fie suficient de mare pentru a putea primi toat
secvena i caracterul terminator null. irul de intrare se termin
la un spaiu alb sau la atingerea mrimii maxime a cmpului
(prima condiie ntlnit).
c Potrivire cu o secven de caractere de mrime w (dac aceasta
este specificat; prin lips se ia w=1); argumentul asociat trebuie
s fie un pointer la char , i zona trebuie s fie suficient de mare
pentru a putea primi toat secvena (nu se adaug terminator
null). Nu se ignor ca de obicei spaiile albe din fa. Pentru a
ignora mai nti spaiile albe se indic un spaiu explicit n format.
[ Potrivire cu o secven nevid de caractere din setul specificat de
caractere acceptate; argumentul asociat trebuie s fie un pointer
la char , i zona trebuie s fie suficient de mare pentru a putea
primi toat secvena i caracterul terminator null. Nu se ignor ca
de obicei spaiile albe din fa. irul de intrare va fi format din
caractere aflate n (sau care nu se afl n) setul specificat n
format; setul este definit de caracterele aflate ntre [ i ]. Setul
__________________________________________________________________________
146
exclude acele caractere dac primul caracter dup [ este ^.
Pentru a include caracterul ] n set, acesta trebuie s fie primul
caracter dup [ sau ^; caracterul ] aflat n orice alt poziie
nchide setul. Caracterul - are i el un rol special: plasat ntre
dou alte caractere adaug toate celelalte caractere aflate n
intervalul respectiv la set. Pentru a include caracterul - acesta
trebuie s fie ultimul caracter nainte de ]. De exemplu,
" %[ ^] 0- 9- ] " semnific setul orice caracter cu excepia ], 0
pn la 9, i -. irul se termin la apariia unui caracter care nu
se afl (sau, dac se precizeaz ^, care se afl) n set sau dac se
atinge mrimea maxim specificat.
p Potrivire cu o valoare pointer (aa cum se afieaz cu %p n
pr i nt f ); argumentul asociat trebuie s fie un pointer la pointer.
n Nu se prelucreaz nimic din irul de intrare; n schimb, numrul
de caractere consumate pn la acest punct din irul de intrare
este memorat la argumentul asociat, care trebuie s fie un pointer
la i nt .
Valori returnate
Funciile returneaz numrul de valori atribuite, care poate fi mai
mic dect numrul de argumente pointer, sau chiar zero, n cazul n
care apar nepotriviri ntre format i irul de intrare. Zero indic faptul
c, chiar dac avem un ir de intrare disponibil, nu s-a efectuat nici o
conversie (i atribuire); aceast situaie apare atunci cnd un caracter
din irul de intrare este invalid, cum ar fi un caracter alfabetic pentru
o conversie %d. Valoarea EOF este returnat dac apare un eroare
nainte de prima conversie, cum ar fi detectarea sfritului de fiier.
Dac o eroare sau un sfrit de fiier apare dup ce o conversie a
nceput, se returneaz numrul de conversii efectuate cu succes.
__________________________________________________________________________
147
11.5. Scriere cu format
Nume
printf, fprintf, sprintf - scriere cu format
Declaraie
i nt pr i nt f ( const char *f or mat , . . . ) ;
i nt f pr i nt f ( FI LE *f l ux, const char
*f or mat , . . . ) ;
i nt spr i nt f ( char *st r , const char *f or mat ,
. . . ) ;
Descriere
Funciile din familia printf genereaz o ieire n concordan cu
format dup cum se descrie mai jos. Funcia pr i nt f afieaz ieirea
la fluxul standard st dout ; f pr i nt f scrie ieirea la f l ux;
spr i nt f scrie ieirea n irul de caractere st r .
Aceste funcii genereaz ieirea sub controlul irului format care
specific cum se convertesc argumentele pentru ieire.
irul de formatare
irul format este un ir de caractere, printre care se pot afla zero
sau mai multe directive: caractere obinuite (diferite de %) care snt
copiate aa cum snt n fluxul de ieire, i specificaii de conversie,
fiecare dintre ele rezultnd din ncrcarea a zero sau mai multe
argumente. Fiecare specificaie de conversie este introdus de
caracterul % i se termin cu un specificator de conversie. ntre
acestea pot fi (n aceast ordine) zero sau mai muli indicatori, o
mrime minim a cmpului opional, o precizie opional i un
modificator opional de lungime.
Argumentele trebuie s corespund n ordine cu specificatorii de
conversie. Acestea snt folosite n ordinea dat, unde fiecare caracter
* i fiecare specificator de conversie solicit urmtorul argument.
Dac argumentele nu snt suficiente comportamentul programului
este imprevizibil.
__________________________________________________________________________
148
Caractere indicatori
Caracterul % este urmat de zero, unul sau mai muli indicatori:
# Valoarea numeric se convertete n format alternativ. Pentru
conversii de tip o, primul caracter al irului de ieire este zero
(prin prefixare cu 0 dac valoarea nu este zero). Pentru conversii
de tip x i X, o valoare nenul este prefixat cu 0x (sau 0X
pentru conversii de tip X). Pentru conversii de tip e, E, f, F, g i
G, rezultatul va conine ntotdeauna punctul zecimal, chiar dac
nu apare partea fracionar (n mod normal punctul zecimal apare
n aceste conversii numai dac exist i partea fracionar).
Pentru conversii de tip g i G zerourile finale nu snt eliminate
aa cum se procedeaz n mod normal. Pentru alte conversii
rezultatul este nedefinit.
0 Valoarea numeric este convertit cu zerouri la stnga. Pentru
conversii de tip d, i, o, u, x, X, e, E, f, F, g i G, valoarea
convertit este completat cu zerouri la stnga n loc de blanc.
Dac apar indicatorii 0 i - mpreun, indicatorul 0 este ignorat.
Dac pentru o conversie numeric (d, i, o, u, x, X) este dat o
precizie, indicatorul 0 este ignorat. Pentru alte conversii
rezultatul este nedefinit.
- Valoarea convertit este aliniat la stnga (implicit alinierea se
face la dreapta). Cu excepia conversiilor de tip n, valoarea
convertit este completat la dreapta cu blanc, n loc s fie
completat la stnga cu blanc sau zero. Dac apar indicatorii 0 i
- mpreun, indicatorul 0 este ignorat.
Sp (spaiu) n cazul unui rezultat al unei conversii cu semn, naintea
unui numr pozitiv sau ir vid se pune un blanc.
+ Semnul (+sau - ) este plasat naintea numrului generat de o
conversie cu semn. Implicit semnul este folosit numai pentru
numere negative. Dac apar indicatorii + i Sp mpreun,
indicatorul Sp este ignorat.
__________________________________________________________________________
149
Limea cmpului
Un ir de cifre zecimale (cu prima cifr nenul) specific o lime
minim pentru cmp. Dac valoarea convertit are mai puine
caractere dect limea specificat, va fi completat cu spaii la stnga
(sau dreapta, dac s-a specificat aliniere la stnga). n locul unui
numr zecimal se poate folosi * pentru a specifica faptul c limea
cmpului este dat de argumentul urmtor, care trebuie s fie de tip
i nt . O valoare negativ pentru lime este considerat un indicator -
urmat de o valoare pozitiv pentru lime. n nici un caz nu se va
trunchia cmpul; dac rezultatul conversiei este mai mare dect
limea cmpului, cmpul este expandat pentru a conine rezultatul
conversiei.
Precizia
Precizia (opional) este dat de caracterul . urmat de un ir de
cifre zecimale. n locul irului de cifre zecimale se poate scrie *
pentru a specifica faptul c precizia este dat de argumentul urmtor,
care trebuie s fie de tip i nt . Dac precizia este dat doar de ., sau
dac precizia este negativ, atunci aceasta se consider zero. Precizia
d numrul minim de cifre care apar pentru conversii de tip d, i, o,
u, x, X, numrul de cifre care apar dup punctul zecimal pentru
conversii de tip e, E, f, F, numrul maxim de cifre semnificative
pentru conversii de tip g i G, sau numrul maxim de caractere
generate pentru conversii de tip s.
Modificator de lungime
n acest caz prin conversie ntreag nelegem conversie de tip d,
i, o, u, x, X.
h Conversia ntreag care urmeaz corespunde unui argument
shor t sau unsi gned shor t , sau urmtoarea conversie de tip
n corespunde unui argument de tip pointer la shor t .
l Conversia ntreag care urmeaz corespunde unui argument
l ong sau unsi gned l ong, sau urmtoarea conversie de tip n
corespunde unui argument de tip pointer la l ong.
__________________________________________________________________________
150
L Urmtoarea conversie de tip e, E, f, g sau G corespunde unui
argument l ong doubl e.
Specificator de conversie
Un caracter care specific tipul conversiei care se va face.
Specificatorii de conversie i semnificaia lor snt:
d,i
Argumentul de tip i nt este convertit la notaia zecimal cu
semn. Precizia, dac este dat, d numrul minim de cifre care
trebuie s apar; dac valoarea convertit necesit mai puine
cifre, aceasta este completat la stnga cu zerouri. Precizia
implicit este 1. Dac valoarea 0 este afiat cu precizie explicit
0, ieirea este vid.
o,u,x,X
Argumentul de tip unsi gned este convertit la notaie octal
fr semn (o), zecimal fr semn (u), sau hexazecimal fr
semn (x i X). Literele abcdef se folosesc pentru conversii de
tip x; literele ABCDEF pentru conversii de tip X. Precizia, dac
este dat, d numrul minim de cifre care trebuie s apar; dac
valoarea convertit necesit mai puine cifre, aceasta este
completat la stnga cu zerouri. Precizia implicit este 1. Dac
valoarea 0 este afiat cu precizie explicit 0, ieirea este vid.
e,E
Argumentul de tip flotant este rotunjit i convertit n stil
[- ]d.dddedd unde avem o cifr nainte de punctul zecimal i
numrul de cifre dup acesta este egal cu precizia; dac aceasta
lipsete se consider 6; dac precizia este zero, punctul zecimal
nu apare. O conversie de tip E folosete litera E (n loc de e)
pentru a introduce exponentul. Exponentul are ntotdeauna cel
puin dou cifre; dac valoarea este zero, exponentul este 00.
f,F
Argumentul de tip flotant este rotunjit i convertit n notaie
zecimal n stil [- ]ddd.ddd, unde numrul de cifre dup punctul
zecimal este egal cu precizia specificat. Dac precizia lipsete se
__________________________________________________________________________
151
consider 6; dac precizia este explicit zero, punctul zecimal nu
apare. Dac punctul zecimal apare, cel puin o cifr apare
naintea acestuia.
g,G
Argumentul de tip flotant este convertit n stil f sau e (sau E
pentru conversii de tip G). Precizia specific numrul de cifre
semnificative. Dac precizia lipsete se consider 6; dac precizia
este zero se consider 1. Stilul e este folosit dac exponentul
rezultat n urma conversiei este mai mic dect 4 ori mai mare
sau egal cu precizia. Zerourile finale snt eliminate din partea
fracionar a rezultatului; punctul zecimal apare numai dac este
urmat de cel puin o cifr.
c Argumentul de tip i nt este convertit la unsi gned char i se
scrie caracterul rezultat.
s Argumentul de tip const char * este un pointer la un ir de
caractere. Caracterele din ir snt scrise pn la (fr a include)
caracterul terminator null; dac precizia este specificat, nu se
scrie un numr mai mare dect cel specificat. Dac precizia este
dat, nu e nevoie de caracterul null; dac precizia nu este
specificat, sau dac este mai mare dect mrimea irului, irul
trebuie s conin un caracter terminator null.
p Argumentul de tip pointer este scris n hexazecimal; formatul
este specific sistemului de calcul.
n Numrul de caractere scrise pn n acest moment este memorat
la argumentul de tip i nt *. Nu se face nici o conversie.
% Se scrie un caracter %. Nu se face nici o conversie. Specificaia
complet este %%.
Valoare returnat
Funciile returneaz numrul de caractere generate (nu se include
caracterul terminator null pentru spr i nt f ).
__________________________________________________________________________
152
11.6. Tratarea erorilor
Nume
perror - afieaz un mesaj de eroare sistem
Declaraie
voi d per r or ( const char *s) ;
#i ncl ude <er r no. h>
const char *sys_er r l i st [ ] ;
i nt sys_ner r ;
Descriere
Rutina per r or afieaz un mesaj la ieirea standard de eroare,
care descrie ultima eroare ntlnit la ultimul apel sistem sau funcie
de bibliotec. Mai nti se afieaz argumentul s, apoi virgula i
blanc, i n final mesajul de eroare i new-line. Se recomand (mai
ales pentru depanare) ca argumentul s s includ numele funciei n
care a aprut eroarea. Codul erorii se ia din variabila extern er r no.
Lista global de erori sys_er r l i st [ ] indexat cu er r no
poate fi folosit pentru a obine mesajul de eroare fr new-line.
Ultimul indice de mesaj din list este sys_ner r - 1. Se recomand
o atenie deosebit n cazul accesului direct la list deoarece unele
coduri noi de eroare pot lipsi din sys_er r l i st [ ] .
Dac un apel sistem eueaz variabila er r no indic codul erorii.
Aceste valori pot fi gsite n <er r no. h>. Funcia per r or servete
la afiarea acestui cod de eroare ntr-o form lizibil. Dac un apel
terminat cu eroare nu este imediat urmat de un apel per r or ,
valoarea variabilei er r no se poate pierde dac nu e salvat.
Nume
clearerr, feof, ferror - verific i reseteaz starea
fluxului
Declaraie
voi d cl ear er r ( FI LE *f l ux) ;
__________________________________________________________________________
153
i nt f eof ( FI LE *f l ux) ;
i nt f er r or ( FI LE *f l ux) ;
i nt f i l eno( FI LE *f l ux) ;
Descriere
Funcia cl ear er r terge indicatorii de sfrit de fiier i eroare
ai fluxului.
Funcia f eof testeaz indicatorul de sfrit de fiier al fluxului,
i returneaz non-zero dac este setat. Acesta este setat dac o
operaie de citire a detectat sfritul de fiier.
Funcia f er r or testeaz indicatorul de eroare al fluxului, i
returneaz non-zero dac este setat. Acesta este setat dac o operaie
de citire sau scriere a detectat o eroare (datorat de exemplu
hardware-ului).
Funciile de citire (cu sau fr format) nu fac distincie ntre
sfrit de fiier i eroare, astfel c trebuie apelate funciile f eof i
f er r or pentru a determina cauza.
Funcia f i l eno examineaz argumentul f l ux i returneaz
descriptorul asociat de sistemul de operare acestui flux.
Atenie! Este foarte frecvent folosirea incorect a funciei f eof
pentru a testa dac s-a ajuns la sfritul fiierului. Nu se recomand n
nici un caz acest stil de programare:
#def i ne LSI R 80
char l i n[ LSI R] ;
FI LE *f i , *f o;
f i =f open( nume-fiier-intrare, " r t " ) ;
f o=f open( nume-fiier-ieire, " wt " ) ;
whi l e ( ! f eof ( f i ) ) { /* greit! */
f get s( l i n, LSI R, f i ) ;
f put s( l i n, f o) ;
}
f cl ose( f i ) ; f cl ose( f o) ;
n aceast secven, dac i ultima linie a fiierului text de intrare
este terminat cu new-line, aceasta va fi scris de dou ori n fiierul
de ieire. De ce? Dup ce se citete ultima linie nc nu este
__________________________________________________________________________
154
poziionat indicatorul de sfrit de fiier, deci funcia f get s
returneaz succes. La reluarea ciclului se ncearc un nou f get s i
abia acum se depisteaz sfritul de fiier, fapt marcat n zona
rezervat fluxului f i . Astfel coninutul tabloului l i n rmne
nemodificat i este scris a doua oar n fiierul de ieire. Abia la o
nou reluare a ciclului funcia f eof ne spune c s-a depistat sfritul
de fiier.
n acest manual snt prezentate mai multe programe care
efectueaz diferite prelucrri asupra unor fiiere text. Pentru
simplitate toate programele presupun c nu apar erori la citire sau la
scriere.
11.7. Operaii cu directoare
Funciile de parcurgere a cataloagelor de fiiere descrise n
aceast seciune (opendi r , r eaddi r , cl osedi r ) snt definite de
mai multe medii de programare C (Borland, Watcom, Visual C, GNU
Linux), precum i de standardul POSIX. Aceste funcii snt descrise
n <di r ent . h>.
Funciile de redenumire i tergere a unor fiiere snt descrise n
<st di o. h>.
Nume
opendir - deschide un director
Declaraie
DI R *opendi r ( const char *nume) ;
Descriere
Funcia opendi r deschide un flux pentru directorul cu numele
nume, i returneaz un pointer la fluxul deschis. Fluxul este
poziionat pe prima intrare din director.
Valoare returnat
Funcia returneaz un pointer la flux n caz de succes, sau NULL
n caz de eroare i variabila global er r no indic codul erorii.
__________________________________________________________________________
155
Cteva erori posibile
EACCES Acces interzis
ENOTDI R nume nu este un director
Nume
readdir - citete un director
Declaraie
st r uct di r ent *r eaddi r ( DI R *di r ) ;
Descriere
Funcia r eaddi r returneaz un pointer la o structur de tip
di r ent care reprezint urmtoarea intrare din directorul indicat de
fluxul di r . Returneaz NULL dac s-a depistat sfritul de director
sau dac a aprut o eroare.
Structura de tip di r ent conine un cmp char d_name[ ] .
Utilizarea altor cmpuri din structur reduce portabilitatea
programelor.
Valoare returnat
Funcia returneaz un pointer la o structur de tip dirent, sau
NULL dac s-a depistat sfritul de director sau dac a aprut o
eroare.
Nume
closedir - nchide un director
Declaraie
i nt cl osedi r ( DI R *di r ) ;
Descriere
Funcia cl osedi r nchide fluxul di r .
Valoare returnat
Funcia returneaz 0 n caz de succes sau EOF n caz de eroare.
__________________________________________________________________________
156
Nume
rename - redenumete un fiier
remove - terge un fiier
Declaraie
i nt r ename( const char *ol d, const char
*new) ;
i nt r emove( const char *name) ;
Descriere
Funcia r ename schimb numele unui fiier din ol d n new.
Dac a fost precizat un periferic n new, acesta trebuie s coincid cu
cel din ol d. Directoarele din ol d i newpot s fie diferite, astfel c
r ename poate fi folosit pentru a muta un fiier dintr-un director n
altul. Nu se permit specificatori generici (wildcards).
Funcia r emove terge fiierul specificat prin name.
Valoare returnat
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global er r no indic codul erorii.
11.8. Programe demonstrative
Primele trei programe primesc ca parametri n linia de comand
numele fiierelor pe care le vor prelucra. Ultimul program primete
ca parametru n linia de comand numele directorului al crui
coninut va fi afiat.
1) Determinarea mrimii unui fiier
#i ncl ude <st di o. h>
FI LE *f ;
i nt mai n( i nt ac, char **av) {
i f ( ac! =2) {
f put s( " Un ar gument ! \ n" , st der r ) ;
r et ur n 1;
}
__________________________________________________________________________
157
f = f open( av[ 1] , " r b" ) ;
i f ( ! f ) {
per r or ( " Er oar e l a deschi der e" ) ;
r et ur n 1;
}
f seek( f , 0, SEEK_END) ;
f pr i nt f ( st der r , " Fi l e %s, si ze %l d\ n" ,
f t el l ( f ) ) ;
f cl ose( f ) ;
r et ur n 0;
}
2) Copierea unui fiier
Funciile f get s i f put s se folosesc pentru fluxuri deschise n
mod text. Cum se utilizeaz pentru copierea unui fiier text?
#i ncl ude <st di o. h>
#def i ne LSI R 80
char l i n[ LSI R] ;
FI LE *f i , *f o;
i nt mai n( i nt ac, char **av) {
i f ( ac! =3) {
f put s( " Doua ar gument e! \ n", st der r ) ;
}
f i =f open( av[ 1] , " r t " ) ; f o=f open( av[ 2] , "wt " ) ;
i f ( ! f i | | ! f o) {
per r or ( " Er oar e l a deschi der e" ) ;
r et ur n 1;
}
whi l e ( f get s( l i n, LSI R, f i ) )
f put s( l i n, f o) ;
f cl ose( f i ) ; f cl ose( f o) ;
r et ur n 0;
}
Funciile f r ead i f wr i t e se folosesc pentru fluxuri deschise
n mod binar. Cum se utilizeaz pentru copierea unui fiier binar?
__________________________________________________________________________
158
#i ncl ude <st di o. h>
#def i ne LZON 80
char zon[ LZON] ;
FI LE *f i , *f o;
i nt k;
i nt mai n( i nt ac, char **av) {
i f ( ac! =3) {
f put s( " Doua ar gument e! \ n" , st der r ) ;
r et ur n 1;
}
f i =f open( av[ 1] , " r b" ) ;
f o=f open( av[ 2] , " wb" ) ;
i f ( ! f i | | ! f o) {
per r or ( " Er oar e l a deschi der e" ) ;
r et ur n 1;
}
whi l e ( k=f r ead( zon, 1, LZON, f i ) )
f wr i t e( zon, 1, k, f o) ;
f cl ose( f i ) ; f cl ose( f o) ;
r et ur n 0;
}
3) Prelucrarea unui fiier text
Programul prezentat n continuare citete un fiier text care
conine pe fiecare linie un ir de caractere (fr spaii) i trei valori
ntregi, i afieaz pe terminal numele pe 12 poziii aliniat la stnga i
media aritmetic a celor trei valori ntregi.
#i ncl ude <st di o. h>
FI LE *f i ;
char num[ 10] ;
i nt a, b, c;
doubl e m;
i nt mai n( i nt ac, char **av) {
i f ( ac! =2) {
f put s( " Un ar gument ! \ n" , st der r ) ;
__________________________________________________________________________
159
r et ur n 1;
}
f i =f open( av[ 1] , " r t " ) ;
i f ( ! f i ) {
per r or ( " Er oar e l a deschi der e" ) ;
r et ur n 1;
}
whi l e ( f scanf ( f i , "%s %d %d %d" ,
num, &a, &b, &c) ! =EOF) {
m=( a+b+c) / 3. 0;
pr i nt f ( " %- 12s%6. 2l f \ n" , num, m) ;
}
f cl ose( f i ) ;
r et ur n 0;
}
4) Afiarea coninutului unui director
#i ncl ude <di r ent . h>
#i ncl ude <st di o. h>
DI R *di r ;
st r uct di r ent *ent ;
i nt mai n( i nt ac, char **av) {
i f ( ac! =2) {
pr i nt f ( " Un par amet r u\ n" ) ;
r et ur n 1;
}
di r = opendi r ( av[ 1] ) ;
i f ( ! di r ) {
per r or ( " Er oar e open di r " ) ;
r et ur n 1;
}
whi l e ( ent =r eaddi r ( di r ) )
pr i nt f ( " %s\ n" , ent - >d_name) ;
r et ur n 0;
}
__________________________________________________________________________
160
12. Alte rutine din biblioteca standard
n acest capitol snt descrise funcii care rezolv probleme legate
de alocarea dinamic a memoriei, sortare i cutare, clasificare,
operaii cu blocuri de memorie i iruri de caractere, funcii
matematice.
12.1. Alocarea dinamic a memoriei
Nume
calloc, malloc, realloc - aloc memoria n mod
dinamic
free - elibereaz memoria alocat n mod dinamic
Declaraie
#i ncl ude <st dl i b. h>
voi d *cal l oc( unsi gned nel , unsi gned si ze) ;
voi d *mal l oc( unsi gned si ze) ;
voi d *r eal l oc( voi d *pt r , unsi gned si ze) ;
voi d f r ee( voi d *pt r ) ;
Descriere
Funcia cal l oc aloc memorie pentru un tablou de nel
elemente, fiecare de mrime si ze octei i returneaz un pointer la
memoria alocat. Coninutul memoriei este pus la zero.
Funcia mal l oc aloc size octei i returneaz un pointer la
memoria alocat. Coninutul memoriei nu este ters.
Funcia f r ee elibereaz spaiul de memorie indicat de pt r , care
trebuie s fi fost returnat de un apel anterior mal l oc, cal l oc sau
r eal l oc. n caz contrar, sau dac a existat deja un apel anterior
f r ee( pt r ) , comportamentul programului este imprevizibil.
Funcia r eal l oc schimb mrimea blocului de memorie indicat
de pt r la si ze octei. Coninutul rmne neschimbat la mrimea
minim dintre mrimea veche i cea nou; noul spaiu de memorie
__________________________________________________________________________
161
care este eventual alocat este neiniializat. Dac pt r este NULL
apelul este echivalent cu mal l oc( si ze) ; dac si ze este egal cu
zero apelul este echivalent cu f r ee( pt r ) . Cu excepia cazului cnd
pt r este NULL, acesta trebuie s fi fost returnat de un apel precedent
mal l oc, cal l oc sau r eal l oc.
Valori returnate
Pentru cal l oc i mal l oc valoarea returnat este un pointer la
memoria alocat, care este aliniat n mod corespunztor pentru orice
tip de variabile, sau NULL dac nu exist suficient memorie
continu.
Funcia f r ee nu returneaz nimic.
Funcia r eal l oc returneaz un pointer la noua zon de
memorie alocat, care este aliniat n mod corespunztor pentru orice
tip de variabile, i poate fi diferit de pt r , sau poate fi NULL dac nu
exist suficient memorie continu sau dac valoarea si ze este
egal cu 0. Dac r eal l oc eueaz, blocul original rmne neatins -
nu este nici eliberat nici mutat.
12.2. Sortare i cutare
Nume
qsort - sorteaz un tablou
bsearch - cutare binar ntr-un tablou sortat
Declaraie
#i ncl ude <st dl i b. h>
voi d qsor t ( voi d *base, unsi gned nel ,
unsi gned si ze, i nt ( *comp)
( const voi d *, const voi d *) ) ;
voi d *bsear ch( const voi d *key, const voi d
*base, unsi gned nel , unsi gned si ze, i nt
( *comp) ( const voi d *, const voi d *) ) ;
Descriere
__________________________________________________________________________
162
Funcia qsor t sorteaz un tablou de nel elemente, fiecare de
mrime si ze. Argumentul base indic spre nceputul tabloului.
Elementele tabloului snt sortate n ordine cresctoare n
concordan cu funcia de comparare referit de comp, apelat cu
dou argumente care indic spre obiectele ce se compar. Funcia de
comparare trebuie s returneze un ntreg mai mic dect, egal cu, sau
mai mare dect zero dac primul argument este considerat a fi mai
mic dect, egal cu, respectiv mai mare dect al doilea. Dac cele dou
elemente comparate snt egale, ordinea n tabloul sortat este
nedefinit.
Funcia bsear ch caut ntr-un tablou de nel elemente, fiecare
de mrime si ze, un membru care coincide cu obiectul indicat de
key. Argumentul base indic spre nceputul tabloului.
Coninutul tabloului trebuie s fie sortat cresctor n concordan
cu funcia de comparare referit de comp, apelat cu dou argumente
care indic spre obiectele ce se compar. Funcia de comparare
trebuie s returneze un ntreg mai mic dect, egal cu, sau mai mare
dect zero dac primul argument este considerat a fi mai mic dect,
egal cu, respectiv mai mare dect al doilea.
Valoare returnat
Funcia bsear ch returneaz un pointer la un membru al
tabloului care coincide cu obiectul indicat de key, sau NULL dac nu
se gsete nici un membru. Dac exist mai multe elemente care
coincid cu key, poate fi returnat oricare element cu aceast
proprietate.
12.3. Rutine de clasificare
Nume
isalnum, isalpha, isascii, iscntrl, isdigit,
isgraph, islower, isprint, ispunct, isspace,
isupper, isxdigit - rutine de clasificare
tolower - conversie n liter mic
toupper - conversie n liter mare
__________________________________________________________________________
163
Declaraie
#i ncl ude <ct ype. h>
i nt i sal num( i nt c) ; i nt i sl ower ( i nt c) ;
i nt i sal pha( i nt c) ; i nt i spr i nt ( i nt c) ;
i nt i sasci i ( i nt c) ; i nt i spunct ( i nt c) ;
i nt i scnt r l ( i nt c) ; i nt i sspace( i nt c) ;
i nt i sdi gi t ( i nt c) ; i nt i supper ( i nt c) ;
i nt i sgr aph( i nt c) ; i nt i sxdi gi t ( i nt c) ;
i nt t ol ower ( i nt c) ; i nt t oupper ( i nt c) ;
Descriere
Primele 12 funcii verific dac c, care trebuie s fie o valoare de
tip unsi gned char sau EOF, se afl n una din clasele de caractere
enumerate mai sus.
i sal num
Verific dac c este alfanumeric; este echivalent cu
( i sal pha( c) | | i sdi gi t ( c) ) .
i sal pha
Verific dac c este alfabetic; este echivalent cu ( i supper ( c)
| | i sl ower ( c) ) .
i sasci i
Verific dac c este o valoare pe 7 bii din setul de caractere
ASCII.
i scnt r l
Verific dac c este un caracter de control.
i sdi gi t
Verific dac c este o cifr (ntre 0 i 9).
i sgr aph
Verific dac c este un caracter afiabil cu excepia spaiului.
i sl ower
Verific dac c este o liter mic.
__________________________________________________________________________
164
i spr i nt
Verific dac c este un caracter afiabil inclusiv spaiu.
i spunct
Verific dac c este un caracter diferit de spaiu i non-
alfanumeric.
i sspace
Verific dac c este un spaiu alb.
i supper
Verific dac c este o liter mare.
i sxdi gi t
Verific dac c este o cifr hexazecimal din setul 0 1 2 3 4 5 6 7
8 9 a b c d e f A B C D E F.
t ol ower
Convertete caracterul c, dac este o liter, la litera mic
corespunztoare.
t oupper
Convertete caracterul c, dac este o liter, la litera mare
corespunztoare.
Valoare returnat
Valoarea returnat de funciile i s... este nenul dac caracterul c
se afl n clasa testat, i zero n caz contrar.
Valoarea returnat de funciile t o... este litera convertit dac
caracterul c este o liter, i nedefinit n caz contrar.
12.4. Operaii cu blocuri de memorie
Pentru majoritatea funciilor din aceast categorie compilatorul
expandeaz codul acestora folosind instruciuni pe iruri de caractere.
Declaraiile acestor funcii se obin cu
#i ncl ude <st r i ng. h>
__________________________________________________________________________
165
Nume
memcpy - copiaz o zon de memorie
Declaraie
voi d *memcpy( voi d *dest , const voi d *sr c,
unsi gned n) ;
voi d *memmove( voi d *dest , const voi d *sr c,
unsi gned n) ;
Descriere
Funcia memcpy copiaz n octei din zona de memorie sr c n
zona de memorie dest . Zonele de memorie nu trebuie s se
suprapun. Dac exist acest risc se utilizeaz memmove.
Valoare returnat
Funciile returneaz un pointer la dest .
Nume
memcmp - compar dou zone de memorie
Declaraie
i nt memcmp( const voi d *s1, const voi d *s2,
unsi gned n) ;
Descriere
Funcia memcmp compar primii n octei ai zonelor de memorie
s1 i s2.
Valoare returnat
Returneaz un ntreg mai mic dect, egal cu, sau mai mare dect
zero dac s1 este mai mic dect, coincide, respectiv este mai mare
dect s2.
Nume
memset - umple o zon de memorie cu o constant pe un
octet
Declaraie
__________________________________________________________________________
166
voi d *memset ( voi d *s, i nt c, unsi gned n) ;
Descriere
Funcia memset umple primii n octei ai zonei de memorie
indicat de s cu constanta c pe un octet.
Valoare returnat
Funcia returneaz un pointer la zona de memorie s.
Nume
memchr - caut n memorie un caracter
Declaraie
voi d *memchr ( const voi d *s, i nt c,
unsi gned n) ;
Descriere
Funcia memchr caut caracterul c n primii n octei de memorie
indicai de s. Cutarea se oprete la primul octet care are valoarea c
(interpretat ca unsi gned char ).
Valoare returnat
Funcia returneaz un pointer la octetul gsit sau NULL dac
valoarea nu exist n zona de memorie.
12.5. Operaii cu iruri de caractere
Pentru majoritatea funciilor din aceast categorie compilatorul
expandeaz codul acestora folosind instruciuni pe iruri de caractere.
Declaraiile acestor funcii se obin cu
#i ncl ude <st r i ng. h>
Nume
strlen - calculeaz lungimea unui ir
Declaraie
unsi gned st r l en( const char *s) ;
Descriere
__________________________________________________________________________
167
Funcia st r l en calculeaz lungimea irului s, fr a include
caracterul terminator null.
Valoare returnat
Funcia returneaz numrul de caractere din s.
Nume
strcpy, strncpy - copiaz un ir de caractere
Declaraie
char *st r cpy( char *dest , const char *sr c) ;
char *st r ncpy( char *dest , const char *sr c,
unsi gned n) ;
Descriere
Funcia st r cpy copiaz irul indicat de sr c (inclusiv caracterul
terminator null) n zona indicat de dest . irurile nu trebuie s se
suprapun, i n plus zona dest trebuie s fie suficient de mare
pentru a primi copia.
Funcia st r ncpy este similar, cu excepia faptului c nu se
copiaz mai mult de n octei din sr c. Astfel, dac caracterul
terminator null nu se afl n primii n octei din sr c, rezultatul nu va
fi terminat cu null. n cazul n care lungimea lui sr c este mai mic
dect n, restul octeilor din dest primesc valoarea null.
Valoare returnat
Funciile returneaz un pointer la irul dest .
Nume
strdup - duplic un ir
Declaraie
char *st r dup( const char *s) ;
Descriere
Funcia st r dup returneaz un pointer la un nou ir care este un
duplicat al irului s. Memoria pentru noul ir se obine cu mal l oc,
i poate fi eliberat cu f r ee.
__________________________________________________________________________
168
Valoare returnat
Funcia returneaz un pointer la irul duplicat, sau NULL dac nu
exist memorie suficient disponibil.
Nume
strcat, strncat - concateneaz dou iruri
Declaraie
char *st r cat ( char *dest , const char *sr c) ;
char *st r ncat ( char *dest , const char *sr c,
unsi gned n) ;
Descriere
Funcia st r cat adaug irul sr c la irul dest suprascriind
caracterul null de la sfritul lui dest , i la sfrit adaug un caracter
terminator null. irurile nu trebuie s se suprapun, i n plus irul
dest trebuie s aib suficient spaiu pentru a pstra rezultatul.
Funcia st r ncat este similar, cu excepia faptului c numai
primele n caractere din sr c se adaug la dest .
Valoare returnat
Funciile returneaz un pointer la irul rezultat dest .
Nume
strcmp - compar dou iruri de caractere
Declaraie
i nt st r cmp( const char *s1, const char
*s2) ;
Descriere
Funcia st r cmp compar cele dou iruri s1 i s2.
Valoare returnat
Funcia returneaz un ntreg mai mic dect, egal cu, sau mai mare
dect zero dac s1 este mai mic dect, coincide, respectiv este mai
mare dect s2.
__________________________________________________________________________
169
Nume
strchr, strrchr - localizeaz un caracter
Declaraie
char *st r chr ( const char *s, i nt c) ;
char *st r r chr ( const char *s, i nt c) ;
Descriere
Funcia st r chr returneaz un pointer la prima apariie a
caracterului c n irul s.
Funcia st r r chr returneaz un pointer la ultima apariie a
caracterului c n irul s.
Valoare returnat
Funciile returneaz un pointer la caracterul gsit sau NULL dac
valoarea nu a fost gsit.
Nume
strstr - localizeaz un subir
Declaraie
char *st r st r ( const char *si r , const char
*subs) ;
Descriere
Funcia st r st r gsete prima apariie a subirului subs n
irul si r . Caracterul terminator null nu este luat n considerare.
Valoare returnat
Funcia returneaz un pointer la nceputul subirului, sau NULL
dac subirul nu este gsit.
Nume
strspn, strcspn - caut un set de caractere ntr-un ir
Declaraie
unsi gned st r spn( const char *s, const char
__________________________________________________________________________
170
*acc) ;
unsi gned st r cspn( const char *s, const char
*r ej ) ;
Descriere
Funcia st r spn calculeaz lungimea segmentului iniial din s
format n ntregime numai cu caractere din acc.
Funcia st r cspn calculeaz lungimea segmentului iniial din s
format n ntregime numai cu caractere care nu se gsesc n r ej .
Valori returnate
Funcia st r spn returneaz poziia primului caracter din s care
nu se afl n acc.
Funcia st r cspn returneaz poziia primului caracter din s care
se afl n r ej .
12.6. Biblioteca matematic
1) Funciile din prima categorie snt descrise n <st dl i b. h>.
Nume
rand, srand - generarea numerelor pseudo-aleatoare
Declaraie
i nt r and( voi d) ;
voi d sr and( unsi gned i nt seed) ;
Descriere
Funcia r and returneaz un ntreg pseudo-aleator ntre 0 i
RAND_MAX (pentru majoritatea mediilor de programare C aceast
constant este egal cu valoarea maxim cu semn reprezentabil pe
un cuvnt al sistemului de calcul).
Funcia sr and iniializeaz generatorul cu valoarea seed
pentru o nou secven de valori ntregi pseudo-aleatoare care vor fi
returnate de rand. Aceste secvene se repet dac sr and se apeleaz
cu aceeai valoare seed.
__________________________________________________________________________
171
Se obinuiete ca generatorul s fie iniializat cu o valoare dat
de ceasul sistemului de calcul, ca n exemplul de mai jos:
#i ncl ude <t i me. h>
sr and( t i me( NULL) ) ;
Valoare returnat
Funcia r and returneaz o valoare ntre 0 i RAND_MAX.
Observaie
n lucrarea Numerical Recipes in C: The Art of Scientific
Computing - William H Press, Brian P Flannery, Saul A Teukolsky,
William T Vetterling / New York: Cambridge University Press, 1990
(1st ed, p 207), se face urmtorul comentariu:
"Dac dorii s generai o valoare aleatoare ntreag ntre 1 i 10,
se recomand s folosii secvena
j =1+( i nt ) ( 10. 0*r and( ) / ( RAND_MAX+1. 0) ) ;
i nu o secven de tipul
j =1+( i nt ) ( 1000000. 0*r and( ) ) %10;
care folosete biii de rang inferior."
Tot n fiierul <st dl i b. h>snt descrise i urmtoarele funcii:
i nt abs( i nt i ) ; valoare absolut
l ong l abs( l ong i ) ; valoare absolut
i nt at oi ( char *s) ; conversie din ASCII n ntreg
l ong at ol ( char *s) ; conversie din ASCII n ntreg lung
doubl e at of ( char *s) ; conversie din ASCII n dubl
precizie
2) Funciile din a doua categorie snt descrise n fiierul
<mat h. h>.
doubl e f abs( doubl e x) ; valoare absolut
doubl e f l oor ( doubl e x) ; parte ntreag inferioar
doubl e cei l ( doubl e x) ; parte ntreag superioar
doubl e sqr t ( doubl e x) ; x
doubl e si n( doubl e x) ; sin(x)
doubl e cos( doubl e x) ; cos(x)
__________________________________________________________________________
172
doubl e t an( doubl e x) ; tg(x)
doubl e asi n( doubl e x) ; arcsin(x)
doubl e acos( doubl e x) ; arccos(x)
doubl e at an( doubl e x) ; arctg(x) n [-t/ 2,t/ 2]
doubl e at an2( doubl e y, doubl e x) ;
arctg(y/x) n [t,t]
doubl e exp( doubl e x) ; e
x
doubl e l og( doubl e x) ; ln(x)
doubl e pow( doubl e x, doubl e y) ; x
y
doubl e si nh( doubl e x) ; sinh(x)
doubl e cosh( doubl e x) ; cosh(x)
doubl e t anh( doubl e x) ; tgh(x)
doubl e l dexp( doubl e x, i nt e) ; x 2
e
doubl e f mod( doubl e x, doubl e y) ; x modulo y
Funcia f mod returneaz o valoare f definit astfel: x = a y + f
a este o valoare ntreag (dat de x/ y) i 0 s |f | < y; f are semnul lui
x.
Urmtoarele dou funcii returneaz dou valori: una este
valoarea returnat de funcie (de tip doubl e), i cealalt returnat
prin intermediul unui argument de tip pointer la i nt respectiv
doubl e.
doubl e f r exp( doubl e x, i nt *e) ;
Funcia f r exp desparte valoarea x n dou pri: o parte fracionar
normalizat (f e [0.5,1)) i un exponent e. Dac x este 0 atunci f=0 i
e=0. Valoarea returnat este f.
doubl e modf ( doubl e x, doubl e *n) ;
Funcia modf desparte valoarea x n dou pri: o parte fracionar
subunitar f i o parte ntreag n. Valorile f i n au acelai semn ca i
x. Valoarea returnat este f.
__________________________________________________________________________
173
12.7. Programe demonstrative
1) Programul prezentat n continuare genereaz un ir de n valori
ntregi aleatoare n intervalul [0,M1] pe care le depune n tabloul X
(alocat dinamic), i apoi le sorteaz cresctor. n continuare se
genereaz k valori ntregi aleatoare pe care le caut n tabloul X.
Pentru fiecare cutare cu succes se afieaz pe terminal valoarea
cutat i poziia n tablou.
Valorile n, k i Mse iau n aceast ordine din linia de comand.
#i ncl ude <st dl i b. h>
#i ncl ude <st di o. h>
#i ncl ude <t i me. h>
i nt cmp( const voi d *A, const voi d *B) {
r et ur n *( i nt *) A- *( i nt *) B;
}
i nt mai n( i nt ac, i nt **av) {
i nt *X, *p, M, n, k, i , v;
i f ( ac! =4) {
f put s( " Tr ei ar gument e! \ n", st der r ) ;
r et ur n 1;
}
n=at oi ( av[ 1] ) ; k=at oi ( av[ 2] ) ;
M=at oi ( av[ 3] ) ;
X=( i nt *) mal l oc( n*si zeof ( i nt ) ) ;
i f ( ! X) r et ur n 1;
sr and( t i me( NULL) ) ;
f or ( i =0; i <n; i ++)
X[ i ] =r and( ) %M;
qsor t ( X, n, si zeof ( i nt ) , cmp) ;
f or ( i =0; i <k; i ++) {
v=r and( ) %M;
p=( i nt *) bsear ch( &v, X, n, si zeof ( i nt ) ,
cmp) ;
i f ( p)
pr i nt f ( " Val : %d Pos: %d\ n" , v, p- X) ;
}
__________________________________________________________________________
174
f r ee( X) ;
r et ur n 0;
}
2) S relum al treilea exemplu din capitolul precedent. Se citete
un fiier text care conine pe fiecare linie un nume (ir de caractere
fr spaiu) i trei valori reale (note). Pentru fiecare linie se
calculeaz media aritmetic a celor trei valori i se determin dac
elementul este admis (fiecare not este minimum 5) sau respins (cel
puin o not este sub 5). n final se afieaz liniile n ordinea
urmtoare: mai nti elementele admise n ordinea descresctoare a
mediei, i apoi elementele respinse n ordine alfabetic dup nume.
Se afieaz doar numele, situaia (A/ R) i media.
n acest exemplu punem n eviden o modalitate comod de
selectare a membrilor unei structuri cu ajutorul macrourilor. Macroul
Fl d selecteaz din zona referit de pointerul P membrul f . Deoarece
pointerul P (care poate fi argumentul Asau Bal funciei comp) refer
o zon de tip voi d, este necesar mai nti un cast pentru a preciza
tipul concret al acesteia. Membrul f poate fi: nm, ar , md, definii n
cadrul structurii de tip St El .
Deoarece nu tim de la nceput cte linii are fiierul de intrare,
sntem nevoii s folosim urmtoarea strategie. La nceput alocm
pentru tabloul El o zon care s memoreze NA elemente. Pe msur
ce aceast zon se completeaz, la un moment dat numrul de linii
citite coincide cu NA. n acest moment se aloc o zon nou care s
poat memora un numr mai mare de elemente. Desigur, de fiecare
dat se va actualiza mrimea spaiului alocat.
#i ncl ude <st dl i b. h>
#i ncl ude <st r i ng. h>
#i ncl ude <st di o. h>
#def i ne NA 32
t ypedef st r uct {
char nm[ 10] , ar ;
f l oat na, nb, nc, md;
} St El ;
__________________________________________________________________________
175
#def i ne Fl d( P, f ) ( ( St El *) P) - >f
i nt comp( const voi d *A, const voi d *B) {
f l oat w;
i nt d;
i f ( d=Fl d( A, ar ) - Fl d( B, ar ) )
r et ur n d;
i f ( Fl d( A, ar ) ==' A' ) {
w=Fl d( B, md) - Fl d( A, md) ;
i f ( w>0) r et ur n 1;
i f ( w<0) r et ur n - 1;
}
r et ur n st r cmp( Fl d( A, nm) , Fl d( B, nm) ) ;
}
i nt mai n( i nt ac, char **av) {
i nt na, ne, i ;
St El *El ;
FI LE *f i ;
i f ( ac! =2) {
f put s( " Un ar gument ! \ n" , st der r ) ;
r et ur n 1;
}
f i =f open( av[ 1] , " r t " ) ;
i f ( ! f i ) {
per r or ( " Er oar e l a deschi der e" ) ;
r et ur n 1;
}
na=NA; ne=0;
El =( St El *) mal l oc( na*si zeof ( St El ) ) ;
whi l e ( f scanf ( f i , "%s %d %d %d" ,
El [ ne] . nm, &El [ ne] . na, &El [ ne] . nb,
&El [ ne] . nc) ! =EOF) {
i f ( ( El [ ne] . na>=5) && ( El [ ne] . nb>=5) &&
( El [ ne] . nc>=5) )
El [ ne] . ar =' A' ;
el se El [ ne] . ar =' R' ;
El [ ne] . md=( El [ ne] . na+El [ ne] . nb+
El [ ne] . nc) / 3. 0;
__________________________________________________________________________
176
ne++;
i f ( ne==na) {
na+=NA;
El =( St El *) r eal l oc( El , na*
si zeof ( St El ) ) ;
}
}
f cl ose( f i ) ;
qsor t ( El , ne, si zeof ( St El ) , comp) ;
f or ( i =0; i <ne; i ++)
pr i nt f ( " %- 12s %c%6. 2l f \ n",
El [ i ] . nm, El [ i ] . ar , El [ i ] . md) ;
f r ee( El ) ;
r et ur n 0;
}
3) Se citete dintr-un fiier text o valoare natural n. Urmtoarele
linii conin n cuvinte, fiecare cuvnt avnd acelai numr de litere (cel
mult 10). S se afieze cuvintele din fiier ordonate alfabetic.
Pentru a memora lista de cuvinte folosim urmtoarea strategie. n
loc s alocm pentru fiecare cuvnt (ir de caractere) citit o zon nou
de memorie, alocm de la nceput o zon n care s putem memora
toate cuvintele din list. Aceast zon va avea mrimea de ( l +1) *n
octei, unde l este lungimea fiecrui cuvnt (numrul de litere). De ce
( l +1) ? pentru c trebuie s memorm i caracterul terminator null.
Avantaje: memoria este utilizat mai eficient dac se aloc de la
nceput o zon contigu de dimensiune mai mare, i se reduce foarte
mult fragmentarea memoriei.
#i ncl ude <st dl i b. h>
#i ncl ude <st r i ng. h>
#i ncl ude <st di o. h>
i nt comp( const voi d *A, const voi d *B) {
r et ur n st r cmp( ( char *) A, ( char *) B) ;
}
i nt mai n( i nt ac, char **av) {
__________________________________________________________________________
177
char *C, s[ 11] ;
i nt n, l , i ;
FI LE *f i ;
i f ( ac! =2) {
f put s( " Un ar gument ! \ n" , st der r ) ;
r et ur n 1;
}
f i =f open( av[ 1] , " r t " ) ;
i f ( ! f i ) {
per r or ( " Er oar e l a deschi der e" ) ;
r et ur n 1;
}
f scanf ( f i , " %d %s" , n, s) ;
l =st r l en( s) ;
C=( char *) mal l oc( ( l +1) *n) ;
St r cpy( C, s) ;
f or ( i =1; i <n; i ++)
f scanf ( f i , " %s" , C+( l +1) *i ) ;
f cl ose( f i ) ;
qsor t ( C, n, l +1, comp) ;
f or ( i =0; i <n; i ++)
pr i nt f ( " %s\ n" , C+( l +1) *i ) ;
f r ee( C) ;
r et ur n 0;
}
__________________________________________________________________________
178
Bibliografie
Brian W Kernigham, Dennis M Ritchie - The C Programming Language
Prentice-Hall Software Series, 1978
- - - - Limbajul C; manual de programare
Institutul de tehnic de calcul, Cluj-Napoca 1984
Herbert Schildt - Manual C complet
Editura Teora, 1998
Manuale electronice
http://www.programmingtutorials.com/c.html
Marshall Brain - Introduction to C Programming
http://devcentral.iftech.com/learning/tutorials/c-cpp/c/
Steve Summit - Introductory C Programming Class Notes
http://www.eskimo.com/~scs/cclass/cclass.html
Steve Summit - Intermediate C Programming Class Notes
http://www.eskimo.com/~scs/cclass/cclass.html
Brian Brown - C Programming
http://www.cit.ac.nz/smac/cprogram/onlinet.htm
Brian Brown - An Introduction to C Programming
http://www.cit.ac.nz/smac/cprogram/default.htm
__________________________________________________________________________
179
Cuprins
1. Generaliti asupra limbajului C . . . . . . . . . . . . . . . . . . . 4
1.1. Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Primele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3. Meta-limbajul i setul de caractere . . . . . . . . . . . . . . . . . . . . 9
2. Unitile lexicale ale limbajului C . . . . . . . . . . . . . . . . 10
2.1. Identificatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.2. Cuvinte cheie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3. Constante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.4. iruri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5. Operatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.6. Separatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3. Variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1. Clase de memorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2. Tipuri de variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3. Obiecte i valori-stnga . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4. Conversii de tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4. Operatori i expresii . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1. Expresii primare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.2. Operatori unari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.3. Operatori multiplicativi . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4. Operatori aditivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.5. Operatori de deplasare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.6. Operatori relaionali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.7. Operatori de egalitate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.8. Operatorul I pe bii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.9. Operatorul SAU-exclusiv pe bii . . . . . . . . . . . . . . . . . . . . 35
4.10. Operatorul SAU-inclusiv pe bii . . . . . . . . . . . . . . . . . . . . 36
4.11. Operatorul I-logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.12. Operatorul SAU-logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.13. Operatorul condiional . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.14. Operatori de atribuire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
__________________________________________________________________________
180
4.15. Operatorul virgul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
4.16. Precedena i ordinea de evaluare . . . . . . . . . . . . . . . . . . . 40
5. Declaraii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.1. Specificatori de clas de memorie . . . . . . . . . . . . . . . . . . . 43
5.2. Specificatori de tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.3. Declaratori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.4. Modificatorul const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.5. Iniializare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.6. Nume-tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6. Instruciuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.1. Instruciunea expresie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2. Instruciunea compus sau blocul . . . . . . . . . . . . . . . . . . . . 51
6.3. Instruciunea condiional . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.4. Instruciunea whi l e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.5. Instruciunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.6. Instruciunea f or . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6.7. Instruciunea swi t ch . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.8. Instruciunea br eak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.9. Instruciunea cont i nue . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.10. Instruciunea r et ur n . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.11. Instruciunea vid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
7. Funciile i structura unui program . . . . . . . . . . . . . . . . 60
7.1. Definiia funciilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
7.2. Apelul funciilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
7.3. Revenirea din funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .63
7.4. Argumentele funciei i transmiterea parametrilor . . . . . . . 64
7.5. Funcii cu numr variabil de parametri . . . . . . . . . . . . . . . . 65
7.6. Exemple de funcii i programe . . . . . . . . . . . . . . . . . . . . . 66
8. Liniile de control ale compilatorului . . . . . . . . . . . . . . .70
8.l. nlocuirea simbolurilor, substituii macro . . . . . . . . . . . . . . 70
8.2. Includerea fiierelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
8.3. Compilarea condiionat . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
8.4. Utilizarea directivelor de compilare . . . . . . . . . . . . . . . . . . 73
__________________________________________________________________________
181
9. Pointeri i masive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
9.1. Pointeri i adrese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
9.2 Pointeri i argumente de funcii . . . . . . . . . . . . . . . . . . . . . . 77
9.3. Pointeri i masive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
9.4. Aritmetica de adrese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9.5. Pointeri la caracter i funcii . . . . . . . . . . . . . . . . . . . . . . . . 83
9.6. Masive multidimensionale . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.7. Masive de pointeri i pointeri la pointeri . . . . . . . . . . . . . . 88
9.8. Iniializarea masivelor i masivelor de pointeri . . . . . . . . . 93
9.9. Masive de pointeri i masive multidimensionale . . . . . . . . 96
9.10. Argumentele unei linii de comand . . . . . . . . . . . . . . . . . 97
9.11. Pointeri la funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10. Structuri i reuniuni . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.1. Elemente de baz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.2. Structuri i funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
10.3. Masive de structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
10.4. Pointeri la structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
10.5. Structuri auto-referite . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
10.6. Cutare n tabele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
10.7. Cmpuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
10.8. Reuniuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
10.9. Declaraii de structuri, reuniuni i cmpuri . . . . . . . . . . 131
10.10. Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
11. Intrri / ieiri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
11.1. Intrri i ieiri standard; fiiere . . . . . . . . . . . . . . . . . . . . 136
11.2. Accesul la fiiere; deschidere i nchidere . . . . . . . . . . . 138
11.3. Citire i scriere fr format . . . . . . . . . . . . . . . . . . . . . . . 141
11.4. Citire cu format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
11.5. Scriere cu format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
11.6. Tratarea erorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
11.7. Operaii cu directoare . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
11.8. Programe demonstrative . . . . . . . . . . . . . . . . . . . . . . . . 156
12. Alte rutine din biblioteca standard . . . . . . . . . . . . . . 160
12.1. Alocarea dinamic a memoriei . . . . . . . . . . . . . . . . . . . . 160
__________________________________________________________________________
182
12.2. Sortare i cutare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
12.3. Rutine de clasificare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
12.4. Operaii cu blocuri de memorie . . . . . . . . . . . . . . . . . . . 164
12.5. Operaii cu iruri de caractere . . . . . . . . . . . . . . . . . . . . 166
12.6. Biblioteca matematic . . . . . . . . . . . . . . . . . . . . . . . . . . 170
12.7. Programe demonstrative . . . . . . . . . . . . . . . . . . . . . . . . . 173
Bibliografie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

You might also like