Sunteți pe pagina 1din 179

Theory without practice is useless; practice without theory is blind

Roger Bacon
Acest manual este structurat astfel nct elementele limbajului C s fie
prezentate ntr-o manier unitar. Primul capitol face o scurt introducere i
prezint ase programe C.
Urmtoarele cinci capitole descriu elementele primare ale limbajului C:
tipurile fundamentale de date, definiii de constante i variabile, instruciuni,
funcii. Capitolul apte descrie strategii de organizare a activitii de
programare pentru elaborarea programelor mari, compuse din mai multe
module. Tot n acest capitol snt prezentate i directivele de compilare cele
mai folosite. Capitolele opt i nou descriu elemente avansate ale limbajului
C: pointeri i structuri.
Capitolele zece i unsprezece trec n revist funciile cele mai des
utilizate definite n biblioteca standard, mpreun cu cteva programe
demonstrative. Am selectat 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 prezentare exhaustiv a limbajului C; am
selectat strictul necesar care s permit unui programator s elaboreze
programe eficiente i uor de ntreinut.
Deoarece avem convingerea c cea mai bun explicaie este un program
funcional, exemplele din acest manual 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.
Dac constructorii ar fi construit cldiri aa cum scriu programatorii
programe, atunci prima ciocnitoare care ar fi venit ar fi distrus civilizaia
(din Legile lui Murphy)

___________________________________________________________________________________________________________________________

1. Generaliti asupra limbajului C


1.1. Introducere
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.
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 (if), cicluri cu testul de terminare
naintea ciclului (while, for) sau dup ciclu (do) i selecia unui
caz dintr-o mulime de cazuri (switch).
Limbajul C permite lucrul cu pointeri i are o aritmetic de
adrese puternic. Permite de asemenea definirea de ctre programator
a unor tipuri structurate de date, care pot fi orict de complexe.
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 crete experiena n programare.

1.2. Reprezentarea valorilor numerice


O valoare ntreag se reprezint n baza doi:
105(10) 01101001(2)
32750(10) 0111111111101110(2)
O valoare ntreag negativ se reprezint n complement fa de
doi astfel: dac pentru reprezentare se folosesc w bii, a (0a2w1)
se reprezint ca i valoarea 2wa, din care se pstreaz cele mai puin
semnificative w poziii. Astfel operaiile de adunare (cele mai
frecvente operaii elementare) se efectueaz ntr-un timp foarte scurt,
fr a fi nevoie de alte operaii auxiliare.
105(10) 10010111(2)
32750(10) 1000000000010010(2)
Codificarea ASCII a caracterelor folosete 8 bii (un octet):
codul caracterului spaiu este 32;
codurile cifrelor snt valori ntre 48 i 57;
codurile literelor mari snt valori ntre 65 i 90;
codurile literelor mici snt valori ntre 97 i 122.
Exist dou convenii de memorare a valorilor ntregi care
necesit mai muli octei:
Big Endian, octeii apar n ordinea cifrelor semnificative:
valoarea 127AC450(16) se memoreaz astfel: 12, 7A, C4, 50;
Little Endian, octeii apar n ordine invers: valoarea
127AC450(16) se memoreaz astfel: 50, C4, 7A, 12.
Calculatoarele bazate pe procesoare Intel folosesc a doua
convenie de reprezentare. Detalii despre acest subiect se afl la:
___________________________________________________________________________________________________________________________

http://www.cs.umass.edu/~verts/cs32/endian.html
O valoare real nenul se reprezint normalizat n virgul
mobil, descompus n dou elemente: o parte subunitar n
intervalul [0.5,1) i o putere a bazei doi. Pentru semn se folosete o
poziie distinct (0 pozitiv, 1 negativ).
Exemplu: 4.625 100.101(2) 0.100101(2) 23
Numrul de poziii rezervate pentru partea fracionar, respectiv
pentru exponent depind de precizia aleas, care poate fi simpl, dubl
sau extins.

1.3. Primele programe


n aceast seciune snt prezentate i explicate ase 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.
#include <stdio.h>
int main() {
printf("Hello, world\n");
return 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
stdio.h .
A doua linie definete funcia main care va conine instruciunile
programului. n acest caz singura instruciune este un apel al funciei
printf care afieaz un mesaj la terminal. Mesajul este dat ntre
ghilimele i se termin cu un caracter special new-line (\n).
Instruciunea return 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 main apare ntre acolade. Orice program C
trebuie s aib o funcie main.
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.
#include <stdio.h>
int main() {
int s,n;
s = 0;
do {
scanf("%d",&n);
s += n;
} while (n!=0);
printf("%d\n",s);
return 0;
}

n cadrul funciei main se definesc 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 (do) 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 (while) valoarea
introdus (n) este nenul. Operatorul != are semnificaia diferit de.
n final funcia printf 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): a0, a1, ..., an1. n continuare se afieaz media
aritmetic a acestor valori, calculat n cadrul unei funcii care se
definete n acest scop.
#include <stdio.h>
double media(double v[], int n) {
double s;
int i;
for (s=i=0; i<n; i++)
s += v[i];
return s/n;
}
int main() {
int n,i;
double a[100];
scanf("%d",&n);
for (i=0; i<n; i++)
scanf("%lf",&a[i]);
printf("%.3lf\n",produs(a,n));
return 0;
}

n cadrul funciei main se definesc dou variabile n i i care


vor memora valori ntregi. Variabila n pstreaz numrul actual de
valori din masivul a. Se definesc de asemenea un masiv
unidimensional a care va putea memora 100 de valori de tip real
(dubl precizie).
Se citete de la terminal o valoare n. n continuare se introduc
valorile reale ai (i0,1,...,n1); se observ modul de referire la
fiecare element din masiv: a[i]. Formatul de introducere "%lf"
indic faptul c se ateapt introducerea unei valori n dubl precizie
de la terminal, care va fi depus la locaia de memorie asociat
variabilei ai. n locul construciei &a[i] se poate folosi forma
echivalent a+i.
___________________________________________________________________________________________________________________________

Pentru a introduce toate valorile ai se efectueaz un ciclu for,


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 ++.
Media aritmetic a acestor valori, calculat cu ajutorul funciei
media, este afiat cu o precizie de trei cifre dup punctul zecimal,
conform formatului de afiare ".3lf".
S analizm acum definiia funciei media. Aceasta calculeaz o
valoare de tip double, opernd asupra unui masiv v care are
elemente de tip double. Cte elemente are masivul, aflm din al
doilea parametru al funciei: n.
Declaraia double v[]
nu definete un masiv: n momentul apelului din main, argumentul v
va indica spre masivul a cu care este pus n coresponden.
Declaraia int n
definete o variabil local n, care n momentul apelului din main va
primi valoarea argumentului n.
Trebuie s subliniem faptul c cele dou variabile, dei au acelai
nume, snt distincte: fiecare este local funciei n care este definit.
Aceeai observaie este valabil i pentru variabilele i.
Ciclul for iniializeaz variabilele s i i cu zero. Variabila s (de
asemenea local) va memora suma valorilor din masivul v. Cu
ajutorul variabilei i se parcurge masivul v, i valoarea fiecrui
element este cumulat n s. n final instruciunea return comunic
funciei principale valoarea s/n.
Acelai program poate fi scris i altfel, definind o parte din
variabile n afara celor dou funcii.
#include <stdio.h>
int n;
double a[100];
double media() {
double s;
int i;
for (s=i=0; i<n; i++)
s += a[i];
___________________________________________________________________________________________________________________________

return s/n;
}
int main() {
int i;
scanf("%d",&n);
for (i=0; i<n; i++)
scanf("%lf",&a[i]);
printf("%.3lf\n",produs());
return 0;
}

n acest caz ambele funcii se refer la acelai masiv a i la


aceeai variabil n; spunem c variabilele a i n snt globale. Funcia
media definete dou variabile locale: s i i, funcia main
definete o variabil local: i.
Exist o diferen important ntre cele dou funcii media din
cele dou programe. Prima definiie este mai general, deoarece
poate opera pe orice masiv de elemente de tip double, avnd oricte
elemente. Dac, de exemplu, n funcia principal am avea nc un
masiv b cu m elemente, apelul ar fi: media(b,m).
A doua definiie nu permite acest lucru. Funcia principal ar
trebui s copieze mai nti toate elementele masivului b n masivul a,
i valoarea m n variabila n. Abia dup aceea ar putea fi apelat
funcia media. Dar astfel am distruge vechile valori din a i n.
Al doilea stil de programare trebuie evitat, deoarece utilizarea
funciilor este foarte greoaie pentru argumente distincte la apeluri
distincte ale acestora. Exist o singur justificare pentru acest stil:
cnd tim cu certitudine c o funcie pe care o definim va opera
ntotdeauna cu argumente memorate tot timpul n aceleai variabile.
Al cincilea program este o ilustrare a unor probleme legate de
capacitatea reprezentrilor valorilor de tip ntreg i virgul mobil.
#include <stdio.h>
int main() {
short k,i;
float a,b,c,u,v,w;
i = 240; k = i * i;
___________________________________________________________________________________________________________________________

10

printf("%hd\n",k);
a = 60.1; k = a * 100;
printf("%hd\n",k);
a = 12345679; b = 12345678;
c = a * a b * b;
u = a * a; v = b * b; w = u - v;
printf("%f %f\n",c,w);
if (c==w) return 0;
else return 1;
}

Variabila k, care ar trebui s memoreze valoarea 57600, are tipul


ntreg scurt (short), 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.
n continuare variabila a primete valoarea 60.1, care nu poate fi
reprezentat exact n virgul mobil. De aceea variabila k primete
valoarea 6009 datorit erorilor de aproximare.
Al treilea 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 24691357
(rezultatul corect), va avea valoarea 24691356, deoarece tipul float
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 patrulea set de
operaii. Aici rezultatele intermediare snt memorate de fiecare dat
cu trunchiere n variabile de tip float. 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 w snt 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.
___________________________________________________________________________________________________________________________

11

Ultimul program citete cte o linie de la intrarea standard i o


afieaz la ieirea standard.
#include <stdio.h>
char lin[80];
int main() {
while (gets(lin)) puts(lin);
return 0;
}

Linia citit cu ajutorul funciei de bibliotec gets este memorat


n masivul lin, ultimul caracter valid al liniei fiind urmat de o
valoare zero, care marcheaz astfel sfritul irului. Citirea se ncheie
atunci cnd intrarea standard nu mai furnizeaz nimic. Funcia gets
returneaz o valoare care are semnificaia True dac intrarea standard
a furnizat o linie, sau False dac nu s-a mai furnizat nimic. Dac linia
se termin cu new-line, acest caracter este preluat de la intrarea
standard, dar nu este memorat n irul lin.
Am presupus c fiecare linie introdus are mai puin de 80 de
caractere, altfel programul nu va funciona corect. De altfel funcia
gets este folosit n aceast lucrare doar n scop didactic, pentru
simplificarea expunerilor.
Fiecare linie citit de la intrarea standard este afiat cu ajutorul
funciei puts. Aceasta afieaz automat la sfrit caracterul new-line.
Dac programul este rulat n mod obinuit nu se ntmpl nimic
spectaculos: fiecare linie introdus de la terminal va fi reafiat.
Programul poate fi totui folosit pentru a afia coninutul unui fiier
text, pentru a crea un fiier text, sau pentru a copia un fiier text. n
acest scop programul este rulat n mod linie de comand astfel:
Prog Fiier-intrare >Fiier-ieire
Prog este numele programului executabil, obinut n urma
compilrii programului surs. Fiier-intrare este numele fiierului al
crui coninut dorim s-l afim sau s-l copiem. Fiier-ieire este
numele fiierului pe care dorim s-l crem.
Oricare din cele dou argumente poate lipsi. Dac primul
argument este prezent, programul va citi datele din fiierul de intrare
specificat, altfel va atepta introducerea datelor de la terminal. n al
___________________________________________________________________________________________________________________________

12

doilea caz trebuie marcat ntr-un mod special terminarea


introducerii, i acest mod depinde de sistemul de operare sub care se
lucreaz. Dac al doilea argument este prezent, programul va scrie
datele n fiierul de ieire specificat, care va fi creat n acest scop.
Dac ieirea nu este specificat, datele vor fi afiate la terminal.
Observaie general. n cazul fiecrui program, fiecare variabil
sau funcie este definit nainte de utilizare, pentru ca n momentul
utilizrii s existe toate informaiile despre contextul n care poate fi
utilizat identificatorul respectiv.

___________________________________________________________________________________________________________________________

13

2. Unitile lexicale ale limbajului C


Setul de caractere al limbajului C este un subset al setului de
caractere ASCII, format din:
26 litere mici
abcdefghijklmnopqrstuvwxyz
26 litere mari
ABCDEFGHIJKLMNOPQRSTUVWXYZ
10 cifre
0123456789
30 simboluri speciale
Blanc ! " # % & ' ( ) * , . / : ; ? [ \ ] ^ _ ~ { | }
6 simboluri negrafice
\n, \t, \b, \r, \f, \a
n limbajul C exist ase tipuri de uniti lexicale: identificatori,
cuvinte-cheie, constante, iruri, operatori i separatori.

2.1. Identificatori; cuvinte cheie


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.
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.
Cuvintele cheie definite de standardul ANSI snt:
auto
break
case
char
const
continue
default

do
double
else
enum
extern
float
for

goto
if
int
long
register
return
short

signed
sizeof
static
struct
switch
typedef
union

unsigned
void
volatile
while

___________________________________________________________________________________________________________________________

14

2.2. 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 A la 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 long 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 long.
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

___________________________________________________________________________________________________________________________

15

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
urmat de litera l sau L.
Exemplu: 123ul

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
unor secvene de evitare. Acestea ofer de altfel i un mecanism
general pentru reprezentarea caracterelor mai dificil de introdus n
calculator i a oricror configuraii de bii. Aceste secvene de evitare
snt:
___________________________________________________________________________________________________________________________

16

\n new-line
\r carriage return
\\ backslash
\t tab orizontal
\f form feed
\' apostrof
\b backspace
\a semnal sonor
\" ghilimele
\ooo configuraie de bii precizat n baza 8
\xhh configuraie de bii precizat n baza 16
Fiecare din aceste secvene, dei e format din mai multe
caractere, reprezint n realitate un singur caracter.
Exemplu: secvena '\040' va genera caracterul spaiu.

Constante simbolice
O constant simbolic este un identificator cu valoare de
constant. Valoarea constantei poate fi orice ir de caractere introdus
prin construcia #define (capitolul apte).
Exemplu: #define MAX 1000
Dup ntlnirea acestei construcii compilatorul va nlocui toate
apariiile constantei simbolice MAX cu valoarea 1000.
Numele constantelor simbolice se scriu de obicei cu litere mari
(fr a fi obligatoriu).

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

17

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, analizndul 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 static (capitolul
trei). Un ir este iniializat cu caracterele date.
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. Secvena urmtoare determin lungimea irului de
caractere s, excluznd caracterul terminal null.
for (n=0; s[i]; n++) ;

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.4. 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. Capitolul
patru este rezervat n exclusivitate descrierii operatorilor definii n
limbajul C.

2.5. Separatori
Un separator este un caracter sau un ir de caractere care separ
unitile lexicale ntr-un program scris n C.
___________________________________________________________________________________________________________________________

18

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
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 acestuia.
Nu se admit comentarii imbricate.

___________________________________________________________________________________________________________________________

19

3. Variabile
Ca i constantele, variabilele snt elemente de baz cu care
opereaz un program scris n C. O variabil este un obiect de
programare cruia i se atribuie un nume i i se asociaz o zon de
memorie.
Variabilele se deosebesc dup nume i pot primi diferite valori.
Numele variabilelor snt identificatori. Numele de variabile se scriu
de obicei cu litere mici (fr a fi obligatoriu).
n limbajul C, variabilele snt caracterizate prin dou atribute:
clas de memorie i tip. Acestea i snt atribuite unei variabile prin
intermediul unei declaraii. Declaraiile enumer variabilele care
urmeaz a fi folosite, stabilesc clasa de memorie, tipul variabilelor i
eventual valorile iniiale. Sintaxa unei declaraii este:
clas tip list-variabile;
Lista de variabile poate avea un element sau mai multe, n al
doilea caz ele fiind separate prin virgul.
Clasa de memorie precizeaz care funcii pot vedea variabilele
declarate n cadrul unui modul:
doar funcia / blocul unde acestea snt definite: variabile automatice
i variabile statice interne;
toate funciile din cadrul modulului care urmeaz declaraiei:
variabile globale statice;
toate funciile din toate modulele care cunosc declaraia: variabile
globale externe.
Clasa de memorie precizeaz n plus i durata de via a
variabilelor declarate. Variabilele automatice exist doar pe durata
execuiei funciei sau blocului unde au fost definite. Variabilele
statice i externe exist pe toat durata execuiei programului.
Tipul unei variabile precizeaz domeniul de valori pe care le
poate lua i operaiile care se pot efectua cu aceste valori.
Declaraiile se folosesc pentru a specifica interpretarea pe care
compilatorul trebuie s o dea fiecrui identificator, i pot aprea n
___________________________________________________________________________________________________________________________

20

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 creeaz variabila i
i se aloc memorie;
declaraia de utilizare a unei variabile, care doar anun
proprietile variabilei care urmeaz a fi folosit.

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 sau
funcii. 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 auto.
O variabil auto 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
reziduale. Nici o funcie nu are acces la variabilele auto din alt
funcie. n funcii diferite pot exista variabile locale cu aceleai nume,
fr ca variabilele s aib vreo legtur ntre ele.
Exemple. n fiecare program din primul capitol, variabilele locale
snt automatice.

Variabile registru
O variabil registru se declar prin specificatorul de clas de
memorie register. Ca i variabilele auto ele snt locale unui
bloc sau funcii i valorile lor se pierd la ieirea din blocul sau funcia
respectiv. Variabilele declarate register indic compilatorului c
___________________________________________________________________________________________________________________________

21

variabilele respective vor fi folosite foarte des. Dac este posibil,


variabilele register vor li plasate de ctre compilator n regitrii
rapizi ai calculatorului, ceea ce conduce la programe mai compacte i
mai rapide.
Variabile register pot fi numai variabilele automatice sau
parametrii formali ai unei funcii. Practic exist cteva restricii
asupra variabilelor register care reflect realitatea hardware-ului
de baz. Astfel:
numai cteva variabile din fiecare funcie pot fi pstrate n regitri
(de obicei dou sau trei); declaraia register este ignorat pentru
celelalte variabile;
numai tipurile de date ntregi i pointer snt admise;
nu este posibil referirea la adresa unei variabile register.

Variabile statice
Variabilele statice se declar prin specificatorul de clas de
memorie static. 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; spre deosebire de variabilele auto, 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 printf 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.

___________________________________________________________________________________________________________________________

22

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 static. Aceasta face
ca numele funciei s nu fie recunoscut n afara fiierului n care a
fost declarat.
n seciunea rezervat problemei iniializrilor de la sfritul
acestui capitol vom da un exemplu care ilustreaz comportamentul
variabilelor automatice i statice.

Variabile externe
Variabilele externe snt variabile cu caracter global. Ele se
definesc n afara oricrei funcii i pot fi apelate prin nume din
oricare modul (fiier surs) 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.
Detalii despre utilizarea variabilelor externe vor fi date n
capitolul apte.

3.2. Tipuri de variabile


Limbajul C admite numai cteva tipuri fundamentale de variabile:
caracter, ntreg, virgul mobil.
Pentru fiecare din tipurile prezentate n continuare se precizeaz
i descriptorii de format cei mai utilizai n cadrul funciilor de intrare
/ ieire. n capitolul zece va fi detaliat tema formatrii datelor de
intrare i de ieire.
___________________________________________________________________________________________________________________________

23

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.
Domeniul valorilor 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 int prin extensia bitului cel mai din stnga din
octet (datorit modului de funcionare a instruciunilor
calculatorului).
Domeniul valorilor variabilelor caracter fr semn (unsigned
char) este ntre 0 i 255.
Descriptori de format:
%c pentru o variabil sau o valoare de tip char;
%s pentru o variabil sau o expresie de tip ir de caractere.

Tipul ntreg
Variabilele ntregi pozitive sau negative pot fi declarate prin
specificatorul de tip int. Zona de memorie alocat unei variabile
ntregi poate fi de cel mult trei dimensiuni.
Relaii despre dimensiune snt furnizate de calificatorii short,
long i unsigned, care pot fi aplicai tipului int.
Calificatorul short se refer totdeauna la numrul minim de
octei pe care se reprezint un ntreg, de obicei 2.
Calificatorul long se refer la numrul maxim de octei pe care
poate fi reprezentat un ntreg, de obicei 4.
___________________________________________________________________________________________________________________________

24

Tipul int are dimensiunea natural sugerat de sistemul de


calcul. Domeniul numerelor ntregi reprezentabile n main depinde
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 unsigned alturi de declaraia de tip int
determin ca valorile variabilelor astfel declarate s fie considerate
ntregi fr semn. Un ntreg fr semn poate lua valori ntre 0 i
65535 (sisteme de calcul pe 16 bii) sau ntre 0 i 4294967295
(sisteme de calcul pe 32 de bii).
Valorile de tip unsigned respect legile aritmeticii modulo 2 w,
unde w este numrul de bii din reprezentarea unei variabile de tip
int. Numerele de tipul unsigned snt totdeauna pozitive.
Declaraiile pentru calificatori snt de forma:
short int x;
long int y;
unsigned int z;
unsigned long int u;
Cuvntul int poate fi omis n aceste situaii.
Descriptori de format:
%d pentru o variabil sau o valoare de tip int;
%u pentru o variabil sau o valoare de tip unsigned;
%ld pentru o variabil sau o valoare de tip long;
%lu pentru o variabil sau o valoare de tip unsigned long;
%hd pentru o variabil sau o valoare de tip short;
%hu pentru o variabil sau o valoare de tip unsigned short.

Tipul flotant (virgul mobil)


Variabilele flotante pot fi n simpl precizie i atunci se declar
prin specificatorul de tip float sau n dubl precizie i atunci se
declar prin specificatorul double. Majoritatea sistemelor de calcul
admit i reprezentarea n precizie extins; o variabil n precizie
extins se declar prin specificatorul long double.
___________________________________________________________________________________________________________________________

25

Descriptori de format:
%f pentru o variabil sau o valoare de tip float;
%lf pentru o variabil sau o valoare de tip double;
%Lf pentru o variabil sau o valoare de tip long double.

Tipuri derivate
n afar de tipurile aritmetice fundamentale, exist, n principiu, o
clas infinit de tipuri derivate, construite din tipurile fundamentale
n urmtoarele moduri:
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. Tipurile derivate vor fi detaliate n capitolele
opt i nou.

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 apare n stnga operatorului de
atribuire. n paragraful de descriere a operatorilor se va indica dac
___________________________________________________________________________________________________________________________

26

operanzii snt valori-stnga sau dac rezultatul operaiei este o


valoare-stnga.
Exemple.
s = a;

n partea dreapt a operatorului de atribuire, a reprezint


valoarea variabilei respective. Operaia s V; semnific: valoarea
V se atribuie variabilei s.
*p = *q;

n partea dreapt a operatorului de atribuire, *q reprezint


valoarea aflat la adresa indicat de q. Operaia *p V; semnific:
valoarea V se memoreaz la adresa indicat de p.

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.
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 int pot aprea
mpreun. Aceasta permite o flexibilitate considerabil n anumite
tipuri de transformri de caractere. Un astfel de exemplu este funcia
atoi descris n capitolul ase care convertete un ir de cifre n
echivalentul lor numeric.
Expresia urmtoare produce valoarea numeric a caracterului
(cifr) memorat n ASCII:

s[i] - '0'
Atragem atenia c atunci cnd o variabil de tip char este
convertit la tipul int, se poate produce un ntreg negativ, dac bitul
___________________________________________________________________________________________________________________________

27

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 int.
Conversia tipului int n char se face cu pierderea biilor de
ordin superior.
ntregii de tip short snt convertii automat la int. Conversia
ntregilor se face cu extensie de semn; ntregii snt totdeauna cantiti
cu semn.
Un ntreg long este convertit la un ntreg short 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 virgul mobil la ntreg se face prin
trunchierea prii fracionare. Conversia de la ntreg la virgul mobil
este acceptat.

ntregi fr semn
ntr-o expresie n care apar doi operanzi, dintre care unul
unsigned 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 int trece n unsigned, valoarea sa este cel mai mic
ntreg fr semn congruent cu ntregul cu semn (modulo 2 16 sau 232).
ntr-o reprezentare la complementul fa de 2, conversia este
conceptual, nu exist nici o schimbare real a configuraiei de bii.
Cnd un ntreg fr semn este convertit la long, 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
___________________________________________________________________________________________________________________________

28

nivel mai nalt nainte de operaie. Rezultatul este de tipul de nivel


mai nalt. Ierarhia tipurilor este urmtoarea:
char short int long;
float double long double;
tip ntreg cu semn tip ntreg fr semn;
tip ntreg tip 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.
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:
sqrt((double)n)
se convertete n la double nainte de a se transmite funciei sqrt.
Variabila n nu-i modific valoarea.
___________________________________________________________________________________________________________________________

29

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:
#define NMAX 1000
char lin[NMAX+1];

3.5. Masive
n limbajul C se pot defini masive unidimensionale,
bidimensionale, tridimensionale etc. Un masiv se compune din mai
multe elemente de acelai tip; un element se identific prin indice
(poziia relativ n masiv), sau prin indici (dac masivul este
multidimensional).
Exemple:
char s[100];
int a[10][15];

Prima linie declar un ir de caractere: s[0], s[1], ..., s[99].


A doua linie declar o matrice de ntregi: a[0][0], ..., a[0][14],
a[1][0], ..., a[1][14], ..., a[9][0], ..., a[9][14].
Acest subiect va fi detaliat n capitolul opt.

3.6. Iniializri
ntr-o declaraie se poate specifica o valoare iniial pentru
identificatorul care se declar. Iniializatorul este precedat de semnul
= i const dintr-o expresie (variabile simple) sau o list de valori
incluse n acolade (masive sau structuri).
Toate expresiile dintr-un iniializator pentru variabile statice sau
externe trebuie s fie expresii constante sau expresii care se reduc la
adresa unei variabile declarate anterior. 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.
___________________________________________________________________________________________________________________________

30

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 auto i register, iniializarea este fcut
la fiecare intrare n funcie sau bloc.
Problema iniializrii masivelor i masivelor de pointeri este
detaliat n capitolul opt. Problema iniializrii structurilor este
detaliat n capitolul nou.
#include <stdio.h>
int e = 1;
int f() {
int a = 2;
a++; e++;
return a + e;
}
int g() {
static int s = 2;
s++; e++;
return s + e;
}
int main() {
int v1,v2,v3,v4;
v1 = f(); v2 = g();
v3 = f(); v4 = g();
printf("%d %d %d %d %d\n",v1,v2,v3,v4,e);
return 0;
}

S executm pas cu pas acest program.


v1 = f(): a = 3; e = 2; v1 = 5;
v2 = g(): s = 3; e = 3; v2 = 6;
v3 = f(): a = 3; e = 4; v3 = 7;
variabila a este reiniializat la intrarea n funcia f;
v4 = g(): s = 4; e = 5; v1 = 9;
variabila s i pstreaz valoarea la intrarea n funcia g;
Se vor afia valorile: 5 6 7 9 5
___________________________________________________________________________________________________________________________

31

3.7. Calificatorul const


Valoarea unei variabile declarate cu acest calificator nu poate fi
modificat.
Sintaxa:
const tip nume-variabil valoare ;
nume-funcie(..., const tip *nume-variabil, ...);
n prima variant (declararea unei variabile) se atribuie o valoare
iniial unei variabile care nu mai poate fi ulterior modificat de
program. De exemplu,
const int virsta = 39;
Orice atribuire pentru variabila virsta va genera o eroare de
compilare.
n a doua variant (declararea unei funcii) calificatorul 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:
int printf (const char *format, ...);
Atenie! O variabil declarat cu const poate fi indirect
modificat prin intermediul unui pointer:
int *p = &virsta;
*p = 35;

___________________________________________________________________________________________________________________________

32

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


constant
O constant este o expresie-primar. Tipul su poate fi int,
long sau double. Constantele caracter snt de tip int, constantele
flotante snt de tip long double.

identificator
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 T, atunci valoarea
expresiei-identificator este un pointer la primul obiect al masivului,
iar tipul expresiei este pointer la T. Mai mult, un identificator de
masiv nu este o expresie valoare-stnga (detalii n capitolul opt).
La fel, un identificator declarat de tip funcie care returneaz T,
care nu apare pe poziie de apel de funcie este convertit la pointer la
funcie care returneaz T (detalii n capitolul opt).

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

33

(expresie)
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).

expresie-primar[expresie-indice]
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 T, expresia-indice are tipul
int, iar rezultatul are tipul T. O expresie de forma E1[E2] este
identic (prin definiie) cu *((E1)+(E2)), unde * este operatorul
de indirectare (detalii n capitolul opt).

expresie-primar(list-expresii)
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. Expresiaprimar trebuie s fie de tipul funcie care returneaz T, iar rezultatul
apelului de funcie va fi de tipul T (detalii n capitolul ase).
naintea apelului, oricare argument de tip float este convertit la
tipul double, oricare argument de tip char sau short este
convertit la tipul int. Numele de masive snt convertite n pointeri la
nceputul masivului. Nici o alt conversie nu se efectueaz automat.
Dac este necesar ca tipul unui argument actual s coincid cu cel
al argumentului formal, se va folosi un cast.
Snt permise apeluri recursive la orice funcie.
Despre definirea i apelul funciilor, detalii n capitolul ase.

valoare-stnga . identificator
O valoare-stnga urmat de un punct i un identificator este o
expresie-primar. Valoarea-stnga denumete o structur sau o
reuniune (capitolul nou) iar identificatorul denumete un membru
din structur sau reuniune. Rezultatul este o valoare-stnga care se
refer la membrul denumit din structur sau reuniune.

___________________________________________________________________________________________________________________________

34

expresie-primar - identificator
O expresie-primar urmat de o sgeat (constituit dintr-o
liniu i semnul urmat de un identificator) este o expresieprimar. 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 E1E2 este identic din punctul de vedere al
rezultatului cu (*E1).E2 (detalii n capitolul nou).

List-expresii
O list de expresii este considerat de asemenea expresie, dac
acestea snt separate prin virgul.

4.2. Operatori unari


Expresiile unare se grupeaz de la dreapta la stnga.

* expresie
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 T atunci tipul rezultatului este T. Acest
operator trateaz operandul su ca o adres, face acces la ea i i
obine coninutul (detalii n capitolul opt).

& valoare-stnga
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 T atunci tipul
rezultatului este pointer la T (detalii n capitolul opt).

expresie
Operatorul unar - este operatorul de negativare. Operandul su
este o expresie, iar rezultatul este negativarea operandului. n acest
___________________________________________________________________________________________________________________________

35

caz snt aplicate conversiile aritmetice obinuite. Negativarea unui


ntreg de tip unsigned se face scznd valoarea sa din 2 w, unde w
este numrul de bii rezervai tipului int.

! expresie
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 int.
Acest operator este aplicabil la orice expresie de tip aritmetic sau la
pointeri.

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

++ valoare-stnga
valoare-stnga ++
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.
___________________________________________________________________________________________________________________________

36

Rezultatul operaiei nu este o valoare-stnga, dar tipul su este


tipul valorii-stnga.

-- valoare-stnga
valoare-stnga -Operatorul unar -- este operatorul de decrementare. Acest
operator este analog cu operatorul ++ doar c produce decrementarea
cu 1 a operandului.

(nume-tip) expresie
Operatorul (nume-tip) este operatorul de conversie de tip. Prin
nume-tip nelegem unul dintre tipurile fundamentale admise n C
inclusiv tipul pointer. Operandul acestui operator este o expresie.
Operatorul produce conversia valorii expresiei la tipul denumit.
Aceast construcie se numete cast.

sizeof(operand)
Operatorul sizeof furnizeaz dimensiunea n octei a
operandului su. Aplicat unui masiv sau structuri, rezultatul este
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 este n comunicarea cu rutinele de alocare a memoriei sau
cele de intrare / ieire.
Operatorul sizeof poate fi aplicat i unui nume-tip. n acest caz
el furnizeaz dimensiunea n octei a unui obiect de tipul indicat.

4.3. Operatori aritmetici


Expresie-multiplicativ:
expresie * expresie
expresie / expresie
expresie % expresie
Operatorii multiplicativi * / i % snt operatori aritmetici binari
i se grupeaz de la stnga la dreapta.
___________________________________________________________________________________________________________________________

37

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.
Operatorul binar % furnizeaz restul mpririi primei expresii la
cea de a doua. Operanzii trebuie s fie de tip ntreg. 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.

Expresie-aditiv:
expresie + expresie
expresie - expresie
Operatorii aditivi + i - snt operatori aritmetici binari i se
grupeaz de la stnga la dreapta. Se execut conversiile aritmetice
obinuite.
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.4. Operatori de comparare


Expresie-relaional:
expresie < expresie
expresie > expresie
expresie <= expresie
expresie >= expresie
___________________________________________________________________________________________________________________________

38

Operatorii relaionali <, >, <=, >= se grupeaz de la stnga la


dreapta.
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 int. Se execut conversiile aritmetice
obinuite. Aceti operatori au precedena mai mic dect operatorii
aritmetici.

Expresie-egalitate:
expresie == expresie
expresie != expresie
Operatorii == (egal cu) i != (diferit de) snt analogi cu
operatorii relaionali, dar precedena lor este mai mic. Astfel c
a<b == c<d este 1 dac a<b i c<d au aceeai valoare de
adevr.

4.5. Operatori logici pe bii


Expresie-deplasare:
expresie << expresie
expresie >> expresie
Operatorii de deplasare << i >> snt operatori logici pe bii.
Ei se grupeaz de la stnga la dreapta.
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 int; tipul rezultatului este
cel 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
___________________________________________________________________________________________________________________________

39

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.

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.

Expresie-SAU-exclusiv:
expresie ^ expresie
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

___________________________________________________________________________________________________________________________

40

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.6. Operatori pentru expresii logice


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

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

41

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

4.7. 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
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 ?:
___________________________________________________________________________________________________________________________

42

este mai mic, dar ele pot fi folosite pentru a face expresia
condiional mai lizibil.

4.8. 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.
Expresiile de forma E1op=E2 se interpreteaz ca fiind
echivalente cu expresiile de forma E1=E1opE2; 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 (capitolul
opt). 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.
___________________________________________________________________________________________________________________________

43

4.9. 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 t5.
Prima valoare a lui t se pierde.

4.10. 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. Dm n continuare dou exemple:
x = f() + g();
a[i] = i++;
___________________________________________________________________________________________________________________________

44

n asemenea situaii trebuie memorate rezultate intermediare n


variabile temporare pentru a garanta o execuie n ordinea dorit.
Singurele cazuri n care se garanteaz evaluarea de la stnga la
dreapta se refer la operatorii pentru expresii logice && i ||. n
aceste cazuri se garanteaz n plus un numr minim de evaluri.
Operator
() [] -> .
! ++ -- - (tip) * & sizeof
* / %
+ << >>
< <= > >=
== !=
&
^
|
&&
||
?:
= op=
,

Asociativitate
stnga la dreapta
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
dreapta la stnga
stnga la dreapta

___________________________________________________________________________________________________________________________

45

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

5.1. Instruciunea expresie


Cele mai multe instruciuni snt instruciuni expresie. O expresie
devine instruciune dac ea este urmat de punct i virgul.
a = b + c;
n++;
scanf(. . .);
n limbajul C punct i virgula este un terminator de instruciune
i este obligatoriu.
O instruciune de atribuire poate aprea pe post de expresie ntr-o
alt instruciune. Funcia strcpy copiaz irul s peste irul t.
void strcpy(char t[], const char s[]) {
int i = 0;
while (t[i]=s[i]) i++;
}
Expresia t[i]=s[i] este o atribuire i are valoarea True sau
False n funcie de valoarea s[i].

5.2. Instruciunea compus sau blocul


Instruciunea compus este o grupare de declaraii i instruciuni
nchise ntre acolade. Aceasta a fost introdus cu scopul de a folosi
mai multe instruciuni acolo unde sintaxa cere o instruciune.
Instruciunea compus sau blocul este echivalent sintactic cu o
singur instruciune.
___________________________________________________________________________________________________________________________

46

Format:
{
list-declaratori
list-instruciuni
}
Dac anumii identificatori din lista-declaratori au fost declarai
anterior, atunci declaraia exterioar este ignorat pe durata blocului,
dup care i reia sensul su.
Orice iniializare pentru variabilele auto i register se
efectueaz la fiecare intrare n bloc. Iniializrile pentru variabilele
static 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 caracterul punct i virgul.

5.3. Instruciunea if
Sintaxa instruciunii condiionale if admite dou formate:
if (expresie)
if (expresie) instruciune-1
instruciune-1
else instruciune-2
Instruciunea if 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.
n al doilea caz una i numai una dintre cele dou instruciuni se
execut. Deoarece un if testeaz pur i simplu valoarea numeric a
unei expresii, se admite o prescurtare i anume:
if (expresie)
n loc de:
if (expresie != 0)
Deoarece partea else a unei instruciuni if este opional,
exist o ambiguitate cnd un else este omis dintr-o secven de if
___________________________________________________________________________________________________________________________

47

imbricat. Aceasta se rezolv asociind else cu ultimul if care nu


are else.
if (n>0)
if (a>b)
z = a;
else
z = b;

if (n>0) {
if (a>b)
z = a;
}
else
z = b;

n exemplul de mai sus, n primul caz partea else se asociaz


if-ului din interior. Dac nu dorim acest lucru atunci folosim
acoladele pentru a fora asocierea, ca n al doilea caz.
Instruciunea condiional admite i construcia else-if:
if (expresie-1) instruciune-1

else

if (expresie-2) instruciune-2

else
if (expresie-3) instruciune-3
else instruciune-4

Aceast cascad de if-uri 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.
Instruciunea dup ultimul else 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 ultima parte poate s lipseasc:
else instruciune-4

Pot exista un numr arbitrar de construcii:


else if (expresie) instruciune

grupate ntre un if iniial i un else final.


ntotdeauna un else se asociaz cu ultimul if ntlnit.
___________________________________________________________________________________________________________________________

48

5.4. Instruciunile while i do


Formatul instruciunii while:
while (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 (expresia din paranteze are o
valoare diferit de zero) se execut corpul instruciunii while, se
verific din nou condiia, dac ea este adevrat se execut din nou
corpul instruciunii. Cnd condiia devine fals (valoarea expresiei
din paranteze este zero) se continu execuia cu instruciunea de dup
corpul instruciunii while, deci instruciunea while se termin.
Formatul instruciunii do:
do instruciune
while (expresie);
Instruciunea se execut repetat pn cnd valoarea expresiei
devine zero. Testul are loc dup fiecare execuie a instruciunii.
unsigned n, c = 0;
scanf("%u",&n);
while (n>0) {
n /= 10; c++;
}
printf("%u",c);

unsigned n, c = 0;
scanf("%u",&n);
do {
c++; n /= 10;
} while (n>0);
printf("%u",c);

Secvena din stnga afieaz numrul de cifre zecimale ale valorii


lui n numai dac n 0, altfel afieaz zero. Secvena din dreapta
afieaz numrul de cifre zecimale ale valorii lui n n orice condiii.

5.5. Instruciunea for


Format:
for (expresie-1; expresie-2; expresie-3) instruciune
Aceast instruciune este echivalent cu secvena:
expresie-1;
___________________________________________________________________________________________________________________________

49

while (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
while 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 while i for 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.
Exemple. 1) n primul capitol, al treilea i al patrulea program
folosesc instruciunea for pentru a parcurge un ir de valori. S
notm faptul c fiecare expresie poate fi orict de complex. Secvena
urmtoare determin suma primelor n numere naturale.
unsigned s,i;
scanf("%u",&n);
for (s=0,i=1; i<=n; i++)
s += i;
printf("%u",s);

2) Funcia strlen(s) returneaz lungimea irului de caractere


s, excluznd caracterul terminal null.
int strlen(const char s[]) {
int n;
___________________________________________________________________________________________________________________________

50

for (n=0; s[i]; ++n) ;


return n;
}

3) Instruciunea de iniializare (expresie-1) trebuie s fie una


singur, de aceea, dac avem mai multe operaii de fcut, ele trebuie
separate prin virgul i nu prin punct i virgul. Aceeai observaie
este valabil i pentru expresie-3. Funcia strrev inverseaz un ir
de caractere:
void strrev(char s[]) {
int i,j;
for (i=0,j=strlen(s)-1; i<j; i++,j--) {
char c = s[i];
s[i] = s[j];
s[j] = c;
}
}

4) Relum problema din seciunea precedent: dndu-se o valoare


natural n, s se determine numrul de cifre zecimale.
unsigned n,c;
scanf("%u",&n);
for (c=!n; n; c++)
n /= 10;
printf("%u",c);

5) Funcia strcpy poate fi implementat i astfel.

void strcpy(char t[], const char s[]) {


int i;
for (i=0; t[i]=s[i]; i++) ;
}

5.6. Instruciunea switch


Instruciunea switch 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:
___________________________________________________________________________________________________________________________

51

switch (expresie) instruciune-bloc


Expresia este supus la conversiile aritmetice obinuite dar
rezultatul evalurii trebuie s fie de tip ntreg.
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 ntreg.
Poate exista de asemenea cel mult o instruciune etichetat cu
default:
Cnd o instruciune switch 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 secvena care urmeaz dup case-ul respectiv.
Dac nici o constant case nu este egal cu valoarea expresiei i
dac exist un prefix default, atunci se execut secvena de dup
el, altfel nici o instruciune din switch nu se execut.
Prefixele case i default nu altereaz fluxul de control, care
continu printre astfel de prefixe.
Pentru ieirea din switch se folosete instruciunea break sau
return. Dac o secven de instruciuni este urmat de un nou
case fr s fie ncheiat cu break sau return, nu se iese din
switch. n concluzie, ieirea din switch trebuie s fie explicit.
Exemplu. Un meniu poate fi implementat astfel:
char op[80];

do {

gets(op);
switch (op[0]) {
case 'i': case 'I':
Initializare(. . .); break;
case 'a': case 'A':
Adaugare(. . .); break;
case 'm': case 'M':
Modificare(. . .); break;
case 's': case 'S':
___________________________________________________________________________________________________________________________

52

Stergere(. . .); break;


case 'c': case 'C':
Consultare(. . .); break;
case 'l': case 'L':
Listare(. . .); break;
default:
Eroare(. . .); break;
}
} while (op!='t' && op!='T');

n funcie de opiunea dorit (primul caracter din irul op) se


selecteaz aciunea asociat valorii respective. Dac opiunea nu este
recunoscut, se selecteaz aciunea care semnaleaz o eroare. Toate
aceste aciuni se execut n cadrul unui ciclu do. Acesta se ncheie n
momentul n care se introduce opiunea 't' sau 'T'.

5.7. Instruciunile break i continue


Formatul instruciunii break:
break;
Aceast instruciune determin terminarea celei mai interioare
instruciuni while, do, for sau switch care o conine. Controlul
trece la instruciunea care urmeaz dup instruciunea astfel
terminat.
Exemplu. Fie un ir de caractere s; s se elimine spaiile de la
sfritul irului.
for (i=strlen(s)-1; i>=0; i--)
if (s[i]!=' ') break;
s[i+1] = 0;

Formatul instruciunii continue:


continue;
Aceast instruciune determin trecerea controlului la poriunea
de continuare a ciclului celei mai interioare instruciuni while, do
sau for care o conine, adic la sfritul ciclului i reluarea
urmtoarei iteraii a ciclului. n while i do se continu cu testul,
iar n for se continu cu expresie-3.
___________________________________________________________________________________________________________________________

53

Mai precis n fiecare dintre instruciunile:


while (. . .) {
...
contin:;
}

for (. . .) {
...
contin:;
}

do {
...
contin:;
} while (...);

dac apare o instruciune continue aceasta este echivalent cu un


salt la eticheta contin. Dup contin: urmeaz o instruciune
vid.
Secvena urmtoare prelucreaz numai elementele pozitive ale
unui masiv.
for (i=0; i<n; i++) {
if (a[i]<0)
/* sare peste elementele negative */
continue;
...
/* prelucreaz elementele pozitive */
}

Un exemplu de utilizare a instruciunii goto


Pentru a efectua un salt explicit n interiorul unui program C se
poate folosi i instruciunea goto, dei aceasta conduce la programe
greu de verificat.
Fie dou masive, a cu n elemente i b cu m elemente. S se
determine dac exist cel puin o valoare comun. O secven care
utilizeaz instruciunea goto se poate scrie astfel:
for (i=0; i<n; i++)
for (j=0; j<m; j++)
if (a[i]==b[j])
goto egale;
/* Nu s-a gsit nici un element; prelucrri necesare acestui caz */
goto contin;
egale:
/* Un element comun pe poziiile i i j; prelucrri necesare acestui caz */
contin:
/* Continu execuia dup finalizarea prelucrrilor pentru cazul depistat */

Dac scriem o funcie n acest scop, nu avem nevoie s folosim


instruciunea goto. Funcia va memora poziiile i i j n variabile
___________________________________________________________________________________________________________________________

54

ale cror adrese snt comunicate de o funcie apelant. Dac nu exist


o valoare comun atunci aceste variabile vor conine valori negative.
void Comun(double a[], int n, double b[], int m,
int *pi, int *pj) {
int i,j;
*pi = *pj = -1;
for (i=0; i<n; i++)
for (j=0; j<m; j++)
if (a[i]==b[j]) {
*pi = i; *pj = j;
return;
}
}

Aceast funcie poate fi apelat astfel:


Comun(a,n,b,m,&k,&l);

5.8. Instruciunea return


O instruciune return permite ieirea dintr-o funcie i
transmiterea controlului apelantului funciei. O funcie poate returna
valori apelantului su, prin intermediul unei instruciuni return.
Formate:
return;
return expresie;
Primul format se folosete pentru funcii de tip void, care nu
returneaz n mod explicit o valoare. n al doilea format valoarea
expresiei este returnat funciei apelante. Dac este nevoie, expresia
este convertit, ca ntr-o atribuire, la tipul funciei n care ea apare.
Detalii despre instruciunea return vor fi prezentate n
capitolul urmtor.

5.11. Instruciunea vid


Format:
;
___________________________________________________________________________________________________________________________

55

Instruciunea vid este util ntr-o instruciune compus, sau


pentru a introduce un corp nul ntr-o instruciune de ciclare care cere
corp al instruciunii, ca de exemplu while sau for.
Exemplu:
for (nc=0; s[nc]; ++nc) ;

n aceast instruciune, care numr caracterele unui ir, corpul


lui for este vid, deoarece toat activitatea se face n partea de test i
actualizare dar sintaxa lui for cere un corp al instruciunii.
Instruciunea vid satisface acest lucru.
Am precizat anterior c o instruciune compus nu este urmat
niciodat de caracterul punct i virgul.
if (...) {
...
};
else . . .

Aceast secven este greit, deoarece dup instruciunea


compus urmeaz o instruciune vid, i clauza else apare izolat
fr a avea nici o instruciune if asociat.

___________________________________________________________________________________________________________________________

56

6. Funcii
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, permit desfacerea programelor mari
n module mai 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.
Un program C const dintr-o secven de definiii externe de
funcii i de date. n fiecare program trebuie s existe o funcie cu
numele impus main; orice program i ncepe execuia cu funcia
main. Celelalte funcii snt apelate din interiorul funciei main.
Unele dintre funciile apelate snt definite n acelai program, altele
snt coninute ntr-o bibliotec de funcii.

6.1. Definiia unei funcii


O funcie se definete astfel:
clas tip-funcie nume-funcie(list-parametri) {
list-declaraii
list-instruciuni
}
ntr-o declaraie, numele funciei este urmat de paranteze chiar
dac lista parametrilor este vid. Dac funcia are mai muli
___________________________________________________________________________________________________________________________

57

parametri, declaraiile acestora snt separate prin virgul. Un


declarator de funcie care nu o i definete nu conine corpul funciei,
acesta fiind nlocuit cu caracterul punct i virgul.
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 o valoare de alt tip dect int, sau dac tipul acesteia este
void. O funcie de tip void nu returneaz o valoare n mod
explicit, execut doar o secven de instruciuni.
Numele funciei poate fi de asemenea precedat de clasa de
memorie extern sau static, ali specificatori de clas nu snt
admii. 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.
Dac o funcie este declarat static, ea poate fi apelat numai din
fiierul unde a fost definit.
Comunicarea ntre funcii se face prin argumente i valori
returnate de funcii. Comunicarea ntre funcii poate fi fcut i prin
intermediul variabilelor globale.
Se recomand ca definiia unei funcii sau cel puin declararea ei
s se fac nainte de orice apelare. Astfel compilatorul poate depista
imediat neconcordane ntre tipurile de parametri.
S ilustrm mecanismul definirii unei funcii scriind funcia
factorial fact(n). Aceast funcie este folosit ntr-un program
care calculeaz valoarea expresiei

Cnk

n!
k!( n k )!

#include <stdio.h>
int fact(int n) {
int i,p;
for (p=i=1; i<=n; i++)
p *= i;
return p;
}
___________________________________________________________________________________________________________________________

58

int main() {
int n,k,i;
puts("Valori n si k: ");
scanf("%d %d",&n,&k);
printf("Combinari:%d\n",fact(n)/fact(k)/fact(n-k));
return 0;
}

Funcia fact este apelat n programul principal de trei ori,


fiecare apel transmite funciei un argument.
O secven mai eficient din punctul de vedere al volumului de
calcul i al corectitudinii acestora se bazeaz pe urmtoarea
n n 1
n k 1
...
formul: C nk
1 2
k
Funcia fact produce rezultate eronate pentru valori mari ale lui
n (n8, sisteme de calcul pe 16 bii; n12, sisteme de calcul pe 32
de bii). Funcia comb, bazat pe relaia de mai sus, permite operarea
cu valori mai mari ale parametrilor n i k.
int comb(int n, int k) {
int i,p;
for (p=i=1; i<=k; i++)
p = p * (n-i+1) / i;
return p;
}

Atenie! n acest caz nu putem folosi forma compact


p *= (n-i+1) / i;

deoarece se va evalua mai nti expresia din dreapta i rezultatul va fi


nmulit cu valoarea lui p. Astfel se vor obine rezultate eronate
pentru orice valoare n par.

6.2. Apelul funciilor; funcii recursive


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 stnga (, adic nu se apeleaz funcia respectiv, se
___________________________________________________________________________________________________________________________

59

genereaz un pointer la funcie. Detalii despre acest subiect vor fi


prezentate n capitolul opt.
Funciile n C pot fi definite 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.
Exemple. 1) Funcia rcomb implementeaz formula recursiv de
calcul a valorii

Cnk :

int rcomb(int n, int k) {


if (n==k || k==0) return 1;
else return rcomb(n,k-1) + rcomb(n-1,k-1);
}

2) Funcia rfibo implementeaz procedura recursiv de calcul a


termenului de rang n din irul lui Fibonacci:

n, n 1
Fn
Fn1 Fn2 , n 1
int rfibo(int n) {
if (n<=1) return 1;
else return fibo(n-1) + fibo(n-2);
}

Simplitatea cu care snt definite aceste funcii ar putea cuceri


imediat pe un programator neexperimentat. Trebuie s atragem
atenia asupra faptului c cele dou definiii au o complexitate
exponenial: pentru o valoare oarecare a lui n, numrul de operaii
elementare efectuate (aritmetice i logice) este aproximativ
proporional cu 2n.
De aceea recursivitatea este justificat numai n situaia n care
tim cu certitudine c se obine o definiie avnd complexitate
acceptabil. Un exemplu n acest sens va fi dat n capitolul nou:
gestionarea unei structuri de date de tip arbore.
___________________________________________________________________________________________________________________________

60

Am vzut n seciunea precedent o definiie simpl i eficient a


unei funcii care calculeaz valoarea Cnk . n continuare prezentm o
definiie simpl i eficient pentru determinarea unui termen din irul
lui Fibonacci:
int
int
a =
for

fibo(int n) {
a,b,c,i;
0; b = 1; c = n;
(i=2; i<=n; i++) {
c = a + b;
a = b; b = c;
}
return p;
}

Funcia folosete variabilele locale a i b pentru a memora


termenii Fk2 i Fk1. La un anumit pas se calculeaz FkFk2Fk1 n
variabila c, i apoi variabilele a i b memoreaz termenii Fk1 i Fk.

6.3. Revenirea din funcii


Revenirea dintr-o funcie se face prin intermediul unei
instruciuni return.
Valoarea pe care o funcie o calculeaz poate fi returnat prin
instruciunea return, care dup cum am vzut are dou formate. Ca
argument al acestei instruciuni poate aprea orice expresie admis n
C. Funcia returneaz valoarea acestei expresii funciei apelante.
O funcie de tip void nu returneaz n mod explicit o valoare. O
instruciune return, fr expresie ca parametru, cauzeaz doar
transferul controlului funciei apelante.
La rndul su funcia apelant poate ignora valoarea returnat.
Dac nu se precizeaz tipul funciei, aceasta returneaz o valoare
de tip ntreg. Dac se dorete ca funcia s returneze un alt tip, atunci
numele tipului trebuie s precead numele funciei.
Pentru a evita orice confuzie recomandm ca tipul valorii
returnate de funcie s fie ntotdeauna precizat, iar dac dorim n mod
expres ca funcia s nu returneze o valoare n mod explicit s o
declarm de tip void.
___________________________________________________________________________________________________________________________

61

De exemplu, funcia atof(s) din biblioteca asociat limbajului


C convertete irul s de cifre n valoarea sa n dubl precizie. Putem
declara funcia sub forma:
double atof(char s[]);
Dac o funcie returneaz o valoare de tip char, nu este nevoie
de nici o declaraie de tip datorit conversiilor implicite. Totdeauna
tipul char este convertit la int n expresii.

6.4. Argumentele funciei i transmiterea


parametrilor
O metod de a comunica datele ntre funcii este prin argumente.
Parantezele care urmeaz imediat 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 locale (concret
pe stiv).
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 factorial care face
uz de acest fapt:
int fact(int n) {
int p;
for (p=1; n>0; --n)
p *= n;
return p;
}

Argumentul n este utilizat ca o variabil automatic 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.
___________________________________________________________________________________________________________________________

62

Dac totui se dorete alterarea efectiv a unui argument al


funciei apelante, acest lucru se realizeaz cu ajutorul pointerilor sau
al variabilelor declarate externe. n cazul pointerilor, funcia apelant
trebuie s furnizeze adresa variabilei care trebuie modificat (tehnic
printr-un pointer la aceast variabil), iar funcia apelat trebuie s
declare argumentul corespunztor ca fiind un pointer. Referirea la
variabila care trebuie modificat se face prin adresare indirect
(capitolul opt).
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. Dac a fost precizat calificatorul const,
modificarea nu este permis.
Pentru funciile cu numr variabil de parametri standardul ANSI
definete o construcie special ... (trei puncte) numit elips.
Acesta este de exemplu cazul funciilor de citire i scriere cu format
(familiile ...scanf i ...printf capitolul zece). n acest caz, parametrii
fici snt verificai la compilare, iar cei variabili snt transmii fr
nici o verificare.

6.5. Exemple de funcii i programe


1. Acest exemplu este un program de cutare i afiare a tuturor
liniilor dintr-un text care conin o anumit configuraie de caractere,
de exemplu cuvntul englezesc "the". Structura de baz a acestui
program este:
while (mai exist linii surs)
if (linia conine configuraia de caractere)
afieaz linia
Pentru a citi cte o linie din fiierul de intrare vom folosi funcia
gets din bibliotec, cunoscut din primul capitol unde a fost folosit
ntr-un exemplu. Aceast funcie citete o linie de la intrarea standard
i o depune ntr-un ir de caractere precizat prin argumentul acesteia.
Dac la intrarea standard mai exist linii (se mai poate citi) funcia
___________________________________________________________________________________________________________________________

63

returneaz adresa irului unde a fost memorat linia, n caz contrar


funcia returneaz valoarea NULL (zero).
Funcia index 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.
Pentru afiarea unei linii folosim funcia puts din bibliotec,
de asemenea cunoscut din primul capitol.
Programul care realizeaz structura de mai sus este:
#include <stdio.h>
int index(char s[], char t[]) {
/* returneaz poziia din irul s unde ncepe irul t, sau 1 */
int i,j,k;
for (i=0; s[i]!=0; i++) {
for (j=i, k=0; (t[k]!=0) && (s[j]==t[k]);
j++, k++) ;
if (t[k]==0)
return i;
}
return -1;
}
int main() {
/* afieaz toate liniile care conin cuvntul "the" */
char lin[80];
while (gets(lin)>0)
if (index(lin,"the")>=0)
puts(lin);
}

2. Funcia atof convertete un ir de cifre din formatul ASCII


ntr-un numr flotant n precizie dubl. Aceast funcie accept un ir
care conine reprezentarea unui numr zecimal (eventual cu semn),
compus din parte ntreag i parte fracionar (oricare putnd lipsi),
separate prin punct zecimal.
double atof(char s[]) {
double val,fr;
int i,sn;
___________________________________________________________________________________________________________________________

64

sn = 1; i = 0;
if (s[0]=='+' || s[0]=='-')
sn = (s[i++]=='+') ? 1 : -1;
for (val=0; s[i]>='0' && s[i]<='9'; i++)
val = 10 * val + s[i] - '0';
if (s[i]== '.')
i++;
for (fr=1; s[i]>='0' && s[i]<='9'; i++) {
val = 10 * val +s[i] - '0':
fr *= 10;
}
return sn * val / fr;
}

3. Funcia atoi convertete un ir de cifre n echivalentul lor


numeric ntreg.
int atoi(char s[]) {
int i,sn,v;
i = 0; sn = 1;
if (s[i]=='+' || s[i]=='-')
sn = (s[i++]=='+') ? 1 : -1;
for (v=i=0; s[i]>='0' && s[i]<='9'; i++)
v = 10 * v + s[i] - '0';
return sn * v;
}

4. Funcia lower 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.
int lower(int c) {
if (c>='A' && c<='Z')
return c + 'a' - 'A';
else
return c;
}

5. Funcia binary realizeaz cutarea valorii x ntr-un masiv


sortat v, care are n elemente.
___________________________________________________________________________________________________________________________

65

int binary(int x, int v[], int n) {


int l,r,m;
l = 0; r = n - 1;
while (l<=r) {
m = (l + r) / 2;
if (v[m]==x) return m;
if (v[m]<x) l = m + 1;
else r = m - 1;
}
return -1;
}

Funcia returneaz poziia lui x (un numr ntre 0 i n1), dac x


apare n v sau 1 altfel.

___________________________________________________________________________________________________________________________

66

7. Dezvoltarea programelor mari


n multe situaii este mai convenabil s fragmentm un program
mare n module mai mici, pe care le putem dezvolta mai uor
independent unele de altele, i n final s le asamblm ntr-un singur
program. De altfel acesta este principiul de baz care se aplic la
proiectele realizate n colective de programatori.
Dintre modulele (fiierele surs) care intr n compunerea
programului, unul singur conine funcia main. Acest modul apeleaz
funcii i utilizeaz variabile care pot fi definite n alte module, cu
alte cuvinte snt externe. Deoarece aceste module snt compilate n
mod independent, este necesar un mecanism care s permit o
declarare unitar a elementelor puse n comun. Astfel compilatorul
poate ti n orice moment cel fel de parametri ateapt o anumit
funcie, sau n ce context poate fi utilizat o variabil.
Limbajul C ofer o facilitate de extensie cu ajutorul unui
preprocesor de macro-operaii simple. Folosirea directivelor de
compilare #define i #include 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.

7.1. Includerea fiierelor


#include "nume-fiier"

Aceast linie 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 directoare standard, cum ar fi biblioteca I/O standard
asociat compilatorului.
Linia urmtoare caut fiierul nume-fiier numai n biblioteca
standard i nu n directorul fiierului surs:
___________________________________________________________________________________________________________________________

67

#include nume-fiier

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 #define 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 #include,
toate fiierele care depind de el trebuie recompilate.

7.2. nlocuirea simbolurilor; substituii macro


O definiie de forma:
#define identificator ir-simboluri

determin ca preprocesorul s nlocuiasc toate apariiile ulterioare


ale identificatorului cu ir-simboluri dat (ir-simboluri nu se termin
cu ; (punct i virgul) deoarece n urma substituiei identificatorului
cu acest ir ar aprea prea multe caractere ; unele situaii conduc
chiar la erori de sintax.
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 #define poate fi
redefinit ulterior n program printr-o alt linie #define. 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:
#define ALFA 1

Oriunde va aprea n programul surs identificatorul ALFA el va


fi nlocuit cu constanta 1, cu excepia unei situaii de forma:
printf("ALFA");

n care se va afia chiar textul ALFA i nu constanta 1, deoarece


identificatorul este ncadrat ntre ghilimele.
___________________________________________________________________________________________________________________________

68

Aceast facilitate este deosebit de valoroas pentru definirea


constantelor simbolice ca n:
#define NMAX 100
int tab[NMAX];

deoarece ntr-o eventual modificare a dimensiunii tabelului tab se


va modifica doar o singur linie n fiierul surs. O linie de forma:
#define identif(identif-1,...,identif-n) ir-simboluri

n care nu exist spaiu ntre primul identificator i caracterul


( (parantez stnga) 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:
#define 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 macrooperaia Square(x) definit prin:
#define Square(x) x*x

care se apeleaz n programul surs sub forma:


z = Square(z+1);

va produce un rezultat, altul dect cel scontat, datorit prioritii mai


mari a operatorului * fa de cea a operatorului +.
___________________________________________________________________________________________________________________________

69

7.3. Compilarea condiionat


O linie de control a compilatorului de forma urmtoare verific
dac expresia constant este evaluat la o valoare diferit de zero:
#if expresie-constant

O linie de control de forma urmtoare


identificatorul a fost deja definit:

verific dac

#ifdef identificator

O linie de control de forma urmtoare verific dac


identificatorul este nedefinit:
#ifndef identificator

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:
#else

i apoi de o linie de control de forma:


#endif

Dac condiia supus verificrii este adevrat, atunci orice linie


ntre #else i #endif este ignorat. Dac condiia este fals atunci
toate liniile ntre testul de verificare i un #else sau n lipsa unui
#else pn la #endif snt ignorate.
Toate aceste construcii pot fi imbricate.

7.4. Exemple
1) 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 main i apeleaz dou funcii: eprim i cmmdc. Al
doilea fiier implementeaz cele dou funcii.
Prezentm mai nti un fiier header (numere.h) care conine
declaraiile celor dou funcii.
#ifndef _Numere_H
#define _Numere_H
unsigned eprim(unsigned n);
___________________________________________________________________________________________________________________________

70

unsigned cmmdc(unsigned p, unsigned q);


#endif

Fiierul surs princn.c conine funciile main i citire. S


observm c funcia citire este declarat static, de aceea nu
poate fi apelat de alte module.
#include <stdio.h>
#include "numere.h"
static void citire(unsigned *n) {
scanf("%u",n);
if (eprim(*n))
printf("%u e prim\n",*n);
else
printf("%u nu e prim\n",*n);
}
int main() {
unsigned p,q,k;
citire(&p); citire(&q);
k = cmmdc(p,q);
printf("Cmmdc: %u\n",k);
return 0;
}

Fiierul surs numere.c este proiectat pentru a putea fi folosit


n mai multe aplicaii care au nevoie de astfel de funcii. Prin
includerea fiierului numere.h se garanteaz c definiia funciilor
este conform cu ateptrile modulului principal, n care este inclus
acelai fiier header.
#include "numere.h"
unsigned eprim(unsigned n) {
unsigned i,a,r;
if (n==0) return 0;
if (n<4) return 1;
if ((n&1)==0) return 0;
for (i=3; ; i+=2) {
r = n%i;
if (r==0) return 0;
a = n/i;
if (a<=i) return 1;
___________________________________________________________________________________________________________________________

71

}
}
unsigned cmmdc(unsigned p, unsigned q) {
while ((p>0) && (q>0))
if (p>q) p %= q;
else q %= p;
if (p==0) return q;
else return p;
}

Pentru a obine un program executabil din aceste dou fiiere se


poate utiliza o singur comand de compilare:
Cc princ.c numere.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.
2) Al doilea proiect gestioneaz o list de valori numerice asupra
creia se efectueaz urmtoarele operaii:
iniializare, operaie realizat automat la lansarea n execuie a
programului;
adugarea unei valori n list, dac aceasta nu exist deja;
interogare: o anumit valoare se afl n list?
tergerea unei valori din list;
meninerea n permanen a unei relaii de ordine cresctoare.
Masivul Lis[] memoreaz lista de valori, care are un numr de
ne elemente, iniial zero.
Prezentm mai nti un fiier header (lista.h) care conine
declaraiile celor trei funcii, precum i a variabilelor externe Lis i
ne.
#ifndef _Lista_H
#define _Lista_H
extern double Lis[];
extern int ne;
int Adaugare(double v);
int Stergere(double v);
int Interogare(double v);
___________________________________________________________________________________________________________________________

72

#endif

Fiierul surs princl.c conine funcia main. Acesta ateapt


de la terminal opiunea utilizatorului, urmat de un spaiu i de
valoarea care se dorete a fi prelucrat. n funcie de opiune se
apeleaz funcia corespunztoare sau se afieaz un mesaj de eroare.
Dup fiecare operaie se afieaz lista valorilor.
#include <stdio.h>
#include "lista.h"
int main() {
double atof(char s[]);
double v;
int i;
char o[80];
do {
gets(o); v = atof(op+2);
switch (o[0]) {
case 'a':
i = Adaugare(v);
break;
case 's':
if (Stergere(v))
puts("S-a sters");
else puts("Nu exista");
break;
case 'i':
i = Interogare(v);
if (i>=0)
printf("Pozitia: %d\n",i);
else puts("Nu exista");
break;
default:
puts("Optiune eronata");
break;
}
puts("Lista:");
for (i=0; i<ne; i++)
printf(" %.3lf",Lis[i]);
puts("");
} while (o!='t');
___________________________________________________________________________________________________________________________

73

return 0;
}

Fiierul surs lista.c definete variabilele Lis[] i ne, i


funciile Adaugare, Interogare, Stergere.
Funcia Adaugare adaug o valoare v n list dac aceasta nu
exist. Valorile mai mari snt deplasate spre dreapta cu o poziie
pentru a face loc noii valori. Returneaz poziia valorii n list dup
(o eventual) adugare.
Funcia Stergere terge o valoare v din list dac aceasta
exist. Valorile mai mari snt deplasate spre stnga cu o poziie pentru
a pstra lista compact. Returneaz True dac operaia a reuit i
False n caz contrar.
Funcia Interogare returneaz poziia pe care se afl valoarea
v n list, dac aceasta exist, sau 1 n caz contrar. Deoarece lista
este ordonat se face o cutare binar. Aceast funcie este apelat i
de funcia Adaugare.
#include "lista.h"
double Lis[100];
int ne;
int Adaugare(double v) {
int k;
k = Interogare(v);
if (k<0) {
for (k=ne-1; k>=0; k--)
if (Lis[k]>v)
Lis[k+1] = Lis[k];
else break;
Lis[k+1] = v; ne++;
}
return k;
}
int Stergere(double v) {
int k;
k = Interogare(v);
if (k<0) return 0;
for (; k<ne; k++)
___________________________________________________________________________________________________________________________

74

Lis[k] = Lis[k+1];
ne--;
return 1;
}
int Interogare(double v) {
int l,r,m;
double d;
l = 0; r = ne 1;
while (l<=r) {
m = (l + r) / 2; d = Lis[m] v;
if (d==0) return m;
if (d<0) l = m + 1;
else r = m 1;
}
return 1;
}

Proiectele programe de dimensiuni mari snt compuse de cele


mai multe ori din module care se elaboreaz i se pun la punct n mod
independent. 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 definite
aceste funcii, i acesta se pstreaz ca i declaraii n fiiere header.
Fiierele header pstreaz i alte informaii comune mai multor
module, cum snt de exemplu tipurile definite de programator, cel
mai adesea tipuri structurate (detalii n capitolul nou).

___________________________________________________________________________________________________________________________

75

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

8.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 un ntreg. 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.


Variabila x are asociat o zon de memorie, i aceast zon are
(presupunem) adresa 8A54. Dup atribuirea px &x, variabila px
va avea valoarea 8A54.
Evident toate variabilele care snt implicate n aceste instruciuni
trebuie declarate. Aceste declaraii snt:
int x,y,*px;

Declaraiile variabilelor x i y snt deja cunoscute. Declaraia


pointerului px este o noutate. Aceasta 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.
___________________________________________________________________________________________________________________________

76

Pointerii pot aprea i n expresii; n expresia urmtoare


variabilei y i se atribuie valoare variabilei x plus 1:
y = *px + 1;

Instruciunea urmtoare are ca efect convertirea valorii variabilei


x pe care o indic px n tip double i apoi depunerea rdcinii
ptrate a valorii astfel convertite n variabila d:
d = sqrt((double)*px);

Referiri la pointeri pot aprea de asemenea i n partea stng a


atribuirilor. Dac, de exemplu, px indic spre x, atunci urmtoarea
instruciune atribuie variabilei x valoarea zero:
*px = 0;

Instruciunile urmtoare snt echivalente, i incrementeaz


valoarea variabilei x cu 1:
*px += 1;
(*px)++;

n acest ultim exemplu parantezele snt obligatorii deoarece, n


lipsa lor, expresia ar incrementa pe px n loc de valoarea variabilei pe
care o indic (operatorii unari *, ++ au aceeai preceden i snt
evaluai de la dreapta spre stnga).

Pointeri de tip void (tip generic de pointer)


Un pointer poate fi declarat de tip void. n aceast situaie
pointerul nu poate fi folosit pentru indirectare (derefereniere) fr un
cast explicit, i aceasta deoarece compilatorul nu poate cunoate tipul
obiectului pe care pointerul l indic.
int i;
float f;
void *p;
p = &i;
/* p indic spre i */
*(int *)p = 2;
p = &f;
/* p indic spre f */
*(float *)p = 1.5;

Pointerii generici snt foarte utilizai de unele funcii de


bibliotec, atunci cnd este nevoie s se opereze cu zone de memorie
care pot conine informaii de orice natur: operaii de intrare / ieire
___________________________________________________________________________________________________________________________

77

fr format (capitolul zece), alocri de memorie, sortare i cutare


(capitolul unsprezece). Aceste funcii nu cunosc tipul datelor cu care
opereaz, pentru aceste funcii zonele de memorie nu au un tip
precizat. Acest fapt este evideniat prin pointeri de tip void.

8.2 Pointeri i argumente de funcii


Dac 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 dorim s modificm valoarea unui argument?
De exemplu, o rutin de sortare trebuie s schimbe ntre ele dou
elemente care nu respect ordinea dorit, cu ajutorul unei funcii
swap. Fie funcia swap definit astfel:
void swap(int x, int y) {
int tmp;
tmp = x;
x = y;
y = tmp;
}

/* greit */

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 swap este:


void swap(int *px, int *py) {/* interschimb *px i *py */
int tmp;
tmp = *px;
*px = *py;
*py = tmp;
}

Modificarea nu este permis dac parametrul de tip pointer este


precedat de declaratorul const.
___________________________________________________________________________________________________________________________

78

8.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 conduce n plus la o accelerare
a operaiei. Declaraia:
int 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 i1, ncepnd
cu primul element. Dac pa este un pointer la un ntreg, i a este un
masiv de ntregi, atunci instruciunea de atribuire ncarc variabila
pa cu adresa primului element al masivului a.
int *pa,a[10];
pa = &a[0];

Atribuirea urmtoare copiaz coninutul lui a[0] n x:


x = *pa;

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.
Cele dou instruciuni de atribuire snt identice:
pa = &a[1];
___________________________________________________________________________________________________________________________

79

pa = a + 1;

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
urmtoarele instruciuni snt corecte:
pa = a;
pa++;

Dar un nume de masiv este o constant i deci urmtoarele


construcii nu snt permise:
a = pa;
a++;
p = &a;

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
pointer, adic o variabil care conine o adres. Fie de exemplu
funcia strlen care determin lungimea irului s:
int strlen(const char *s) {
int n;
for (n=0; *s; s++) n++;
return n;
}

Incrementarea lui s este legal deoarece s este o variabil


pointer. s++ nu afecteaz irul de caractere din funcia care apeleaz
pe strlen, ci numai copia adresei irului din funcia strlen.
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)

___________________________________________________________________________________________________________________________

80

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(int arr[]) {. . .}
f(int *arr) {. . .}

Declaraiile int arr[] i int *arr snt echivalente.

8.4. Aritmetica de adrese


Dac p este un pointer, atunci urmtoarea instruciune
incrementeaz pe p pentru a indica cu i elemente dup elementul pe
care l indic n prealabil p:
p += i;

Aceast construcie i altele similare snt cele mai simple i


comune formule ale aritmeticii de adrese, care constituie o
caracteristic puternic a limbajului C.
Pentru exemplificare relum inversarea caracterelor unui ir:
void strrev(char s[]) {
int i,j;
char c;
for (i=0,j=strlen(s)-1; i<j; i++,j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}

Versiunea cu pointeri a acestei funcii este:


void strrev(char s[]) {
char *p,*q,c;
for (p=s,q=s+strlen(s)-1; p<q; p++,q--) {
c = *p;
*p = *q;
*q = c;
}
}
___________________________________________________________________________________________________________________________

81

Exemplul de mai sus demonstreaz cteva din facilitile


aritmeticii de adrese (pointeri). n primul rnd, pointerii pot fi
comparai n anumite situaii. Relaiile <, <=, >, >=, ==, != snt
valide numai dac p i q snt pointeri la elementele aceluiai masiv.
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 indic p, dimensiunea fiind determinat din
declaraia lui p (scara de aliniere este 1 pentru char, 2 pentru
short int 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
alte versiuni ale funciei strlen folosind aceast ultim observaie:
int strlen(char *s) {
char *p;
p = s;
while (*p) p++;
return p-s;
}

int strlen(char *s) {


char *p;
for (p=s; *p; p++) ;
return 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.
Snt admise de asemenea incrementrile i decrementrile
precum i alte combinaii ca de exemplu *++p i *--p.
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,
___________________________________________________________________________________________________________________________

82

nmulirea, mprirea sau deplasarea pointerilor, dup cum nici


adunarea lor cu constante de tip double sau float.

8.5. Pointeri la caracter i funcii


O constant ir 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.
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:
puts("Buna dimineata");

funcia puts 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
strcpy(t,s) care copiaz irul s (source) peste irul t (target). O
prim versiune a programului ar fi urmtoarea:
void strcpy(char t[], const char s[]) {
int i;
i = 0;
while (t[i]=s[i]) i++;
}

O a doua versiune cu ajutorul pointerilor este urmtoarea:


void strcpy(char *t, const char *s) {
while (*t++=*s++) ;
}

Aceast versiune cu pointeri modific prin incrementare pe t i s


n partea de test. Valoarea lui *s++ este caracterul indicat de
___________________________________________________________________________________________________________________________

83

pointerul s, nainte de incrementare. Notaia postfix ++ asigur c s


va fi modificat dup preluarea coninutului indicat de el, de la vechea
poziie a lui s, dup care s se incrementeaz. La fel, t va fi
modificat dup memorarea caracterului la vechea poziie a lui t,
dup care i t se incrementeaz. Efectul este c se copiaz
caracterele irului s n irul t pn la caracterul terminal 0 inclusiv.
Am preferat s evitm redundana comparrii cu caracterul 0,
facilitate care rezult din structura instruciunii while. Am fi putut
scrie i astfel:
while ((*t++=*s++)!=0) ;

S considerm, ca al doilea exemplu, funcia strcmp(t,s)


care compar caracterele irurilor t i s i returneaz o valoare
negativ, zero sau pozitiv, dup cum irul t este lexicografic mai
mic, egal sau mai mare ca irul s. Valoarea returnat se obine prin
scderea caracterelor primei poziii n care t i s difer.
O prim versiune a funciei strcmp(t,s) este
urmtoarea:
int strcmp(const char t, const char s) {
int i;
i = 0;
while (t[i]==s[i])
if (t[i++]==0)
return 0;
return t[i]-s[i];
}

O versiune cu pointeri a aceleiai funcii este:


int strcmp(const char *t, const char *s) {
for (; *t==*s; t++,s++)
if (*t==0)
return 0;
return *t-*s;
}

8.6. Masive multidimensionale


___________________________________________________________________________________________________________________________

84

Limbajul C ofer facilitatea utilizrii masivelor multidimensionale, dei n practic ele snt folosite mai rar dect masivele
de pointeri.
Masivele multi-dimensionale snt folosite mai ales de
programatorii nceptori n scop didactic.
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 NrZile convertete ziua i luna n numrul de zile de la
nceputul anului, i funcia LunaZi convertete numrul de zile 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:
static int Zile[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 Zile trebuie s fie declarat global pentru a putea fi


folosit de ambele funcii.
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 multe alte limbaje. ntr-un masiv bidimensional
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
___________________________________________________________________________________________________________________________

85

exemplului nostru, masivul Zile 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:
int
int
bis
for

NrZile (int an, int luna, int zi) {


i,bis;
= !(an%4) && (an%100) || !(an%400);
(i=1; i<luna; i++)
zi += Zile[bis][i];
return zi;
}

Deoarece variabila bis poate lua ca valori numai zero sau unu,
dup cum expresia (scris de data aceasta explicit):
(an%4==0) && (an%100!=0) || (an%400==0)

este fals sau adevrat, ea poate fi folosit ca indice de linie n


tabelul Zile care are doar dou linii n exemplul nostru.
void LunaZi(int an, int zian, int *plun, int *pzi) {
int i,bis;
bis = !(an%4) && (an%100) || !(an%400);
for (i=1; zian>Zile[bis][i]; i++)
zian -= Zile[bis][i];
*plun = i; *pzi = zian;
}

Deoarece aceast ultim funcie returneaz dou valori,


argumentele care corespund lunii i zilei snt pointeri.
Exemplu. LunaZi(1984,61,&m,&d) va ncrca pe m cu 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 Zile trebuie transmis unei funcii f, atunci declaraia lui f
poate fi:
f(int (*Zile)[13]);
___________________________________________________________________________________________________________________________

86

f(int Zile[][13]);

unde declaraia (*Zile)[13]) indic faptul c argumentul lui f


este un pointer la un masiv de 13 ntregi.
n general, un masiv d-dimensional a[i][j]...[l] de rangul
i*j*...*l este un masiv (d1)-dimensional de rangul j*k*...*l ale
crui elemente, fiecare, snt masive (d2)-dimensionale de rang
k*...*l ale crui elemente, fiecare, snt masive (d3)-dimensionale
.a.m.d. Oricare dintre expresiile urmtoare: a[i], a[i][j]...,
a[i][j]...[l] pot aprea n expresii. Prima are tipul masiv, ultima
are tipul int, de exemplu, dac masivul este de tipul int. Vom mai
reveni asupra acestei probleme cu detalii.

8.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.
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.
Citirea textului decurge astfel. Se citete o linie din textul
introdus de la intrarea standard, irul de caractere fiind depus ntr-un
masiv lin. Deoarece i urmtoarele linii vor fi depuse tot aici, este
nevoie s se salveze coninutul acestui masiv. n acest scop se cere
sistemului de operare s se creeze un duplicat al acestui ir prin
apelul funciei strdup. Aceasta va rezerva o zon de memorie, va
copia irul dat n zona respectiv i ne va comunica adresa acestei
zone.
Funciile care opereaz cu iruri de caractere snt declarate cu:
#include <string.h>

Adresele liniilor snt memorate ntr-un masiv de pointeri la


caracter. Astfel 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.
___________________________________________________________________________________________________________________________

87

n primul pas al procesului de sortare se citesc toate liniile


textului de la intrare. n al doilea pas se sorteaz liniile n ordine
lexicografic. n ultimul pas se afieaz 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:
int main() {
/* program care sorteaz liniile de la intrare */
#define NLIN 100
/* nr maxim de linii de sortat */
char *linptr[NLIN];
/* pointeri la linii */
int nrl;
/* nr linii intrare citite */
nrl = ReadLines(linptr,NLIN);
if (nrl>=0) {
Sort(linptr,nrl);
WriteLines(linptr,nrl);
}
else puts("Intrare prea mare pentru sort");
return 0;
}

Cele 3 funcii care realizeaz ntregul proces snt: ReadLines,


Sort i WriteLines.
Rutina de intrare ReadLines 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
afiare. 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 ReadLines care citete liniile textului de la
intrare este urmtoarea:
int ReadLines(char *linptr[], int nmax) {
int nrl = 0;
char *p,lin[80];
while (gets(lin)) {
if (nrl>=nmax) return -1;
p = strdup(lin);
___________________________________________________________________________________________________________________________

88

if (!p) return -1;


linptr[nrl++] = p;
}
return nrl;
}

Rutina care afieaz liniile n noua lor ordine este


WriteLines i are urmtorul cod:
void WriteLines(char *linptr[], int nrl) {
int i;
for (i=0; i<nrl; i++)
puts(linptr[i]);
}

Declaraia nou care apare n aceste programe este:


char *linptr[NLIN];

care indic faptul c linptr este un masiv de NLIN elemente,


fiecare element al masivului fiind un pointer la un caracter. Astfel
linptr[i] este un pointer la un caracter, iar *linptr[i]
permite accesul la caracterul respectiv.
Deoarece linptr este el nsui un masiv, care se transmite ca
argument funciei WriteLines, el va fi tratat ca un pointer i
atunci funcia WriteLines mai poate fi scris i astfel:
void WriteLines(char *linptr[], int nrl) {
while (--nrl>=0)
puts(*linptr++);
}

n funcia puts, linptr indic iniial prima linie de afiat;


fiecare incrementare avanseaz pe *linptr la urmtoarea linie de
afiat, n timp ce nrl se micoreaz dup fiecare afiare a unei linii
cu 1.
Funcia care realizeaz sortarea efectiv a liniilor se bazeaz pe
algoritmul de njumtire i are urmtorul cod:
void Sort(char *v[], int n) {
int gap,i,j;
char *tmp;
for (gap=n/2; gap>0; gap/=2)
___________________________________________________________________________________________________________________________

89

for (i=gap; i<n; i++)


for (j=i-gap; j>=0; j-=gap) {
if (strcmp(v[j],v[j+gap])<=0)
break;
tmp = v[j];
v[j] = v[j+gap];
v[j+gap] = tmp;
}
}

Deoarece fiecare element al masivului v (care este de fapt


masivul linptr din funcia main) este un pointer la primul
caracter al unei linii, variabila tmp 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.
Structura programului este urmtoarea:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int ReadLines(. . .) { . . . }
void WriteLines(. . .) { . . . }
void Sort(. . .) { . . . }
int main(. . .) { . . . }

S reinem 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 multidimensionale. 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
___________________________________________________________________________________________________________________________

90

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 int.
S considerm, de exemplu, masivul:
int X[3][5];
X este un masiv de ntregi, de rangul 35. Cnd X apare ntr-o
expresie, el este convertit ntr-un pointer la (primul din cele trei)
masive de 5 ntregi.
___________________________________________

|______________|______________|______________|

|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|
X[0]

X[1]

X[2]

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.

8.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,
___________________________________________________________________________________________________________________________

91

restul elementelor neiniializate se iniializeaz cu zero. Nu se admite


iniializarea unui masiv de clas automatic.
Acoladele { i } se pot omite n urmtoarele situaii:
dac iniializatorul ncepe cu acolad stnga {, atunci lista de
iniializatori, separai prin virgul, va iniializa elementele masivului;
nu se accept s existe mai muli iniializatori dect numrul
elementelor masivului;
dac ns iniializatorul nu ncepe cu acolad stnga {, 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) int x[] = {1,3,5};
Aceast declaraie definete i iniializeaz pe x ca un masiv
unidimensional cu trei elemente, chiar dac nu s-a specificat
dimensiunea masivului. Prezena iniializatorilor nchii ntre acolade
determin dimensiunea masivului.
2) Declaraia
int 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:
___________________________________________________________________________________________________________________________

92

int 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].
4) Declaraia:
int 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:
static char msg[] = "Eroare de sintaxa";

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 NumeLuna 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 *NumeLuna(int n) { /* returneaz numele lunii a n-a */
static char *NL[] = {
"luna ilegala", "ianuarie", "februarie",
"martie", "aprilie", "mai", "iunie", "iulie",
"august", "septembrie", "octombrie",
"noiembrie", "decembrie"
}
return (n<1) || (n>12) ? NL[0] : NL[n] ;
}

n acest exemplu, NL 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
___________________________________________________________________________________________________________________________

93

genereaz cte un pointer la fiecare din ele pe care apoi i introduce n


masivul name. Deci NL[i] va conine un pointer la irul de
caractere avnd indice i al iniializatorului. Nu este necesar s se
specifice dimensiunea masivului NL deoarece compilatorul o
determin numrnd iniializatorii furnizai i o completeaz n
declaraia masivului.

8.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:
int a[10][10],*b[10];

n acest caz 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 de pointeri poate fi variabil. Acest lucru nseamn c un
element al masivului de pointeri b poate indica un masiv de zece
___________________________________________________________________________________________________________________________

94

elemente, altul un masiv de dou elemente i altul poate s nu indice


nici un masiv.
Cu toate c problema prezentat n acest paragraf a fost descris
n termenii ntregilor, ea este cel mai frecvent utilizat n memorarea
irurilor de caractere de lungimi diferite (ca n funcia NumeLuna
prezentat mai sus).
Prezentm n continuare un model de rezolvare a alocrii de
memorie pentru o matrice de dimensiuni care snt cunoscute abia n
momentul execuiei. n biblioteca limbajului C exist funcii care ne
ofer posibilitatea de a aloca memorie necesar pentru memorarea
unor date. Dup ce datele au fost prelucrate, memoria ocupat nu mai
este necesar i poate fi eliberat.
Declaraiile acestor funcii se obin cu:
#include <stdlib.h>

Pentru alocarea unei zone de memorie apelm funcia malloc:


void *malloc(unsigned n);
Funcia aloc o zon de memorie de n octei i returneaz adresa
acesteia.
O zon de memorie avnd adresa p (p fiind un pointer) poate fi
eliberat dup ce nu mai este necesar:
void free(void *p);
Presupunem c trebuie s alocm memorie pentru o matrice de
elemente ntregi avnd n linii i m coloane.
#include <stdlib.h>
int **IntMatr(int n, int m) {
int i,**p;
p = (int **)malloc(n*sizeof(int *));
p[0] = (int *)malloc(nm*sizeof(int));
for (i=1; i<n; i++)
p[i] = p[i-1] + m;
return p;
}

Iniial se aloc memorie unde se vor depune adresele celor n


linii, de aceea primul apel malloc cere o zon de memorie pentru n
pointeri. n continuare trebuie alocat memorie i pentru matricea
___________________________________________________________________________________________________________________________

95

propriu-zis. Pentru simplificare am preferat s alocm o zon


continu de nm ntregi (al doilea apel malloc).
n acest moment putem determina adresa fiecrei linii a matricei.
Prima linie are determinat adresa din momentul alocrii. Pentru o
linie oarecare i (i 0) adresa acesteia se obine din adresa liniei
precedente la care se adaug m (numrul de elemente ale liniei); a se
vedea i figura de mai jos.
___

________________________________

|__||__|__|__|__|__|__|__|__|__|
|__||
|
|__||
Este foarte important s se respecte aceti pai n alocarea
memoriei pentru matrice. n caz contrar variabilele pointer conin
valori necunoscute i nu exist garanii c indic spre zone de
memorie sigure: este foarte probabil c programul nu are acces la
zonele respective.
Funcia principal va fi scris astfel:
int main() {
int **Mat,n,m;
/* alte definiii de variabile */
/* Citete valorile n i m */
Mat = IntMatr(n,m);
/* Completeaz matricea cu valori Mat[i][j] ... */
/* Prelucreaz matricea */
free(Mat[0]); free(Mat);
/* Elibereaz memoria */
}

Este foarte important s se respecte i paii precizai pentru


eliberarea memoriei ocupate: mai nti zona ocupat de matrice i
apoi i zona ocupat de pointeri. Dac se elibereaz mai nti zona
ocupat de pointeri, programul nu mai are acces la zona respectiv, i
un apel free(Mat[0]) va fi respins de sistemul de operare.
S relum problema alocrii de memorie pentru o matrice n
cazul general, unde cunoatem:
n: numrul liniilor matricei;
___________________________________________________________________________________________________________________________

96

M[]: masiv care indic numrul elementelor din fiecare linie;


s: mrimea n octei a unui element al matricei (se obine cu
operatorul sizeof); toate elementele matricei snt de acelai tip.
void **Genmatr(int n, int M[], int s) {
int i;
void **p;
p = (void **)malloc(n*sizeof(void *));
for (i=0; i<n; i++)
p[i] = malloc(M[i]*s);
return p;
}

Spre deosebire de exemplul precedent, unde toat memoria a fost


alocat ntr-o singur operaie (al doilea apel malloc), aici am
alocat separat memorie pentru fiecare linie.
Apelul acestei funcii necesit anumite pregtiri:
double **Dm;
/* pentru o matrice cu elemente de tip double */
int n,*M;
/* alte definiii de variabile */
/* Citete valoarea n */
M = (int *)malloc(n*sizeof(int));
/* precizeaz pentru fiecare linie numrul de elemente */
Dm = (double **)Genmatr(n,M,sizeof(double));
/* Operaii cu elementele matricei */

Memoria ocupat se elibereaz astfel:


for (i=0; i<n; i++)
free(Dm[i]);
free(Dm); free(M);

Este posibil s alocm o zon compact pentru memorarea


matricei, trebuie mai nti s determinm numrul total de elemente.
A doua variant a funciei GenMatr este:
void **Genmatr(int n, int M[], int s) {
int i,l;
char **p;
p = (char **)malloc(n*sizeof(void *));
for (l=i=0; i<n; i++)
l += M[i];
p[0] = (char *)malloc(l*s);

___________________________________________________________________________________________________________________________

97

for (i=1; i<n; i++)


p[i] = p[i-1] + M[i] * s;
return p;
}

Memoria ocupat de matrice se elibereaz cu dou apeluri free,


aa cum am artat mai sus n cazul matricei de ntregi.
Considerm c acest stil de programare, bazat pe alocarea
compact, este mai avantajos deoarece se evit o fragmentare
excesiv a memoriei. Sistemul de operare memoreaz informaii
suplimentare (ascunse programatorului) care i permit ulterior s
elibereze o zon de memorie la cerere.

8.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 main
este apelat, apelul va conine dou argumente.
n literatura de specialitate declaraia funciei main este:
int main(int argc, char *argv[]);

Pentru a obine descrieri compacte ale programelor am preferat


s folosim numele ac i av pentru argumentele funciei main.
Primul argument reprezint numrul de argumente din linia de
comand care a lansat programul. Al doilea argument 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 pri care dorim s afieze la terminal
argumentele lui luate din linia de comand, afiarea fcndu-se pe o
linie, iar argumentele afiate s fie separate prin spaii.
Comanda:
pri succes colegi

va avea ca rezultat afiarea la terminal a textului


succes colegi
___________________________________________________________________________________________________________________________

98

Prin convenie, av[0] este un pointer la numele pri al


programului apelat, astfel c ac, care specific numrul de
argumente din linia de comand, este cel puin 1.
n exemplul nostru, ac este 3, iar av[0], av[1] i av[2] snt
pointeri la pri, succes i respectiv colegi. Primul argument
efectiv este av[1] iar ultimul este av[ac-1]. Dac ac este 1,
nseamn c linia de comand nu are nici un argument dup numele
programului.
Atunci programul pri are urmtorul cod:
int main(int ac, char *av[]) {
/* afieaz argumentele */
int i;
for (i=1; i<ac; i++)
printf("%s%%c",av[i],(i<ac-1)?' ':'\n');
return 0;
}

Deoarece av este un pointer la un masiv de pointeri, exist mai


multe posibiliti de a scrie acest program. S mai scriem o versiune
a acestui program.
int main(int ac, char *av[]) {
/* versiunea a doua */
while (--ac>0)
printf("%s%c",*++av,(av>1) ? ' ' : '\n');
return 0;
}

Deoarece av este un pointer la un masiv de pointeri, prin


incrementare *++av va indica spre av[1] n loc de av[0]. Fiecare
incrementare succesiv poziioneaz pe av la urmtorul argument,
iar *av este pointerul la argumentul irului respectiv. n acelai timp
ac este decrementat pn devine zero, moment n care nu mai snt
argumente de afiat.
Aceast versiune arat c argumentul funciei printf poate fi o
expresie ca oricare alta.
Ca un al doilea exemplu, s reconsiderm un program anterior
care afieaz fiecare linie a unui text care conine un ir specificat de
caractere (ablon).
___________________________________________________________________________________________________________________________

99

Dorim acum ca acest ablon s poat fi precizat printr-un


argument n linia de comand la momentul execuiei.
i atunci funcia principal a programului care caut ablonul dat
de primul argument al liniei de comand este:
int main(int ac, char *av[ ]) {
char lin[80];
if (ac!=2)
puts("Linia de comanda eronata");
else
while (gets(lin))
if (index(lin,av[1])>=0)
puts(lin);
return 0;
}

unde linia de comand este de exemplu: find limbaj n care


find este numele programului, iar limbaj este ablonul cutat.
Rezultatul va fi afiarea tuturor liniilor textului de intrare care conin
cuvntul limbaj.
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 afieze toate liniile cu excepia
acelora care conin ablonul, i al doilea care s precead fiecare linie
afiat 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 caracter - s
introduc un parametru opional. Dac alegem, de exemplu, -x
pentru a indica cu excepia i -n pentru a cere numrarea liniilor,
atunci comanda:
find -x -n la

avnd intrarea:
la miezul stinselor lumini
s-ajung victorios,
la temelii, la rdcini,
la mduv, la os.
___________________________________________________________________________________________________________________________

100

va produce afiarea liniei a doua, precedat de numrul ei, deoarece


aceast linie nu conine ablonul la.
Argumentele opionale snt permise n orice ordine n linia de
comand. Analizarea i prelucrarea argumentelor unei linii de
comand trebuie efectuat n funcia principal main, 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:
find -xn la

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 ablonul la n liniile de la intrare i le
afieaz pe acelea, care nu conin ablonul, precedate de numrul lor
de linie. Programul trateaz corect att prima form a liniei de
comand ct i a doua. n continuare este prezentat doar funcia
principal a acestui program.
int main(int ac, char *av[]) {
/* caut un ablon */
char lin[80],*s;
long nl;
int exc,num,tst;
nl = num = 0;
while (--ac>0 && (*++av)[0]=='-')
for (s=av[0]+1; *s!=0; s++)
switch(*s) {
case 'x': exc = 1; break;
case 'n': num = 1; break;
default:
printf("find: optiune ilegala %c\n",*s);
ac = 0;
break;
}
if (ac!=1)
puts("Nu exista argumente sau sablon");
else
while (gets(lin) {
nl++;
___________________________________________________________________________________________________________________________

101

tst = index(lin,*av)>=0;
if (tst!=except) {
if (num)
printf("%4d:",nl);
puts(lin);
}
}

Dac nu exist erori n linia de comand, atunci la sfritul


primului ciclu while ac trebuie s fie 1, iar *av conine adresa
ablonului. *++av este un pointer la un ir argument, iar (*++av)
[0] este primul caracter al irului. n aceast ultim expresie
parantezele snt necesare deoarece fr ele expresia nseamn *++
(av[0]) ceea ce este cu totul altceva (i greit): al doilea caracter
din numele programului. O alternativ corect pentru (*++av)[0]
este **++av.

8.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:
int f();
g(f);

unde funcia f este un argument pentru funcia g. Definiia funciei g


va fi:
g(int (*funcpt)()) {
funcpt();
}

___________________________________________________________________________________________________________________________

102

n expresia g(f), f nu este un 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.
int (*funcpt)();

spune c funcpt este un pointer la o funcie care returneaz un


ntreg. Primul set de paranteze este necesar, deoarece fr el
int *funcpt();

ar nsemna c funcpt este o funcie care returneaz un pointer la un


ntreg, ceea ce este cu totul diferit fa de sensul primei expresii.
Folosirea lui funcpt n expresia:
funcpt();

indic apelul funciei a crei adres este dat de pointerul funcpt.


Ca un exemplu, s considerm procedura de sortare a liniilor de
la intrare, 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 de regul din trei componente: o comparare care
determin ordinea oricrei perechi de elemente, un schimb pentru a
inversa 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 astfel nct, transmind diferite funcii de comparare
funciei de sortare, elementele de intrare se pot aranja dup diferite
criterii.
Compararea lexicografic a dou linii se realizeaz prin funcia
strcmp. 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 strcmp.
Declarm aceste trei funcii n funcia principal main, iar
pointerii la aceste funcii i transmitem ca argumente funciei sort,
care la rndul ei va apela aceste funcii prin intermediul pointerilor
respectivi.
Funcia principal main va avea atunci urmtorul cod:
#define NLIN 100

/* numr maxim de linii de sortat */

___________________________________________________________________________________________________________________________

103

int main (int ac, char *av[]) {


char *linptr[NLIN]; /* pointeri la linii text */
int nrl;
/* numr de linii citite */
int num = 0;
/* 1 dac sort numeric */
if (ac>1 && av[1][0]=='-' && av[1][1]=='n')
num = 1;
nrl = ReadLines(linptr,NLIN);
if (nrl>=0) {
if (num) Sort(linptr,nrl,numcmp);
else Sort(linptr,nrl,strcmp);
WriteLines(linptr,nrl);
}
else puts("Intrare prea mare pentru Sort");
}

n apelul funciei Sort, argumentele strcmp i numcmp snt


adresele funciilor respective. Deoarece ele snt declarate funcii care
returneaz un ntreg (a se vedea mai jos structura programului),
operatorul & nu trebuie s precead numele funciilor, compilatorul
fiind cel care gestioneaz transmiterea adreselor funciilor.
S sintetizm ntr-o declaraie elementele comune ale celor dou
funcii:
typedef int (*P_func)(const char *, const char *);

Aceast declaraie definete identificatorul P_func ca fiind un tip


pointer la o funcie care returneaz un ntreg, i are ca argumente doi
pointeri la caracter. Elementele celor dou masive nu pot fi
modificate. Tipul P_func poate fi folosit n continuare pentru a
construi noi declaraii.
Funcia Sort care aranjeaz liniile n ordinea cresctoare
se va modifica astfel:
void Sort(char *v[], int n, P_func comp) {
int gap,i,j;
char *tmp;
for (gap=n/2; gap>0; gap/=2)
for (i=gap; i<n; i++)
for (j=i-gap; j>=0; j-=gap) {
if (comp(v[j],v[j+gap])<=0)
break;
___________________________________________________________________________________________________________________________

104

tmp = v[j];
v[j] = v[j+gap];
v[j+gap] = tmp;
}
}

Funcia numcmp este definit astfel:

int numcmp(const char *s1, const char *s2) {


double atof(char *s),v1,v2;
v1 = atof(s1);
v2 = atof(s2);
if (v1<v2) return -1;
if (v1>v2) return 1;
return 0;
}

Structura programului este urmtoarea:


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int ReadLines(. . .) {. . .}
void WriteLines(. . .) {. . .}
int numcmp(. . .) {. . .}
typedef int (*P_func)(const char *, const char *);
void Sort(. . .) {. . .}
int main(. . .) {. . .}

n funcia main, n locul variabilei num de tip ntreg putem


folosi un pointer de tip P_func astfel:
P_func Fcomp = strcmp;
if (ac>1 && av[1][0]=='-' && av[1][1]=='n')
Fcomp = numcmp;
nrl = ReadLines(linptr,NLIN);
if (nrl>=0) {
Sort(linptr,nrl,Fcomp);
WriteLines(linptr,nrl);
}

___________________________________________________________________________________________________________________________

105

Dac programul este compus din mai multe module, se utilizeaz


un fiier header care conine declaraiile tuturor funciilor utilizate.
Acest fiier trebuie inclus n fiecare modul.

___________________________________________________________________________________________________________________________

106

9. 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.
Structurile ajut la organizarea datelor complexe, n special n
programele mari, deoarece permit unui grup de variabile legate s fie
tratate ca o singur entitate. n acest capitol vom ilustra modul de
utilizare a structurilor.

9.1. Elemente de baz


Sintaxa unei declaraii de structur este:
struct nume-ablon-structur {
list-declaraii-membri
} list-variabile;
Numele ablonului structurii poate lipsi, n acest caz ablonul nu
va avea un nume, i lista variabilelor este obligatorie.
Lista variabilelor poate lipsi, n acest caz ablonul de tip structur
trebuie s aib un nume. Astfel se definete un ablon pentru
variabile care vor fi definite ulterior, avnd acest tip structurat:
struct nume-ablon-structur list-variabile;
Fiecare variabil din list va fi compus din aceleai elemente
(membri), aa cum a fost definit ablonul.
S revenim asupra rutinei de conversie a datei care a fost
prezentat n capitolul anterior. O dat calendaristic se compune din
zi, lun i an. Aceste trei variabile pot fi grupate ntr-o singur
structur astfel:
struct S_data {
int zi,luna,an;
} ;

Cuvntul cheie struct introduce o declaraie de structur care


este o list de declaraii nchise ntre acolade. Acest cuvnt poate fi
___________________________________________________________________________________________________________________________

107

urmat opional de un nume, numit marcaj de structur sau etichet de


structur, cum este n exemplul nostru numele S_data. Marcajul
denumete acest tip de structur i poate fi folosit n continuare ca o
prescurtare pentru declaraia de structur detaliat creia i este
asociat.
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.
Declaraia urmtoare definete variabila d ca o structur avnd
acelai ablon ca structura S_data:
struct S_data d;

O structur extern sau static poate fi iniializat, atand dup


definiia ei o list de iniializatori pentru componente, de exemplu:
struct S_data d = {4,7,1984};

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:
bis = !(d.an%4) && (d.an%100) || !(d.an%400);

Structurile pot fi imbricate: o nregistrare de stat de plat, de


exemplu, poate fi de urmtoarea form:
struct S_pers {
long nrl;
char num[LNUM],adr[LADR];
long sal;
struct S_data dnast,dang;
} ;

Tipul structur S_pers conine dou structuri de ablon


S_data. Declaraia urmtoare definete o variabil de tip structur
cu numele ang, avnd acelai ablon ca structura S_pers:
struct S_pers ang;
n acest caz:
ang.dnast.luna se refer la luna de natere
___________________________________________________________________________________________________________________________

108

ang.dang.an se refer la anul angajrii

Singurele operaii pe care le vom aplica unei structuri vor fi


accesul la un membru al structurii i considerarea adresei ei cu
ajutorul operatorului &. 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.
Structurile i funciile conlucreaz foarte eficient prin intermediul
pointerilor. Ca un exemplu, s rescriem funcia de conversie a datei,
care determin numrul zilei din an.
int NrZile(const struct S_data *pd) {
int i,zi,bis;
zi = pd->zi;
bis = !(pd->an%4) && (pd->an%100) || !(pd->an%400);
for (i=1; i<pd->luna; i++)
zi += Zile[bis][i];
return zi;
}

Declaraia din lista de argumente indic faptul c pd este un


pointer la o structur de ablonul S_data. Notaia pd->an indic
faptul c se refer la membrul an 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 an poate fi de
asemenea referit prin: (*pd).an, dar notaia -> se impune ca un
mod convenabil de prescurtare. n notaia (*pd).an, parantezele
snt necesare deoarece precedena operatorului . (membru de
structur) este mai mare dect cea a operatorului * (indirectare).
Operatorii -> i . ai structurilor, mpreun cu () (modificare
prioriti, liste de argumente) i [] (indexare) se gsesc n vrful
listei de preceden, fiind din acest punct de vedere foarte apropiai.
Astfel, fie declaraia urmtoare care definete un pointer p la o
structur:
___________________________________________________________________________________________________________________________

109

struct {
int x,*y;
} *p;

Semnificaiile urmtoarelor expresii snt:


++p->x
Incrementeaz membrul x, nu pointerul p, deoarece operatorul
-> are o preceden mai mare dect ++. Parantezele pot fi
folosite pentru a modifica ordinea operatorilor dat de
precedena.
(++p)->x
Incrementeaz mai nti pointerul p, apoi acceseaz membrul x,
din structura indicat de noua valoare a lui p.
(p++)->x
Acceseaz mai nti membrul x, apoi incrementeaz pointerul p.
*p->y
Indic coninutul de la adresa memorat la y.
*p->y++
Acceseaz mai nti ceea ce indic y, apoi incrementeaz pe y.
(*p->y)++
Incrementeaz ceea ce indic y.
*p++->y
Acceseaz mai nti ceea ce indic y, apoi incrementeaz
pointerul p.

9.2. typedef
Stilul de utilizare a tipurilor de date structurate prezentat mai sus
este foarte frecvent, i se ntlnete mai ales n programe care
apeleaz funcii ale sistemelor de operare Linux.
Considerm totui c a scrie declaraii de funcii care au ca i
parametri date structurate, sau a defini variabile de tip structur, este
destul de neplcut datorit cerinei de a preciza numele tipului
___________________________________________________________________________________________________________________________

110

precedat de cuvntul rezervat struct. De aceea, pe parcursul


lucrrii vom folosi o facilitate (typedef) de a defini sinonime
pentru tipuri de date existente, care pot fi fundamentale (predefinite)
sau definite de programator.
ntr-o declaraie typedef fiecare identificator care apare ca
parte a unui declarator devine sintactic echivalent cu cuvntul cheie
rezervat pentru tipul asociat cu identificatorul. De exemplu,
declaraia urmtoare l face pe LENGTH sinonim cu int:
typedef int LENGTH;

Tipul LENGTH poate fi folosit ulterior n declaraii n acelai


mod ca i tipul int:
LENGTH len,maxlen,*lgm[];

n mod similar, declaraia urmtoare l face pe STRING sinonim


cu char*, adic pointer la caracter:
typedef char *STRING;

Tipul STRING poate fi folosit n declaraii ulterioare:


STRING p,linptr[NLIN];

Observm c tipul care se declar prin typedef apare pe


poziia numelui de variabil, nu imediat dup cuvntul rezervat
typedef.
Relum definiia tipului P_func din capitolul precedent:

typedef int (*P_func)(const char *, const char *);

Aceast declaraie definete identificatorul P_func ca fiind un tip


pointer la o funcie care returneaz un ntreg, i are ca argumente doi
pointeri la caracter. Elementele celor dou masive nu pot fi
modificate. Tipul P_func poate fi folosit n continuare pentru a
construi noi declaraii.
Rescriem definiiile de tipuri din seciunea precedent:
typedef struct {
int zi,luna,an;
} T_data;

O structur extern sau static poate fi iniializat, atand dup


definiia ei o list de iniializatori pentru componente, de exemplu:
T_data d = {4,7,1984};
___________________________________________________________________________________________________________________________

111

Structurile pot fi imbricate: un tip de nregistrare de stat de plat,


de exemplu, poate fi definit astfel:
typedef struct {
long nrl;
char num[LNUM],adr[LADR];
long sal;
T_data dnast,dang;
} T_pers;

Funcia NrZile poate fi declarat astfel:


int NrZile(const T_data *pd);

Exist dou motive principale care impun folosirea declaraiilor


typedef. Primul este legat de problemele de portabilitate. Cnd se
folosesc declaraii typedef pentru tipuri de date care snt
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. La sfritul acestui capitol vom da un exemplu
n acest sens relund definirea tipului structurat dat calendaristic.
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.
n continuare pe parcursul lucrrii vom folosi facilitatea
typedef pentru a defini tipuri complexe de date i n special
structuri.

9.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 cuvintelor cheie i un masiv de ntregi pentru a contoriza
apariiile acestora. O posibilitate este de a folosi dou masive paralele
keyw i keyc declarate prin:
char *keyw[NKEYS];
int keyc[NKEYS];

respectiv unul de pointeri la iruri de caractere i cellalt de ntregi.


___________________________________________________________________________________________________________________________

112

Fiecrui cuvnt cheie i corespunde perechea (keyw,keyc) astfel


nct putem considera cele dou masive ca fiind un masiv de perechi.
Urmtoarea declaraie definete un tip structur T_key:
typedef struct {
char *keyw;
int keyc;
} T_key;

Folosim acest tip structur pentru a defini un masiv keym, unde


NKEYS este o constant definit cu ajutorul directivei #define:
T_key keym[NKEYS];

Fiecare element al masivului este o structur de acelai ablon ca


i structura T_key.
Deoarece masivul de structuri keym 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 analog cu iniializarea unui masiv n
sensul c definiia este urmat de o list de iniializatori nchii n
acolade.
Atunci masivul de structuri keym va fi iniializat astfel:
T_key keym[] = {
"break",0, "case",0, "char",0,
/* ... */ "while",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:
T_key keym[] = { {"break",0},{"case",0}, . . . }

dar parantezele interioare nu mai snt necesare dac iniializatorii snt


variabile simple sau iruri de caractere i dac toi iniializatorii snt
prezeni.
Dac masivul keym este global, eventualii membri pentru care
nu se precizeaz o valoare iniial vor primi valoarea zero, dar n
acest caz parantezele interioare snt necesare:
T_key keym[] = { {"break"},{"case"}, . . . }
___________________________________________________________________________________________________________________________

113

Compilatorul va determina, pe baza iniializatorilor, dimensiunea


masivului de structuri keym, motiv pentru care nu este necesar
indicarea dimensiunii masivului la iniializare.
Programul de numrare a cuvintelor cheie ncepe cu definirea
masivului de structuri keym. Funcia principal main citete cte un
cuvnt (ir de caractere) din textul de la intrarea standard prin apelul
repetat al funciei scanf. Fiecare ir este apoi cutat n tabelul
keym cu ajutorul unei funcii de cutare binary. Lista cuvintelor
cheie trebuie s fie n ordine cresctoare pentru ca funcia binary
s lucreze corect. Dac irul cercetat este un cuvnt cheie atunci
funcia binary returneaz numrul de ordine al acestuia n tabelul
cuvintelor cheie, altfel returneaz 1.
int binary(char *word, const T_key kt[], int n) {
int l,r,m,c;
l = 0; r = n - 1;
while (l<=r) {
m =(l + r) / 2;
c = strcmp(word,kt[mid].keyw);
if (cond==0) return m;
if (cond<0) r = m - 1;
else l = m + 1;
}
return -1;
}
int main() {
/* numr cuvintele cheie */
int n;
char word[36];
while (scanf("%s",word)!=EOF) {
n = binary(word,keym,NKEYS);
if (n>=0) keym[n].keyc++;
for (n=0; n<NKEYS; n++)
if (keym[n].keyc>0)
printf("%4d %s\n",keym[n].keyc,
keym[n].keyw);
return 0;
}
___________________________________________________________________________________________________________________________

114

Important! Funcia scanf cu descriptorul de format "%s"


furnizeaz n masivul word un ir de caractere care nu conine
spaii. Dac irul are cel puin 36 de caractere, comportamentul
programului este imprevizibil. Dac dorim s prelum cel mult 35 de
caractere folosim formatul "%35s". Citirea i scrierea cu format vor
fi detaliate n capitolul 10.
Valoarea NKEYS este numrul cuvintelor cheie din keym
(dimensiunea masivului de structuri). Dei putem calcula acest numr
manual, este mai simplu i mai sigur s-o facem prin program, mai
ales dac lista cuvintelor cheie este supus modificrilor. O
posibilitate de a calcula NKEYS prin program este de a termina lista
iniializatorilor cu un pointer NULL i apoi prin ciclare pe keym s
detectm sfritul lui. Acest lucru nu este necesar deoarece
dimensiunea masivului de structuri este perfect determinat n
momentul compilrii. Numrul de intrri se determin mprind
dimensiunea masivului la dimensiunea structurii key.
Operatorul sizeof furnizeaz dimensiunea n octei a
argumentului su.
n cazul nostru, numrul cuvintelor cheie este dimensiunea
masivului keym mprit la dimensiunea unui element de masiv.
Acest calcul este fcut ntr-o linie #define pentru a da o valoare
identificatorului NKEYS:
#define NKEYS (sizeof(keym)/sizeof(T_key))

Structura programului este urmtoarea:


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct { . . . } T_key;
T_key keym[] = { . . . } ;
int binary(. . .) {. . .}
int main(. . .) {. . .}

___________________________________________________________________________________________________________________________

115

9.4. Pointeri la structuri


Pentru a ilustra modul de corelare dintre pointeri i masivele de
structuri, s rescriem programul de numrare a cuvintelor cheie dintrun text, de data aceasta folosind pointeri n loc de indici de masiv.
Declaraia extern a masivului de structuri keym nu necesit
modificri, n timp ce funciile main i binary da. Prezentm n
continuare aceste funcii modificate.
T_key *binary(char *word, T_key kt[],int n) {
int c;
struct key *m;
struct key *l = kt;
struct key *r = kt + n - 1;
l = kt; r = kt + n - 1;
while (l<=r) {
m = l + (r - l) / 2;
c = strcmp(word,m->keyw);
if (c==0) return m;
if (c>0) l = m + 1;
else r = m - 1;
}
return NULL;
}
int main() {
/* numr cuvintele cheie, versiune cu pointeri */
int t;
char word[36];
T_key *p;
while (scanf("%s",word)!=EOF)
if (p=binary(word,keym,NKEYS))
p->keyc++;
for (p=keym; p<keym+NKEYS; p++)
if (p->keyc>0)
printf("%4d %s\n",p->keyc,p->keyw);
}

S observm cteva lucruri importante n acest exemplu. n


primul rnd, declaraia funciei binary trebuie s indice c ea
returneaz un pointer la o structur de acelai ablon cu structura
___________________________________________________________________________________________________________________________

116

T_key, n loc de un ntreg. Dac binary gsete un cuvnt n


structura T_key, atunci returneaz un pointer la el; dac nu-1
gsete returneaz NULL. n funcie de aceste dou valori returnate,
funcia main semnaleaz gsirea cuvntului prin incrementarea
cmpului keyc corespunztor cuvntului sau trece direct la citirea
urmtorului cuvnt.
n al doilea rnd, toate operaiile de acces la elementele masivului
de structuri keym se fac prin intermediul pointerilor. Acest lucru
determin o modificare semnificativ n funcia binary. Calculul
elementului mijlociu nu se mai poate face simplu prin:
m = (l + r) / 2

deoarece adunarea a doi pointeri este o operaie ilegal, nedefinit.


Aceast instruciune trebuie modificat n:
m = l + (r - l) / 2

care face ca adresa memorat la m s indice elementul de la jumtatea


distanei dintre l i r.
S mai observm iniializarea pointerilor l i r, care este perfect
legal, deoarece este posibil iniializarea unui pointer cu o adres a
unui element deja definit.
n funcia main avem urmtorul ciclu:
for(p=keym; p<keym+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 structurii. Nu ntotdeauna
dimensiunea structurii este egal cu suma dimensiunilor membrilor
ei, deoarece din cerine de aliniere a unor membri pot aprea goluri
n interiorul acesteia.
De aceea, dac dorim s economisim ct mai mult memorie n
cazul masivelor de structuri, recomandm gruparea membrilor astfel
nct s eliminm pe ct posibil apariia unor astfel de goluri. n
principiu, fiecare tip de date are anumite cerine de aliniere:
short la multiplu de 2;
long, float, double, long double la multiplu de 4.
___________________________________________________________________________________________________________________________

117

Dimensiunea unei structuri este rotunjit la un multiplu ntreg


corespunztor acestor cerine.

9.5. Structuri auto-referite


O alt problem legat de definirea i utilizarea structurilor este
cutarea n tabele. Cnd se ntlnete o linie de forma:
#define 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 va fi
nlocuit cu constanta 1.
Crearea i gestionarea tabelelor de simboluri este o problem de
baz n procesul de compilare. Deoarece nu tim dinainte cte
simboluri vor aprea n program, nu putem defini un masiv care s
memoreze aceste informaii: compilatorul trebuie s cunoasc
numrul de elemente n momentul compilrii. De aceea vom aloca
spaiu pentru fiecare simbol nou, i aceste zone vor fi legate ntre ele.
Definim dou funcii care gestioneaz simbolurile i textele lor
de substituie. Prima, Install(s,t) nregistreaz simbolul s i
textul de substituie t ntr-o tabel, s i t fiind iruri de caractere. A
doua, Lookup(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 (dispersie). 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 H care 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
___________________________________________________________________________________________________________________________

118

substituie i un pointer la urmtorul bloc din lan. Un pointer NULL


la urmtorul bloc din lan indic sfritul lanului.

| h0 || Info || Info || Info |


| h1 || Info || Info || Info || Info |
| h2 || Info || Info |
ablonul unei structuri (nod) este urmtorul:
typedef struct S_list {
char *num,*def;
struct S_list *nxt; / * urmtoarea intrare n lan */
} T_list, *P_list;

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 de mai sus definete un tip structurat cu numele
T_list, sinonim cu tipul struct S_list. Tipul P_list este
sinonim cu T_list * i cu struct S_list *.
Masivul de pointeri care indic nceputurile lanului de blocuri ce
descriu simboluri de acelai cod hash este:
#define NHASH 0x100
static T_list *hasht[NHASH];
Algoritmul de hashing pe care-l prezentm nu este cel mai bun
posibil, dar are meritul de a fi extrem de simplu:
int hashf(char *s) { /* formeaz valoarea hash pentru irul s */
int hv;
for (hv=0; *s;) hv += *s++;
return hv % NHASH;
}

Acesta produce un indice n masivul de pointeri hasht. n


procesul de cutare a unui simbol, dac acesta exist trebuie s se
afle n lanul de blocuri care ncepe la adresa coninut de elementul
din hasht cu indicele respectiv.
___________________________________________________________________________________________________________________________

119

Cutarea n tabela de simboluri hasht se realizeaz cu funcia


Lookup. Dac simbolul cutat este prezent undeva n lan, funcia
returneaz un pointer la el, altfel returneaz NULL.
T_list *Lookup(char *s) {
/* caut irul s n hasht */
T_list *np;
for (np=hasht[hashf(s)]; np; np=np->nxt)
if (strcmp(s,np->num)==0)
return np;
/* s-a gsit s */
return NULL;
/* nu s-a gsit s */
}

Dac dorim s eliminm un nod din list, avem nevoie s tim


unde este memorat adresa acestuia. Dac nodul nedorit este primul
din list, adresa acestuia este memorat ntr-un element din masivul
hast, altfel aceast adres este memorat n membrul nxt al
nodului precedent din list. Variabila pointer pp va reine unde se
afl memorat adresa care ne intereseaz. n parcurgerea listei,
valoarea variabilei pp este tot timpul cu un pas n urma lui np, care
indic nodul curent.
P_list *pp;
T_list *Lookup(char *s) {
T_list *np;
pp = hasht + hashf(s);
while (np=*pp) {
if (strcmp(s,np->num)==0)
return np;
pp = &np->nxt;
}
return pp = NULL;
}

/* caut irul s n hasht */

Fiecare nod din list are tipul T_list i conine, printre altele,
i adresa urmtorului nod. Dar adresa primului nod e memorat ntrun element care are alt tip dect T_list. De aceea avem nevoie s
memorm adresa zonei unde este memorat adresa care ne
intereseaz, de unde necesitatea definiiei globale
P_list *pp;

Aceast definiie este echivalent cu


T_list **pp;
___________________________________________________________________________________________________________________________

120

Dac i elementele masivului ar avea tipul T_list, nu am avea


nevoie de dou nivele de indirectare, ar fi suficient s memorm
adresa nodului precedent ntr-o variabil pointer la T_list.
Rutina Install folosete funcia Lookup 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
Install returneaz NULL dac din anumite motive nu exist
suficient spaiu pentru crearea unui bloc nou.
T_list *Install(char *num, char *def) {
/* scrie (num, def) n htab */
T_list *np;
int hv;
np = Lookup(num);
if (np==NULL) {
/* nu s-a gsit */
np = (T_list*)calloc(1,sizeof(*np));
if (np==NULL) return NULL;
/* nu exist spaiu */
np->num = strdup(num);
if (np==NULL) return NULL;
hv = hashf(np->num);
np->nxt = hasht[hv];
hasht[hv] = np;
}
else
/* nodul exist deja */
free(np->def); /* elibereaz definiia veche */
np->def = strdup(def);
if (np->def==NULL) return NULL;
return np;
}

Rutina Remove elimin din list ultimul element consultat.


void Remove() {
T_list *np;
if (!pp) return;
free(np=(*pp)->nxt);
*pp = np; pp = NULL;
}
___________________________________________________________________________________________________________________________

121

Cum eliberm toate zonele ocupate de o list? Trebuie s fim


foarte ateni deoarece, nainte de a elibera memoria ocupat de nodul
curent, trebuie eliberat memoria ocupat de nodul care urmeaz
celui curent. Prezentm mai nti varianta recursiv:
void FreeList(T_list *np) {
if (np) {
FreeList(np->nxt);
free(np);
}
}

Definiia recursiv a funciei FreeList consum destul de


mult memorie din stiv, de aceea redm n continuare i varianta
nerecursiv, mai eficient.
void FreeList(T_list *np) {
T_list *nx;
while (np) {
nx = np->nxt;
free(np);
np = nx;
}
}

Funcia main, care utilizeaz aceste rutine, nu are nevoie s


iniializeze elementele masivului hasht cu NULL, deoarece acesta
este declarat global i este automat iniializat corespunztor. De la
intrarea standard se ateapt introducerea unor comenzi:
+ nume valoare
nume
? nume
Comanda insereaz n list simbolul nume cu valoarea dat.
Dac numele introdus nu exist n listele hasht atunci se afieaz
un mesaj corespunztor, altfel se afieaz vechea valoare care este
apoi nlocuit de noua valoare.
Comanda ? afieaz valoarea simbolului nume, dac acesta
exist n listele hasht.
Comanda elimin simbolul nume, dac acesta exist n liste.
___________________________________________________________________________________________________________________________

122

Dup epuizarea datelor introduse memoria ocupat de cele


NHASH liste este eliberat.
int main() {
char num[36],def[36],op;
T_list *np;
int i;
while (scanf(" %c",&op)!=EOF)
switch (op) {
case '+':
scanf("%s %s",num,def);
np = Lookup(num);
if (!np)
puts("New name\n");
else
printf("Old value: %s\n",np->def);
Install(num,def);
break;
case '-': case '?':
scanf("%s",num);
np = Lookup(num);
if (!np)
puts("Not in list");
else
switch (op) {
case '-':
Remove(); puts("Removed");
break;
case '?':
printf("Value: %s\n",np->def);
break;
}
}
for (i=0; i<NHASH; i++)
FreeList(hasht[i]);
return 0;
}

___________________________________________________________________________________________________________________________

123

9.6. Arbori binari de cutare


S considerm problema numrului apariiilor tuturor cuvintelor
dintr-un text de intrare. Deoarece lista cuvintelor nu este cunoscut
dinainte, n-o putem sorta folosind algoritmul prezentat la sfritul
capitolului precedent, altfel contorizarea ar fi fost foarte simpl
pentru o list deja ordonat. Nu putem utiliza nici liste nlnuite
pentru a vedea dac un cuvnt citit exist sau nu n liste, pentru c
timpul de execuie al programului 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:
_____
| car |
/

| bac |

| lac |
/

| cer |
Fiecare nod al arborelui va reprezenta un cuvnt distinct din
intrare i va conine urmtoarea informaie:
un pointer la un ir de caractere;
un contor pentru numrul de apariii;
un pointer la descendentul stng al nodului;
un pointer la descendentul drept al nodului; 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, subarborele stng al su conine numai cuvintele care snt mai mici dect
cuvntul din nod, iar sub-arborele drept conine numai cuvintele care
snt mai mari dect cuvntul din nod, compararea fcndu-se din punct
de vedere lexicografic.
___________________________________________________________________________________________________________________________

124

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.
Prin urmare se impune de la sine ca rutinele de inserare n arbore
i de afiare s fie recursive.
Revenind la descrierea unui nod, el apare ca fiind o structur cu
patru componente:
typedef struct S_node {
char *w;
int c;
struct S_node *l;
struct S_node *r;
} T_node,*P_node;

/* nodul de baz */
/* pointer la cuvnt */
/* numrtor de apariii */
/* descendent stng (left) */
/* descendent drept (right) */

Declaraia de mai sus definete un tip structurat cu numele


T_node, sinonim cu tipul struct S_node. Tipul P_node este
sinonim cu T_node * i cu struct S_node *.
Funcia principal main citete cte un cuvnt din textul de la
intrarea standard, i l plaseaz n arbore prin rutina TreeInsert.
int main() {
/* contorizare apariii cuvinte */
char word[36];
P_node root;
root = NULL;
while (scanf("%s",word)!=EOF)
TreeInsert(&root,word);
TreePrint(root);
TreeFree(root);
}
___________________________________________________________________________________________________________________________

125

Funcia TreeInsert 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 TreeInsert. 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 TreeInsert l leag
nodul al crui descendent este noul nod, n cmpul l sau r, dup cum
noul cuvnt este mai mic sau mai mare fa de cuvntul origine.
Tocmai din acest motiv rutina TreeInsert primete adresa
variabilei pointer care va indica spre viitorul nod fiu.
void TreeInsert(P_node *p, char *w) {
int c;
if (*p) {
c = strcmp(w,(*p)->w);
if (c==0) (*p)->c++;
else
if (c<0)
/* noul cuvnt mai mic, mergem spre stnga */
TreeInsert(&(*p)->l,w);
else
/* noul cuvnt mai mare, mergem spre dreapta */
TreeInsert(&(*p)->r,w);
}
else {
*p = (P_node)calloc(1,sizeof(T_node));
(*p)->w = strdup(w);
(*p)->c = 1;
}

Am considerat c este mai comod s solicitm alocare de


memorie pentru un nod apelnd funcia calloc:
void calloc(unsigned ne, unsigned sz);

Argumentele acestei funcii snt ne: numrul de elemente pe care


le va avea zona, i sz: mrimea n octei a unui element, care se
poate obine cu operatorul sizeof. Coninutul zonei de memorie
___________________________________________________________________________________________________________________________

126

este pus la zero, spre deosebire de funcia malloc care pstreaz


neschimbat acest coninut (valori reziduale).
La adugarea unui nod nou, funcia calloc iniializeaz
pointerii spre cei doi descendeni cu NULL. Rmne s crem o copie
a irului dat cu funcia strdup, deja cunoscut, i s iniializm
contorul cu 1.
Rutina TreePrint afieaz arborele astfel nct pentru fiecare
nod se afieaz 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. Funcia
TreePrint este una din cele mai tipice rutine recursive.
void TreePrint(P_node p) {
if (p) {
Treeprint(p->l);
printf("%5d %s\n",p->c,p->w);
TreePrint(p->r);
}
}

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
TreeInsert, datorit recursivitii se parcurge acelai drum, de
data aceasta de la nodul gsit spre rdcina arborelui, refcndu-se
toi pointerii drumului parcurs.
O observaie legat de acest exemplu: dac arborele este
nebalansat, adic cuvintele nu sosesc n ordine aleatoare din punct
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.
Rutina TreeFree elibereaz memoria ocupat de nodurile
arborelui. Ordinea n care se face eliberarea este urmtoarea: mai nti
sub-arborele stng, apoi sub-arborele drept, i n final nodul curent. i
___________________________________________________________________________________________________________________________

127

aici eliberarea memoriei ocupate de nod trebuie s respecte o ordine:


mai nti memoria ocupat de irul de caractere, apoi memoria
ocupat de nodul propriu-zis.
void TreeFree(P_node p) {
if (p) {
TreeFree(p->l);
TreeFree(p->r);
free(p->w); free(p);

9.7. Cmpuri
Un cmp se definete ca fiind o mulime de bii consecutivi dintrun cuvnt sau ntreg. Concret, 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 n
interiorul caracterului sau cuvntului. De exemplu:
#define KEYWORD 01
#define EXTERNAL 02
#define STATIC 04

definesc mtile KEYWORD, EXTERNAL i STATIC care 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.

___________________________________________________________________________________________________________________________

128

Expresii de forma urmtoare apar frecvent i ele seteaz biii 1 i


2 din caracterul sau ntregul flags (n exemplul nostru):
flags | = EXTERNAL | STATIC;

Expresia urmtoare selecteaz biii 1 i 2 din flags:


flags &= EXTERNAL | STATIC;

Expresia urmtoare este adevrat cnd cel puin unul din biii 1
sau 2 din flags este unu:
if (flags & (EXTERNAL | STATIC)) . . .

Expresia urmtoare este adevrat cnd biii 1 i 2 din flags snt


ambii zero:
if (!(flags & (EXTERNAL | STATIC))) . . .

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 #define din exemplul de mai
sus pot fi nlocuite prin definirea a trei cmpuri:
struct {
unsigned is_keyword:1;
unsigned is_external:1;
unsigned is_static:1;
} flags;

Aceast construcie definete variabila flags care conine 3


cmpuri, fiecare de cte un bit. Numrul care urmeaz dup : (dou
puncte) reprezint lungimea cmpului n bii. Cmpurile snt declarate
unsigned pentru a sublinia c ele snt cantiti fr semn. Pentru a
ne referi la un cmp individual din variabila flags folosim o notaie
similar cu notaia folosit pentru membrii structurilor.
flags.is_keyword
flags.is_static

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:
flags.is_extern = flags.is_static = 1;

pentru setarea biilor 1 i 2 din variabila flags,


___________________________________________________________________________________________________________________________

129

flags.is_extern = flags.is_static = 0;

pentru tergerea biilor, iar:


if (flags.is_extern==0 && flags.is_static==0)

pentru testarea lor.


Un cmp nu trebuie s depeasc limitele unui cuvnt (16 sau 32
de bii, n funcie de sistemul de calcul). 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, de la poziiile cele mai
puin semnificative la cele mai semnificative.
Cmpurile nu pot constitui masive, nu au adrese, astfel nct
operatorul & nu se poate aplica asupra lor.

9.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.
S relum exemplul tabelei de simboluri a unui compilator,
presupunnd c se gestioneaz constante de tip int, float sau
iruri de caractere.
Valoarea unei constante particulare ar putea fi memorat ntr-o
variabil de tip corespunztor, dar este mai convenabil, pentru
gestiunea tabelei de simboluri, ca valoarea s fie memorat n aceeai
zon de memorie, indiferent de tipul ei. 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
___________________________________________________________________________________________________________________________

130

definiiei i accesului la o reuniune se bazeaz pe structuri. Fie


definiia:
union u_tag {
int ival;
float fval;
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
pointer-la-reuniune->membru
Dac variabila utype este utilizat pentru a ine evidena tipului
curent memorat n uval, atunci putem avea urmtorul cod:
switch (utype) {
case INT:
printf ("%d\n",uval.ival); break;
case FLOAT:
printf("%f\n",uval.fval); break;
case STRING:
printf("%s\n",uval.pval); break;
default:
printf("tip incorect %d in utype\n",utype);
break;
}

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. De exemplu,
n masivul de structuri symtab[NSYM] definit de:
___________________________________________________________________________________________________________________________

131

struct {
char *name;
int flags,utype;
union {
int ival;
float fval;
char *pval;
} uval;
} symtab[NSYM];

variabila ival se refer prin:


symtab[i].uval.ival

iar primul caracter al irului indicat de pval prin:


*symtab[i].uval.pval

Tehnic, 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. Pointerii la reuniuni pot fi folosii n
mod similar cu pointerii la structuri.
Urmtorul exemplu trebuie studiat cu atenie, deoarece
implementarea este dependent de arhitectura sistemului de calcul.
De aceea vom prezenta dou declaraii de tip, corespunztoare celor
dou moduri de reprezentare a valorilor ntregi:
pentru arhitecturi Big Endian:
typedef struct {
short an;
char luna, zi;
} T_struc;

pentru arhitecturi Little Endian:


typedef struct {
char luna, zi;
short an;
} T_struc;

n continuare putem defini un tip dat calendaristic:


___________________________________________________________________________________________________________________________

132

typedef union {
T_struc ds;
long dn;
} T_data;

Fie urmtoarele definiii de variabile:


T_data dc1, dc2;

n continuare putem interpreta coninutul variabilelor dc1 i dc2


fie ca structuri fie ca ntregi lungi.
O variabil de acest tip se iniializeaz astfel:
dc1.ds.an = . . .;
dc1.ds.luna = . . .;
dc1.ds.zi = . . .;

Dou date calendaristice se compar ct se poate de natural:


dc1.dn < dc2.dn

Cum putem depista automat ce fel de arhitectur are sistemul de


calcul?
union {
long l;
char c[4];
} tst = 0x12345678;
if (tst.c[0]==0x12) puts("Big Endian");
else puts("Little Endian");

___________________________________________________________________________________________________________________________

133

10. Operaii de intrare / ieire


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

10.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 (stdin);
fiierul standard de ieire (stdout);
fiierul standard de afiare a mesajelor (stderr).
Toate aceste trei fiiere snt secveniale i n momentul execuiei
unui program C snt implicit definite i deschise.
stdin i stdout 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 i nchiderea lor la
ncheierea execuiei programului. Redirectarea fiierului stdin se
specific prin construcia:
<specificator-fiier-intrare
n linia de comand prin care a fost lansat programul.
___________________________________________________________________________________________________________________________

134

Redirectarea fiierului stdout se specific prin construcia:


>specificator-fiier-ieire
n linia de comand prin care a fost lansat programul.
Redirectarea fiierului stdout pe un alt periferic, n scopul
efecturii unei operaii de adugare (append) se specific prin
construcia:
>>specificator-fiier-ieire
stderr este ntotdeauna asociat terminalului de la care a fost
lansat programul n execuie i nu poate fi redirectat.
Astfel c o linie de comand a unui program poate arta astfel:
Prog <intrare-standard >ieire-standard ali parametri
Prog < intrare-standard >> ieire-standard ali parametri
Fiecare din parametri poate lipsi; n lipsa unui specificator de
intrare sau ieire standard se folosete terminalul curent. Asocierea
unui fiier cu intrarea sau ieirea standard este fcut de sistemul de
operare, programul primind doar informaii despre ceilali parametri
din linia de comand.
Pentru a se putea face o referire la fiiere orice program C trebuie
s conin fiierul stdio.h, care se include printr-o linie de forma:
#include <stdio.h>

Pentru claritatea i lizibilitatea programelor scrise n C, ct i


pentru crearea unei imagini sugestive asupra lucrului cu fiiere, n
fiierul de definiii standard stdio.h s-a definit un nou nume de tip
de dat i anume FILE care este o structur. Pentru a referi un fiier,
este necesar o declaraie de forma:
FILE *fp;
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 FILE:
un identificator de fiier pe care sistemul de operare l asociaz
fluxului pe durata prelucrrii; acesta poate fi aflat cu ajutorul funciei
fileno;
adresele zonelor tampon asociate; poziia curent n aceste zone;
indicatorii de sfrit de fiier i de eroare;
alte informaii.
___________________________________________________________________________________________________________________________

135

10.2. Accesul la fiiere; deschidere i nchidere


Nume
fopen - deschide un flux
Declaraie
FILE *fopen(const char *num, const char *mod);

Descriere
Funcia fopen deschide fiierul al crui nume este un ir indicat
de num i i asociaz un flux.
Argumentul mod 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 mod 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
fi inefectiv cum ar fi fseek(flux, 0L, SEEK_CUR) apelat
cu scop de sincronizare.
___________________________________________________________________________________________________________________________

136

Valori returnate
n caz de succes se returneaz un pointer de tip FILE. n caz de
eroare se returneaz NULL i variabila global errno indic codul
erorii.

Nume
fclose - nchide un flux
Declaraie
int fclose( FILE *flux);

Descriere
Funcia fclose nchide fiierul asociat fluxului flux. Dac
flux a fost deschis pentru ieire, orice date aflate n zone tampon
snt scrise n fiier n prealabil cu un apel fflush.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global errno indic codul erorii.

Nume
tmpfile - creeaz un fiier temporar
Declaraie
FILE *tmpfile();

Descriere
Funcia tmpfile 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
fiierul nu poate fi deschis. n caz de eroare variabila global errno
indic codul erorii.
___________________________________________________________________________________________________________________________

137

Nume
fflush - foreaz scrierea n flux
Declaraie
int fflush(FILE *flux);

Descriere
Funcia fflush foreaz o scriere a tuturor datelor aflate n zone
tampon ale fluxului flux. Fluxul rmne deschis.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global errno indic codul erorii.

Nume
fseek, ftell, rewind - repoziioneaz un flux
Declaraie
int fseek(FILE *flux, long ofs, int reper);
long ftell(FILE *flux);
void rewind(FILE *flux);

Descriere
Funcia fseek seteaz indicatorul de poziie pentru fiierul
asociat fluxului flux. Noua poziie, dat n octei, se obine adunnd
ofs octei (offset) la poziia specificat de reper. Dac reper
este SEEK_SET, SEEK_CUR, sau SEEK_END, ofs este relativ la
nceputul fiierului, poziia curent a indicatorului, respectiv sfritul
fiierului. Funcia fseek terge indicatorul de sfrit de fiier.
Funcia ftell obine valoarea curent a indicatorului de poziie
pentru fiierul asociat fluxului flux.
Funcia rewind poziioneaz indicatorul de poziie pentru
fiierul asociat fluxului flux la nceputul fiierului. Este echivalent
cu:
(void)fseek(flux, 0L, SEEK_SET)

cu completarea c funcia rewind terge i indicatorul de eroare al


fluxului.
___________________________________________________________________________________________________________________________

138

Valori returnate
Funcia rewind nu returneaz nici o valoare. n caz de succes,
fseek returneaz 0, i ftell returneaz offset-ul curent. n caz de
eroare se returneaz EOF i variabila global errno indic codul
erorii.

Nume
fileno returneaz descriptorul asociat fluxului
Declaraie
int fileno(FILE *flux);

Descriere
Funcia fileno examineaz argumentul flux i returneaz
descriptorul asociat de sistemul de operare acestui flux.

10.3. Citire i scriere fr format


Nume
fgets - citete un ir de caractere dintr-un flux text
Declaraie
char *fgets(char *s, int size, FILE *flux);

Descriere
Funcia fgets citete cel mult size-1 caractere din flux 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 (fscanf, 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.
___________________________________________________________________________________________________________________________

139

Nume
fputs - scrie un ir de caractere ntr-un flux text
Declaraie
int fputs(const char *s, FILE *flux);

Descriere
Funcia fputs 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 (fprintf, 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
unsigned fread(void *ptr, unsigned size,
unsigned nel, FILE *flux);
unsigned fwrite(const void *ptr, unsigned
size, unsigned nel, FILE *flux);

Descriere
Funcia fread citete nel elemente, fiecare avnd mrimea
size octei, din fluxul indicat de flux, i le memoreaz n zona
indicat de ptr.
Funcia fwrite scrie nel elemente, fiecare avnd mrimea
size octei, din fluxul indicat de flux, pe care le ia din zona
indicat de ptr.
Valori returnate
Funciile returneaz numrul de elemente citite sau scrise cu
succes (i nu numrul de caractere). Dac apare o eroare sau se
ntlnete sfritul de fiier, valoarea returnat este mai mic dect
nel (posibil zero).
___________________________________________________________________________________________________________________________

140

10.4. Citire cu format


Nume
scanf, fscanf, sscanf - citire cu format
Declaraie
int scanf(const char *fmt, ...);
int fscanf(FILE *flux, const char *fmt, ...);
int sscanf(char *str, const char *fmt, ...);

Descriere
Funciile din familia ...scanf scaneaz intrarea n concordan cu
irul de caractere fmt 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 stdin, fscanf din flux, i sscanf din irul
indicat de str.
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).
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.

___________________________________________________________________________________________________________________________

141

Conversia este de tip d, i, o, u, x sau n i argumentul asociat


este un pointer la short (n loc de int).

Conversia este de tip d, i, o, u, x sau n i argumentul asociat


este un pointer la long (n loc de int), sau conversia este de tip
e, f, g i argumentul asociat este un pointer la double (n loc
de float).

Conversia este de tip e, f, g i argumentul asociat este un pointer


la long double.

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.

Potrivire cu un ntreg zecimal (eventual cu semn); argumentul


asociat trebuie s fie un pointer la int.

Potrivire cu un ntreg (eventual cu semn); argumentul asociat


trebuie s fie un pointer la int. Valoarea ntreag este citit n
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.

Potrivire cu un ntreg octal fr semn; argumentul asociat trebuie


s fie un pointer la unsigned.

Potrivire cu un ntreg zecimal fr semn; argumentul asociat


trebuie s fie un pointer la unsigned.

___________________________________________________________________________________________________________________________

142

Potrivire cu un ntreg hexazecimal fr semn; argumentul asociat


trebuie s fie un pointer la unsigned.

Potrivire cu un numr n virgul mobil (eventual cu semn);


argumentul asociat trebuie s fie un pointer la float.

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

Potrivire cu o secven de caractere de mrime w (dac aceasta


este specificat; prin lips se ia w1); 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
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

___________________________________________________________________________________________________________________________

143

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

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 o 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, i se
poziioneaz bitul corespunztor din structura FILE, care poate fi
testat.

10.5. Scriere cu format


Nume
printf, fprintf, sprintf - scriere cu format
Declaraie
int printf(const char *fmt, ...);
int fprintf(FILE *flux, const char *fmt, ...);
int sprintf(char *str, const char *fmt, ...);

Descriere
Funciile din familia ...printf genereaz o ieire n concordan cu
format dup cum se descrie mai jos. Funcia printf afieaz ieirea
la fluxul standard stdout; fprintf scrie ieirea la flux;
sprintf scrie ieirea n irul de caractere str.
___________________________________________________________________________________________________________________________

144

Aceste funcii genereaz ieirea sub controlul irului format care


specific cum se convertesc argumentele pentru ieire.
irul de formatare
irul fmt 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 ca tip 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.
Caractere indicatori
Caracterul % este urmat de zero, unul sau mai muli indicatori:
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.
___________________________________________________________________________________________________________________________

145

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.

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 corespondent, care trebuie s fie de
tip int. 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
corespondent, care trebuie s fie de tip int. Dac precizia este dat
doar de caracterul . 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.
Dac se folosete * pentru lime sau precizie (sau ambele),
argumentele se iau n ordine: lime, precizie, valoare de scris.
Modificator de lungime
n acest caz prin conversie ntreag nelegem conversie de tip d,
i, o, u, x, X.
___________________________________________________________________________________________________________________________

146

Conversia ntreag care urmeaz corespunde unui argument


short sau unsigned short, sau urmtoarea conversie de tip
n corespunde unui argument de tip pointer la short.

Conversia care urmeaz corespunde unui argument de tip:


long sau unsigned long, dac este o conversie de tip d, i,
o, u, x, X sau n;
double, dac este o conversie de tip f.

Urmtoarea conversie de tip e, E, f, F, g, G corespunde unui


argument long double.

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 int 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 unsigned 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
___________________________________________________________________________________________________________________________

147

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
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 int este convertit la unsigned char i se


scrie caracterul rezultat.

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, irul trebuie s conin un caracter terminator null.

Argumentul de tip pointer este scris n hexazecimal; formatul


este specific sistemului de calcul.

Numrul de caractere scrise pn n acest moment este memorat


la argumentul de tip int *. Nu se face nici o conversie.

___________________________________________________________________________________________________________________________

148

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

10.6. Tratarea erorilor


Nume
perror - afieaz un mesaj de eroare sistem
Declaraie
void perror(const char *s);
#include <errno.h>
const char *sys_errlist[];
int sys_nerr;

Descriere
Rutina perror 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 errno.
Lista global de erori sys_errlist[] indexat cu errno
poate fi folosit pentru a obine mesajul de eroare fr new-line.
Ultimul indice de mesaj din list este sys_nerr-1. Se recomand
o atenie deosebit n cazul accesului direct la list deoarece unele
coduri noi de eroare pot lipsi din sys_errlist[].
Dac un apel sistem eueaz variabila errno indic codul erorii.
Aceste valori pot fi gsite n <errno.h>. Funcia perror servete
la afiarea acestui cod de eroare ntr-o form lizibil. Dac un apel
terminat cu eroare nu este imediat urmat de un apel perror,
valoarea variabilei errno se poate pierde dac nu e salvat.
___________________________________________________________________________________________________________________________

149

Nume
clearerr, feof, ferror - verific i reseteaz starea
fluxului
Declaraie
void clearerr(FILE *flux);
int feof(FILE *flux);
int ferror(FILE *flux);

Descriere
Funcia clearerr terge indicatorii de sfrit de fiier i eroare
ai fluxului.
Funcia feof 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 ferror 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 feof i
ferror pentru a determina cauza terminrii.
Atenie! Este foarte frecvent folosirea incorect a funciei feof
pentru a testa dac s-a ajuns la sfritul fiierului. Nu se recomand n
nici un caz acest stil de programare:
#define LSIR 80
char lin[LSIR];
FILE *fi,*fo;
fi=fopen(nume-fiier-intrare,"rt");
fo=fopen(nume-fiier-ieire,"wt");
while (!feof(fi)) {
/* greit! */
fgets(lin,LSIR,fi);
fputs(lin,fo);
}
fclose(fi); fclose(fo);

___________________________________________________________________________________________________________________________

150

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
poziionat indicatorul de sfrit de fiier, deci funcia fgets nc
returneaz succes. La reluarea ciclului se ncearc un nou fgets i
abia acum se depisteaz sfritul de fiier, fapt marcat n zona
rezervat fluxului fi. Astfel coninutul irului lin rmne
nemodificat i este scris a doua oar n fiierul de ieire. Abia la o
nou reluare a ciclului funcia feof 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.

10.7. Operaii cu directoare


Funciile de parcurgere a cataloagelor de fiiere descrise n
aceast seciune (opendir, readdir, closedir) snt definite de
mai multe medii de programare C (printre care i GNU C pentru
sistemul de operare Linux), precum i de standardul POSIX. Aceste
funcii snt declarate n <dirent.h>.
Funciile de redenumire i tergere a unor fiiere snt descrise n
<stdio.h>.

Nume
opendir - deschide un director
Declaraie
DIR *opendir(const char *nume);

Descriere
Funcia opendir deschide un flux pentru directorul cu numele
nume, i returneaz un pointer la fluxul deschis. Fluxul este
poziionat pe prima intrare din director.
___________________________________________________________________________________________________________________________

151

Valoare returnat
Funcia returneaz un pointer la flux n caz de succes, sau NULL
n caz de eroare i variabila global errno indic codul erorii.
Cteva erori posibile
EACCES Acces interzis
ENOTDIR nume nu este un director

Nume
readdir - citete dintr-un director
Declaraie
struct dirent *readdir(DIR *dir);

Descriere
Funcia readdir returneaz un pointer la o structur de tip
dirent care reprezint urmtoarea intrare din directorul indicat de
fluxul dir. Returneaz NULL dac s-a depistat sfritul de director
sau dac a aprut o eroare.
Structura de tip dirent 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
int closedir(DIR *dir);

Descriere
Funcia closedir nchide fluxul dir.
Valoare returnat
Funcia returneaz 0 n caz de succes sau EOF n caz de eroare.
___________________________________________________________________________________________________________________________

152

Nume
rename - redenumete un fiier
remove - terge un fiier
Declaraie
int rename(const char *old, const char *new);
int remove(const char *name);

Descriere
Funcia rename schimb numele unui fiier din old n new.
Dac a fost precizat un periferic n new, acesta trebuie s coincid cu
cel din old. Directoarele din old i new pot s fie diferite, astfel c
rename poate fi folosit pentru a muta un fiier dintr-un director n
altul. Nu se permit specificatori generici (wildcards).
Funcia remove terge fiierul specificat prin name.
Valoare returnat
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global errno indic codul erorii.

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


#include <stdio.h>
FILE *f;
int main(int ac, char **av) {
if (ac!=2) {
fputs("Un argument!\n",stderr);
return 1;
}
f = fopen(av[1],"rb");
if (!f) {
perror("Eroare la deschidere");
___________________________________________________________________________________________________________________________

153

return 1;
}
fseek(f,0,SEEK_END);
fprintf(stderr,"File %s, size %ld\n",ftell(f));
fclose(f);
return 0;
}

2) Copierea unui fiier


Funciile fgets i fputs se folosesc pentru fluxuri deschise n
mod text. Cum se utilizeaz pentru copierea unui fiier text?
#include <stdio.h>
#define LSIR 80
char lin[LSIR];
FILE *fi, *fo;
int main(int ac, char **av) {
if (ac!=3) {
fputs("Doua argumente!\n",stderr);
}
fi=fopen(av[1],"rt"); fo=fopen(av[2],"wt");
if (!fi || !fo) {
perror("Eroare la deschidere");
return 1;
}
while (fgets(lin,LSIR,fi))
fputs(lin,fo);
fclose(fi); fclose(fo);
return 0;
}

Funciile fread i fwrite se folosesc pentru fluxuri deschise


n mod binar. Cum se utilizeaz pentru copierea unui fiier binar?
#include <stdio.h>
#define LZON 4096
char zon[LZON];
FILE *fi, *fo;
unsigned k;
int main(int ac, char **av) {
if (ac!=3) {
___________________________________________________________________________________________________________________________

154

fputs("Doua argumente!\n",stderr);
return 1;
}
fi=fopen(av[1],"rb"); fo=fopen(av[2],"wb");
if (!fi || !fo) {
perror("Eroare la deschidere");
return 1;
}
while (k=fread(zon,1,LZON,fi))
fwrite(zon,1,k,fo);
fclose(fi); fclose(fo);
return 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.
#include <stdio.h>
FILE *fi;
char num[10];
int a,b,c;
double m;
int main(int ac, char **av) {
if (ac!=2) {
fputs("Un argument!\n",stderr);
return 1;
}
fi=fopen(av[1],"rt");
if (!fi) {
perror("Eroare la deschidere");
return 1;
}
while (fscanf(fi,"%s %d %d %d",num,&a,&b,&c)!=EOF)
{
m=(a+b+c)/3.0;
printf("%-12s%6.2lf\n",num,m);
}
___________________________________________________________________________________________________________________________

155

fclose(fi);
return 0;
}

4) Afiarea coninutului unui director


#include <dirent.h>
#include <stdio.h>
DIR *dir;
struct dirent *ent;
int main(int ac, char **av) {
if (ac!=2) {
printf("Un parametru\n");
return 1;
}
dir = opendir(av[1]);
if (!dir) {
perror("Eroare open dir");
return 1;
}
while (ent=readdir(dir))
printf("%s\n",ent->d_name);
return 0;
}

___________________________________________________________________________________________________________________________

156

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

11.1. Alocarea dinamic a memoriei


Nume
calloc, malloc, realloc - aloc memoria n mod
dinamic
free - elibereaz memoria alocat n mod dinamic
Declaraie
#include <stdlib.h>
void *calloc(unsigned nel, unsigned size);
void *malloc(unsigned size);
void *realloc(void *ptr, unsigned size);
void free(void *ptr);

Descriere
Funcia calloc aloc memorie pentru un masiv de nel
elemente, fiecare de mrime size octei i returneaz un pointer la
memoria alocat. Coninutul memoriei este pus la zero.
Funcia malloc aloc size octei i returneaz un pointer la
memoria alocat. Coninutul memoriei nu este ters.
Funcia free elibereaz spaiul de memorie indicat de ptr, care
trebuie s fi fost returnat de un apel anterior malloc, calloc sau
realloc. n caz contrar, sau dac a existat deja un apel anterior
free(ptr), comportamentul programului este imprevizibil.
Funcia realloc modific mrimea blocului de memorie
indicat de ptr la size octei. Coninutul rmne neschimbat la
mrimea minim dintre mrimea veche i cea nou; noul spaiu de
___________________________________________________________________________________________________________________________

157

memorie care este eventual alocat este neiniializat. Dac ptr este
NULL apelul este echivalent cu malloc(size); dac size este
egal cu zero apelul este echivalent cu free(ptr). Cu excepia
cazului cnd ptr este NULL, acesta trebuie s fi fost returnat de un
apel precedent malloc, calloc sau realloc.
Valori returnate
Pentru calloc i malloc valoarea returnat este un pointer la
memoria alocat, aliniat n mod corespunztor pentru orice tip de
variabile, sau NULL dac nu exist suficient memorie continu.
Funcia free nu returneaz nimic.
Funcia realloc returneaz un pointer la noua zon de
memorie alocat, aliniat n mod corespunztor pentru orice tip de
variabile. Valoarea acestuia poate fi diferit de ptr, poate fi NULL
dac nu exist suficient memorie continu sau dac valoarea size
este egal cu 0. Dac realloc eueaz, blocul original rmne
neatins nu este nici eliberat nici mutat.

11.2. Sortare i cutare


Nume
qsort - sorteaz un masiv
bsearch - cutare binar ntr-un masiv sortat
Declaraie
#include <stdlib.h>
void qsort(void *base, unsigned nel,
unsigned size, int (*comp)
(const void *, const void *));
void *bsearch(const void *key, const void
*base, unsigned nel, unsigned size, int
(*comp)(const void *, const void *));

Descriere
Funcia qsort sorteaz un masiv de nel elemente, fiecare de
mrime size. Argumentul base indic spre nceputul masivului.
___________________________________________________________________________________________________________________________

158

Elementele masivului 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 masivul sortat este
nedefinit.
Funcia bsearch caut ntr-un masiv de nel elemente, fiecare
de mrime size, un membru care coincide cu obiectul indicat de
key. Argumentul base indic spre nceputul masivului.
Coninutul masivului 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 se definete ca n cazul qsort. Primul argument este
adresa cheii, al doilea este adresa unui element din masiv.
Valoare returnat
Funcia bsearch returneaz un pointer la un membru al
masivului 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.

11.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
Declaraie
#include <ctype.h>
int isalnum(int c);

int isalpha(int c);

___________________________________________________________________________________________________________________________

159

int
int
int
int
int

isascii(int
iscntrl(int
isdigit(int
isgraph(int
islower(int

c);
c);
c);
c);
c);

int
int
int
int
int

int tolower(int c);

isprint(int c);
ispunct(int c);
isspace(int c);
isupper(int c);
isxdigit(int c);

int toupper(int c);

Descriere
Primele 12 funcii verific dac c, care trebuie s fie o valoare de
tip unsigned char sau EOF, se afl n una din clasele de caractere
enumerate mai sus.
isalnum
Verific dac c este alfanumeric (liter sau cifr).
isalpha
Verific dac c este alfabetic (liter mare sau mic).
isascii
Verific dac c este o valoare pe 7 bii din setul de caractere
ASCII.
iscntrl
Verific dac c este un caracter de control.
isdigit
Verific dac c este o cifr (ntre 0 i 9).
isgraph
Verific dac c este un caracter afiabil cu excepia spaiului.
islower
Verific dac c este o liter mic.
isprint
Verific dac c este un caracter afiabil inclusiv spaiu.
ispunct
Verific dac c este un caracter diferit de spaiu i nonalfanumeric.
___________________________________________________________________________________________________________________________

160

isspace
Verific dac c este un spaiu alb.
isupper
Verific dac c este o liter mare.
isxdigit
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 AB C D E F
tolower
Convertete caracterul c, dac este o liter, la litera mic
corespunztoare.
toupper
Convertete caracterul c, dac este o liter, la litera mare
corespunztoare.
Valoare returnat
Valoarea returnat de funciile is... este nenul dac caracterul c
se afl n clasa testat, i zero n caz contrar.
Valoarea returnat de funciile to... este litera convertit dac
caracterul c este o liter, i nedefinit n caz contrar.

11.4. Operaii cu blocuri de memorie


Pentru majoritatea funciilor din aceast categorie compilatorul
expandeaz codul acestora folosind instruciuni pe iruri de caractere.
Aceste funcii snt declarate n <string.h>

Nume
memcpy - copiaz o zon de memorie
Declaraie
void *memcpy(void *dest, const void *src,
unsigned n);
void *memmove(void *dest, const void *src,
unsigned n);
___________________________________________________________________________________________________________________________

161

Descriere
Funcia memcpy copiaz n octei din zona de memorie src 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
int memcmp(const void *s1, const void *s2,
unsigned 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
void *memset(void *s, int c, unsigned 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.

___________________________________________________________________________________________________________________________

162

Nume
memchr - caut n memorie un caracter
Declaraie
void *memchr(const void *s, int c, unsigned 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 unsigned char).
Valoare returnat
Funcia returneaz un pointer la octetul gsit sau NULL dac
valoarea nu exist n zona de memorie.

11.5. Operaii cu iruri de caractere


Pentru majoritatea funciilor din aceast categorie compilatorul
expandeaz codul acestora folosind instruciuni pe iruri de caractere.
Aceste funcii snt declarate n <string.h>

Nume
strlen - calculeaz lungimea unui ir
Declaraie
unsigned strlen(const char *s);

Descriere
Funcia strlen 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 *strcpy(char *dest, const char *src);
___________________________________________________________________________________________________________________________

163

char *strncpy(char *dest, const char *src,


unsigned n);

Descriere
Funcia strcpy copiaz irul indicat de src (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 strncpy este similar, cu excepia faptului c nu se
copiaz mai mult de n octei din src. Astfel, dac caracterul
terminator null nu se afl n primii n octei din src, rezultatul nu va
fi terminat cu null. n cazul n care lungimea lui src 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 *strdup(const char *s);

Descriere
Funcia strdup returneaz un pointer la un nou ir care este un
duplicat al irului s. Memoria pentru noul ir se obine cu malloc,
i poate fi eliberat cu free.
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 *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src,
unsigned n);
___________________________________________________________________________________________________________________________

164

Descriere
Funcia strcat adaug irul src 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 strncat este similar, cu excepia faptului c numai
primele n caractere din src se adaug la dest.
Valoare returnat
Funciile returneaz un pointer la irul rezultat dest.

Nume
strcmp - compar dou iruri de caractere
Declaraie
int strcmp(const char *s1, const char *s2);

Descriere
Funcia strcmp 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.

Nume
strchr, strrchr - localizeaz un caracter
Declaraie
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);

Descriere
Funcia strchr returneaz un pointer la prima apariie a
caracterului c n irul s.
Funcia strrchr returneaz un pointer la ultima apariie a
caracterului c n irul s.
___________________________________________________________________________________________________________________________

165

Valoare returnat
Funciile returneaz un pointer la caracterul gsit sau NULL dac
valoarea nu a fost gsit.

Nume
strstr - localizeaz un subir
Declaraie
char *strstr(const char *sir, const char *subs);

Descriere
Funcia strstr gsete prima apariie a subirului subs n
irul sir. 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
unsigned strspn(const char *s, const char *acc);
unsigned strcspn(const char *s, const char *rej);

Descriere
Funcia strspn determin lungimea segmentului iniial din s
format n ntregime numai cu caractere din acc.
Funcia strcspn determin lungimea segmentului iniial din s
format n ntregime numai cu caractere care nu se gsesc n rej.
Valori returnate
Funcia strspn returneaz poziia primului caracter din s care
nu se afl n acc.
Funcia strcspn returneaz poziia primului caracter din s care
se afl n rej.

___________________________________________________________________________________________________________________________

166

11.6. Biblioteca matematic


1) Funciile din prima categorie snt declarate n <stdlib.h>

Nume
rand, srand - generarea numerelor pseudo-aleatoare
Declaraie
int rand(void);
void srand(unsigned int seed);

Descriere
Funcia rand 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 srand 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 srand se
apeleaz cu aceeai valoare seed.
Se obinuiete ca generatorul s fie iniializat cu o valoare dat
de ceasul sistemului de calcul, ca n exemplul de mai jos:
#include <time.h>
srand(time(NULL));

Valoare returnat
Funcia rand 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+(int)(10.0*rand()/(RAND_MAX+1.0));
i nu o secven de tipul
j=1+(int)(1000000.0*rand())%10;
care folosete biii de rang inferior.
___________________________________________________________________________________________________________________________

167

Tot n fiierul <stdlib.h> snt descrise i urmtoarele funcii:


valoare absolut
valoare absolut
conversie din ASCII n ntreg
conversie din ASCII n ntreg lung
conversie din ASCII n dubl precizie

int abs(int i);


long labs(long i);
int atoi(char *s);
long atol(char *s);
double atof(char *s);

Urmtoarele funcii de conversie necesit o discuie detaliat:


double strtod(char *str, char **end);

conversie din ASCII n dubl precizie


long strtol(char *str, char **end, int base);

conversie din ASCII n ntreg lung


unsigned long strtoul(char *str, char **end, int base);

conversie din ASCII n ntreg lung fr semn


irul de caractere str este convertit n reprezentare binar, ca i
n cazul funciilor atoi, atol, atof. Pentru conversii de tip ntreg
se precizeaz i baza de numeraie folosit, care poate fi ntre 2 i 36.
Pentru baze mai mari dect zece se folosesc litere mici sau mari. n
plus, dac parametrul end indic o adres valid (nu este null), la
ieire primete adresa primului caracter invalid din str, care nu
respect regulile de scriere a unei valori numerice de tipul ateptat.
2) Funciile din a doua categorie snt declarate n <math.h>
double
double
double
double
double
double
double
double
double
double
double

fabs(double x); valoare absolut


floor(double x); parte ntreag inferioar
ceil(double x); parte ntreag superioar
sqrt(double x);
x
sin(double x);
sin(x)
cos(double x);
cos(x)
tan(double x);
tg(x)
asin(double x); arcsin(x)
acos(double x); arccos(x)
atan(double x); arctg(x) n [-/2,/2]
atan2(double y, double x);

arctg(y/x) n [,]
___________________________________________________________________________________________________________________________

168

double
double
double
double
double
double
double
double

exp(double x); ex
log(double x); ln(x)
pow(double x, double y);
sinh(double x); sinh(x)
cosh(double x); cosh(x)
tanh(double x); tgh(x)
ldexp(double x, int e);
fmod(double x, double y);

xy

x 2e
x modulo y

Funcia fmod returneaz o valoare f definit astfel: x a y f


a este o valoare ntreag (dat de x/y) i 0 |f | < y; f are semnul lui
x.
Urmtoarele dou funcii returneaz dou valori: una este
valoarea returnat de funcie (de tip double), i cealalt returnat
prin intermediul unui argument de tip pointer la int respectiv
double.
double frexp(double x, int *e);

Funcia frexp desparte valoarea x n dou pri: o parte fracionar


normalizat (f [0.5,1)) i un exponent e. Dac x este 0 atunci f0 i
e0. Valoarea returnat este f.
double modf(double x, double *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.

11.7. Funcii pentru timp i dat calendaristic


Funciile din aceast categorie, mpreun cu tipurile i structurile
de date folosite, snt declarate n <time.h>

Funcia time
Data i ora calendaristic se reprezint n format compact printr-o
valoare de tip time_t. Majoritatea mediilor de programare definesc
___________________________________________________________________________________________________________________________

169

acest tip ca un sinonim pentru unsigned long. Aceast valoare se


obine prin apelul funciei time.

time_t time(time_t *tims);


Funcia returneaz data i ora curent n coordonate universale
(UTC), care reprezint numrul de secunde de la nceputul erei Unix
(1 ianuarie 1970, ora 0:00:00).
Valoarea returnat este memorat la adresa indicat de
argumentul tims, dac acesta nu este NULL. Dac argumentul
tims este NULL, trebuie s memorm valoarea returnat.

Structura tm (dat calendaristic defalcat)


Funcia gmtime
O valoare de tip time_t poate fi defalcat pe componente i
memorat ntr-o structur:

struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};

/* secunde, ntre 0 i 59 */
/* minute, ntre 0 i 59 */
/* ore, ntre 0 i 23 */
/* ziua din lun, ntre 1 i 31 */
/* luna, ntre 0 i 11 */
/* anul curent minus 1900 */
/* ziua din sptmn, duminica: 0 */
/* ziua din an, ntre 0 i 365 */
/* opiunea daylight saving time */

Observaie. Alegerea numelui tm pentru aceast structur a fost


ct se poate de neinspirat. Programatorii trebuie s fie deosebit de
ateni ca, atunci cnd folosesc funcii din aceast categorie, s nu
foloseasc n nici un caz numele tm pentru definiii proprii.
Funcia gmtime efectueaz aceast defalcare. Aceast funcie
returneaz adresa unei zone unde se afl data calendaristic defalcat.
___________________________________________________________________________________________________________________________

170

Informaiile din aceast zon trebuie salvate n cazul n care se


efectueaz prelucrri ulterioare. Un apel ulterior al funciei gmtime
le va suprascrie, zona fiind alocat static n interiorul acesteia.

struct tm *gmtime(const time_t *tims);


Funcia mktime
time_t mktime(struct tm *datc);
Aceast funcie efectueaz conversia invers, de la o structur
defalcat la o valoare compact. Este suficient s completm valorile
tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec.
Dac o valoare se afl n afara limitelor permise, acestea snt automat
normalizate. De exemplu, ora 25:00:00 este echivalat cu ora 1:00:00
a zilei urmtoare. Funcia corecteaz (dac e nevoie) toate valorile
structurii. Dac data calendaristic nu poate fi reprezentat ca o
valoare de tip time_t, funcia returneaz valoarea (time_t)(1).

Funciile ctime i asctime


char *ctime(const time_t *tims);
char *asctime(const struct *datc);
Aceste funcii convertesc data calendaristic n reprezentare
ASCII. Aceste funcii returneaz adresa unui ir de forma:

"Sun Jun 05 10:00:00 2005"


Informaiile din aceast zon trebuie salvate n cazul n care se
efectueaz prelucrri ulterioare. Un apel ulterior al funciei le va
suprascrie, zona fiind alocat static n interiorul acesteia.

Funcia clock
clock_t clock(void);
Funcia clock returneaz o aproximare a timpului pe care
procesorul l-a folosit pentru program pn la momentul apelului.
Tipul clock_t este definit de majoritatea mediilor de programare
___________________________________________________________________________________________________________________________

171

ca un sinonim pentru unsigned long. Numrul de secunde de la


lansarea programului se obine astfel:
(double)clock()/CLOCKS_PER_SECOND
Constanta CLOCKS_PER_SECOND este specific mediului de
programare folosit. De exemplu, biblioteca GNU C Compiler pentru
sistemul de operare Linux definete valoarea 1000000. O consecin
a acestui fapt este c pe un sistem de calcul pe 32 de bii funcia
clock va returna aceeai valoare la aproximativ fiecare 72 de
minute.
Observaie. Standardul C nu impune ca aceast cronometrare s
nceap de la zero la lansarea programului. De aceea, pentru a
garanta un maxim de portabilitate, cronometrarea se face astfel:
main() {
double stm;
stm = clock();
...
printf("Time:%.3lf\n",(clock()-stm)/
CLOCKS_PER_SECOND);
}

Unele sisteme de operare, de exemplu Linux, permit


cronometrarea exact a timpului de rulare a unui program (evident,
cu limitarea la 72 de minute), incluznd aici i timpul ocupat de
sistemul de operare pentru efectuarea unor operaii cerute de
program. Nu este cronometrat timpul ct procesorul este ocupat cu
rularea altui program.

11.7. Executarea unei comenzi sistem


Funcia system, declarat n <stdlib.h>, ofer posibilitatea
de a executa o comand sub controlul programului.

Nume
system execut o comand a interpretorului de comenzi
Declaraie
system(char *com);
___________________________________________________________________________________________________________________________

172

Descriere
Funcia system execut comanda com
interpretorului de comenzi cu acest parametru.

prin

lansarea

Se recomand folosirea cu atenie a acestei funcii sub unele


sisteme de operare (de exemplu Linux), dac interpretorul de
comenzi permite precizarea mai multor comenzi ntr-o singur linie.

11.8. Programe demonstrative


1) Programul prezentat n continuare genereaz un ir de n valori
ntregi aleatoare n intervalul [0,M1] pe care le depune n masivul X
(alocat dinamic), i apoi le sorteaz cresctor. n continuare se
genereaz k valori ntregi aleatoare pe care le caut n masivul X.
Pentru fiecare cutare cu succes se afieaz pe terminal valoarea
cutat i poziia n masiv.
Valorile n, k i M se iau n aceast ordine din linia de comand.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int cmp(const void *A, const void *B) {
return *(int *)A-*(int *)B;
}
int main(int ac, int **av) {
int *X,*p,M,n,k,i,v;
if (ac!=4) {
fputs("Trei argumente!\n",stderr);
return 1;
}
n=atoi(av[1]); k=atoi(av[2]);
M=atoi(av[3]);
X=(int *)malloc(n*sizeof(int));
if (!X) return 1;
srand(time(NULL));
for (i=0; i<n; i++)
X[i]=rand()%M;
qsort(X,n,sizeof(int),cmp);
for (i=0; i<k; i++) {
___________________________________________________________________________________________________________________________

173

v=rand()%M;
p=(int *)bsearch(&v,X,n,sizeof(int),cmp);
if (p)
printf("Val: %d
Pos: %d\n",v,p-X);
}
free(X);
return 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
Mbr selecteaz din zona referit de pointerul P membrul f. Deoarece
pointerul P (care poate fi argumentul A sau B al funciei comp) refer
o zon de tip void, 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 StEl.
Deoarece nu tim de la nceput cte linii are fiierul de intrare,
sntem nevoii s folosim urmtoarea strategie. La nceput alocm
pentru masivul 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.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define NA 32
typedef struct {
___________________________________________________________________________________________________________________________

174

char nm[11], ar;


float na, nb, nc, md;
} StEl;
#define Mbr(P,f) ((StEl *)P)->f
int comp(const void *A, const void *B) {
float w;
int d;
if (d=Mbr(A,ar)-Mbr(B,ar))
return d;
if (Mbr(A,ar)=='A') {
w=Mbr(B,md)-Mbr(A,md);
if (w>0) return 1;
if (w<0) return -1;
}
return strcmp(Mbr(A,nm),Mbr(B,nm));
}
int main(int ac, char **av) {
int na,ne,i;
StEl *El;
FILE *fi;
if (ac!=2) {
fputs("Un argument!\n",stderr);
return 1;
}
fi=fopen(av[1],"rt");
if (!fi) {
perror("Eroare la deschidere");
return 1;
}
na=NA; ne=0;
El=(StEl *)malloc(na*sizeof(StEl));
while (fscanf(fi,"%s %f %f %f",El[ne].nm,
&El[ne].na,&El[ne].nb, &El[ne].nc)
!=EOF) {
if ((El[ne].na>=5) && (El[ne].nb>=5) &&
(El[ne].nc>=5)) El[ne].ar='A';
else El[ne].ar='R';
El[ne].md=(El[ne].na+El[ne].nb+El[ne].nc)/3.0;
ne++;
if (ne==na) {
___________________________________________________________________________________________________________________________

175

na+=NA;
El=(StEl *)realloc(El,na*sizeof(StEl));
}

}
fclose(fi);
qsort(El,ne,sizeof(StEl),comp);
for (i=0; i<ne; i++)
printf("%-12s %c%6.2lf\n",El[i].nm,
El[i].ar,El[i].md);
free(El);
return 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): 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.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int comp(const void *A, const void *B) {
return strcmp((char *)A,(char *)B);
}
int main(int ac, char **av) {
char *C,s[11];
int n,l,i;
FILE *fi;
if (ac!=2) {
fputs("Un argument!\n",stderr);
return 1;
___________________________________________________________________________________________________________________________

176

}
fi=fopen(av[1],"rt");
if (!fi) {
perror("Eroare la deschidere");
return 1;
}
fscanf(fi,"%d %s",n,s);
l=strlen(s);
C=(char *)malloc((l+1)*n);
strcpy(C,s);
for (i=1; i<n; i++)
fscanf(fi,"%s",C+(l+1)*i);
fclose(fi);
qsort(C,n,l+1,comp);
for (i=0; i<n; i++)
printf("%s\n",C+(l+1)*i);
free(C);
return 0;
}

___________________________________________________________________________________________________________________________

177

Bibliografie (inclusiv manuale electronice)


Pentru o descriere detaliat a limbajului C recomandm:
Brian W Kernigham, Dennis M Ritchie - The C Programming Language
Prentice-Hall Software Series, 1988
http://freebooks.by.ru/CandCpp.html
Herbert Schildt - Manual C complet
Editura Teora, 1998
Brian Brown - C Programming
http://www.xploiter.com/mirrors/cprogram/cstart.htm
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

___________________________________________________________________________________________________________________________

178

Cuprins
1. Generaliti asupra limbajului C . . . . . . . . . . . . . . . . . . . .

1.1. Introducere. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2. Reprezentarea valorilor numerice . . . . . . . . . . . . . . . . . . . . . .
1.3. Primele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4
5
6

2. Unitile lexicale ale limbajului C . . . . . . . . . . . . . . . . . .

14

2.1. Identificatori; cuvinte cheie . . . . . . . . . . . . . . . . . . . . . . . . . .


2.2. Constante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3. iruri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4. Operatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.5. Separatori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14
15
17
18
18

3. Variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

3.1. Clase de memorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


3.2. Tipuri fundamentale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3. Obiecte i valori-stnga . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.4. Conversii de tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.5. Masive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.6. Iniializri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.7. Calificatorul const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21
23
26
27
30
30
32

4. Operatori i expresii . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

4.1. Expresii primare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


4.2. Operatori unari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3. Operatori aritmetici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4. Operatori de comparare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.5. Operatori logici pe bii. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.6. Operatori pentru expresii logice . . . . . . . . . . . . . . . . . . . . . . .
4.7. Operatorul condiional. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.8. Operatori de atribuire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.9. Operatorul virgul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.10. Precedena i ordinea de evaluare . . . . . . . . . . . . . . . . . . . . .

33
35
37
38
39
41
42
43
44
44

5. Instruciuni. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

5.1. Instruciunea expresie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


5.2. Instruciunea compus sau blocul . . . . . . . . . . . . . . . . . . . . . .
5.3. Instruciunea if. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.4. Instruciunile while i do . . . . . . . . . . . . . . . . . . . . . . . . . . .

46
46
47
49

___________________________________________________________________________________________________________________________

179

5.5. Instruciunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


5.6. Instruciunea switch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.7. Instruciunile break i continue. . . . . . . . . . . . . . . . . . . .
5.8. Instruciunea return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.9. Instruciunea vid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

49
51
53
55
55

6. Funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

57

6.1. Definiia funciilor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


6.2. Apelul funciilor; funcii recursive . . . . . . .. . . . . . . . . . . . . . .
6.3. Revenirea din funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4. Argumentele funciei i transmiterea parametrilor . . . . . . . . .
6.5. Exemple de funcii i programe. . . . . . . . . . . . . . . . . . . . . . . .

57
59
61
62
63

7. Dezvoltarea programelor mari . . . . . . . . . . . . . . . . . . . . .

67

7.l. nlocuirea simbolurilor, substituii macro . . . . . . . . . . . . . . . .


7.2. Includerea fiierelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.3. Compilarea condiionat . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.4. Exemple. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67
68
70
70

8. Pointeri i masive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76

8.1. Pointeri i adrese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .


8.2 Pointeri i argumente de funcii . . . . . . . . . . . . . . . . . . . . . . . .
8.3. Pointeri i masive. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.4. Aritmetica de adrese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.5. Pointeri la caracter i funcii . . . . . . . . . . . . . . . . . . . . . . . . . .
8.6. Masive multidimensionale . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.7. Masive de pointeri i pointeri la pointeri . . . . . . . . . . . . . . . .
8.8. Iniializarea masivelor i masivelor de pointeri . . . . . . . . . . .
8.9. Masive de pointeri i masive multidimensionale . . . . . . . . . .
8.10. Argumentele unei linii de comand . . . . . . . . . . . . . . . . . . .
8.11. Pointeri la funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

76
78
79
81
83
84
87
91
94
98
102

9. Structuri i reuniuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106


9.1. Elemente de baz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.2. typedef. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.3. Masive de structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.4. Pointeri la structuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.5. Structuri auto-referite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.6. Arbori binari de cutare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.7. Cmpuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.8. Reuniuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

106
109
111
115
117
123
127
129

___________________________________________________________________________________________________________________________

180

10. Intrri / ieiri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133


10.1. Intrri i ieiri standard; fiiere . . . . . . . . . . . . . . . . . . . . . . .
10.2. Accesul la fiiere; deschidere i nchidere. . . . . . . . . . . . . . .
10.3. Citire i scriere fr format . . . . . . . . . . . . . . . . . . . . . . . . . .
10.4. Citire cu format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.5. Scriere cu format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.6. Tratarea erorilor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.7. Operaii cu directoare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10.8. Programe demonstrative . . . . . . . . . . . . . . . . . . . . . . . . . . . .

133
135
138
140
143
148
150
152

11. Alte rutine din biblioteca standard . . . . . . . . . . . . . . . . . 156


11.1. Alocarea dinamic a memoriei . . . . . . . . . . . . . . . . . . . . . . .
11.2. Sortare i cutare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.3. Rutine de clasificare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.4. Operaii cu blocuri de memorie . . . . . . . . . . . . . . . . . . . . . . .
11.5. Operaii cu iruri de caractere . . . . . . . . . . . . . . . . . . . . . . . .
11.6. Biblioteca matematic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.7. Funcii pentru timp i dat calendaristic . . . . . . . . . . . . . . .
11.8. Executarea unei comenzi sistem . . . . . . . . . . . . . . . . . . . . . .
11.9. Programe demonstrative . . . . . . . . . . . . . . . . . . . . . . . . . . . .

156
157
158
160
162
166
168
171
172

Bibliografie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

___________________________________________________________________________________________________________________________

181

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