Sunteți pe pagina 1din 111

Manual De

Programare C
Theory without practice is useless; practice without theory is blind
Roger Bacon

Limbajul C a fost creat la nceputul anilor 70 de ctre Brian W


Kemigham i Dennis M Ritchie de la Bell Laboratories New Jersey, ind 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 denite de la
nceput, ele pstrndu-se n toate dezvoltrile ulterioare:
Portabilitate maxim;
Structurare;
Posibilitatea efecturii operaiilor la nivelul mainii cu pstrarea
caracteristicilor unui limbaj evoluat.
Acest manual este structurat pe 12 capitole astfel nct elementele
limbajului C s e prezentate ntr-o manier unitar. Primul capitol face o
scurt introducere i prezint patru programe C. Urmtoarele nou capitole
descriu elementele limbajului C. Capitolele unsprezece i doisprezece trec n
revist funciile cele mai des utilizate denite n biblioteca standard,
mpreun cu cteva programe demonstrative. Au fost selectate doar funciile
denite 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
consultat de programatori n elaborarea proiectelor i nu pentru a
memorat. Manualul nu este o introducere n limbajul C; se presupune c
cititorul este familiarizat cu:
Concepte de baz referitoare la programare: variabile, instruciuni de
atribuire, de control al execuiei, apeluri de funcii;
Reprezentarea informaiei n calculator a valorilor ntregi, n virgul
mobil, a codurilor ASCII;
Operaii de intrare/ieire.
Deoarece avem convingerea c cea mai bun explicaie este un
program funcional, majoritatea exemplelor din acest manual se regsesc n
iere surs C care pot 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.

1. Generaliti asupra limbajului C


1.1. Introducere.
Limbajul C este un limbaj de programare universal, caracterizat printro exprimare concis, un control modern al uxului 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 ecient dect multe alte limbaje mai
puternice.
Limbajul C permite scrierea de programe bine structurate, datorit
construciilor sale de control al uxului: 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.
Limbajul C nu are operaii care prelucreaz direct obiectele compuse
cum sunt irurile de caractere, mulimile, listele sau masivele, considerate
ecare ca o entitate. Limbajul C nu prezint faciliti de alocare a memoriei
altele dect deniia static sau disciplina de stiv relativ la variabilele
locale ale funciilor. n sfrit, limbajul C nu are faciliti de intrare-leire i
nici metode directe de acces la iere. Toate aceste mecanisme de nivel nalt
sunt realizate prin funcii explicite.
Dei limbajul C este, aadar, un limbaj de nivel relativ sczut, el este un
limbaj agreabil, expresiv i elastic, care se preteaz la o gam larg de
programe. C este un limbaj restrns i se nva relativ uor, iar subtilitile
se rein pe msur ce experiena n programare crete.
1.2. Primele programe.
n aceast seciune sunt prezentate i explicate patru programe cu
scopul de a asigura un suport de baz pentru prezentrile din capitolele
urmtoare.
Prin tradiie primul program C este un mic exemplu din lucrarea
devenit clasic The C programming language, de Brian W Kemigham i
Dennis M Ritchie.
#include <stdio. H>
Main () {
Printf (Hello, world);
Return 0;
}
Acest program aeaz 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 a n ierul cu numele stdio. H.
A doua linie declar funcia main care va conine instruciunile
programului. n acest caz singura instruciune este un apel al funciei printf

care aeaz un mesaj la terminal. Mesajul este dat ntre ghilimele i se


termin cu un caracter special new-line ().
Instruciunea return pred controlul sistemului de operare la terminarea
programului i comunic acestuia codul 0 pentru terminare. Prin convenie
aceast valoare semnic terminarea normal a programului adic nu au
aprut erori n prelucrarea datelor.
Corpul funciei main apare ntre acolade.
Al doilea program ateapt de la terminal introducerea unor numere
ntregi nenule i determin suma lor. n momentul n care se introduce o
valoare zero, programul aeaz suma calculat.
#include <stdio. H>
Main () {
Int s, n;
S = 0;
Do {
Scanf (%d, &n);
S += n;
} while (n! =0);
Printf (%d, s);
Return 0;
}
n cadrul funciei main se declar dou variabile s i n care vor memora
valori ntregi. Variabila s (care va pstra suma numerelor introduse) este
iniializat cu valoarea 0.
n continuare se repet o secven de dou instruciuni, prima ind 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 semnicaia adun la.
Aceast secven se repet (do) ct timp (while) valoarea introdus (n)
este nenul. Operatorul! = are semnicaia diferit de.
n nal funcia printf aeaz 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 an-l. n continuare se parcurge aceast list i se
determin produsul valorilor strict pozitive. n nal programul aeaz
produsul calculat.
#include <stdio. H>
Main () {
Int n, i;
Double a [100], p;

Scanf (%d, &n);


For (i=0; i<n; i+)
Scanf (%lf, &a [i]);
P = 1;
For (i=0; i<n; i+)
If (a [i] >0)
P *= a [i];
Printf (%lf, p);
Return 0;
}
n cadrul funciei main se declar dou variabile n i i care vor memora
valori ntregi. Variabila n pstreaz numrul de valori reale din lista a. Se
declar de asemenea un tablou unidimensional a care va memora 100 de
valori de tip real (dubl precizie) i o variabil p care va memora produsul
cerut.
Se citete de la terminal o valoare n. n continuare se introduc valorile
reale ai (i = 0, 1 n-l). Formatul de introducere %lf indic faptul c se
ateapt introducerea unei valori reale de la terminal, care va 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 +.
n continuare variabila p, care va memora produsul valorilor cerute, se
iniializeaz cu 1. Fiecare valoare ai este vericat (instruciunea if) dac este
strict pozitiv i n caz armativ este nmulit cu valoarea p. Operatorul *=
are semnicaia nmulete cu.
Al patrulea 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;
Printf (%hd, k);
A=12345679;b=12345678;
C=a*a-b*b;
U=a*a;v=b*b;w=u-v;
Printf (%f %f, 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 la32768 32767.

Astfel c valoarea 1110000100000000(2) (n zecimal 57600), n reprezentare


ntreag cu semn este de fapt 7936.
Al doilea set de operaii necesit o analiz mai atent; explicaiile sunt
valabile pentru programe care ruleaz pe arhitecturi Intel. Variabila c, care ar
trebui s memoreze valoarea 2461357 (rezultatul corect), va avea valoarea
2461356, deoarece tipul oat are rezervate pentru mantis doar 24 de cifre
binare. Rezultatul este foarte apropiat de cel corect deoarece rezultatele
intermediare se pstreaz n regitrii coprocesorului matematic cu precizie
maxim. Abia la memorare se efectueaz trunchierea, de unde rezult
valoarea aat.
Cu totul altfel stau lucrurile n cazul celui de al treilea set de operaii.
Aici rezultatele intermediare sunt memorate de ecare dat cu trunchiere n
variabile de tip oat. n nal se calculeaz i diferena dintre cele dou valori
trunchiate, de unde rezult valoarea 16777216.
nainte de terminare se veric dac valorile c i w sunt egale. n caz
armativ 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.
1.3. Meta-limbajul i setul de caractere.
Meta-limbajul care servete la descrierea formal a sintaxei limbajului
C este simplu.
Categoriile sintactice sau noiunile care trebuie denite sunt urmate de
simbolul :.
Deniiile alternative de categorii sunt listate pe linii separate. Dac o
linie nu este sucient, se trece la linia urmtoare, aliniat la un tab fa de
linia precedent.
Un simbol opional, terminal sau neterminal este indicat prin adugarea
imediat dup el a conguraiei de caractere <opt>.
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
30 simboluri speciale
Blanc! # % & () * +, /:; < = >? [] ^ _ ~ {|}
6 simboluri negrace ,
2. Unitile lexicale ale limbajului C
n limbajul C exist ase tipuri de uniti lexicale: identicatori, cuvintecheie, constante iruri, operatori i separatori.
2.1. Identicatori
Un identicator 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 ind liter. Deci alfabetul peste care sunt denii identicatorii
este urmtorul:
A = <a z, A Z, 0 9, _> 2.2. Cuvinte cheie
Cuvintele cheie sunt identicatori rezervai limbajului. Ei au o
semnicaie bine determinat i nu pot utilizai dect aa cum cere sintaxa
limbajului. Cuvintele-cheie se scriu obligatoriu cu litere mici. Aceste cuvinte
sunt (ecare mediu de programare C poate folosi i alte cuvinte rezervate):
Intregisterifconst
Charautoelsevoid
Floatextemforswitch
Doublestaticwhilecase
Longstructdodefault
Shortunioncontinuereturn
Unsignedtypedefbreaksizeof 2.3. Constante
n limbajul C exist urmtoarele tipuri de constante: ntreg (zecimal,
octal, hexazecimal), ntreg lung explicit, otant, 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 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 generat n
calculator pe 4 octei.
Exemplu: 123L.
O constant ntreag zecimal urmat imediat de litera u sau U este o
constant de tip ntreg fr semn. Litera u sau U poate precedat de litera
l sau L.
Exemplu: 123lu.

Constante otante
O constant otant 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 sunt constituite din cte o
succesiune de cifre. ntr-o constant otant, 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.456e-7 sau 0.12e-3
Orice constant otant se consider a 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 negrace i caractere grace (apostrof) i
(backslash) pot reprezentate ca i constante caracter cu ajutorul aa
numitor secvene de evitare. Secvenele de evitare ofer de altfel i un
mecanism general pentru reprezentarea caracterelor mai greu de introdus n
calculator i a oricror conguraii de bii. Aceste secvene de evitare sunt:
New-linecarriage return\~backslash
Tab orizontalform feed'apostrof
Backspacesemnal sonor" ghilimele
Conguraie de bii (ddd)
Aceste secvene, dei sunt formate din mai multe caractere, ele
reprezint n realitate un singur caracter. Secvena unde ddd este un ir de
1 pn la 3 cifre octale, genereaz pe un octet valoarea caracterului dorit sau
a conguraiei de bii dorite, date de irul ddd.
Exemplu: secvena 40 va genera caracterul spaiu.
Un caz special al acestei construcii este secvena care indic
caracterul NULL, care este caracterul cu valoarea zero. este scris deseori n
locul lui 0 pentru a sublinia natura de caracter a unei anumite expresii.
Cnd caracterul care urmeaz dup un backslash nu este unul dintre
cele specicate, backslash-ul este ignorat. Atragem atenia c toate
caracterele setului ASCII sunt pozitive, dar o constant caracter specicat
printr-o secven de evitare poate i negativ, de exemplu 77 are valoarea
-l.
Constante simbolice

O constant simbolic este un identicator cu valoare de constant.


Valoarea constantei poate orice ir de caractere introdus prin construcia
#dene (vezi capitolul 8).
Exemplu:#dene 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
obligatoriu).
2.4. iruri
Un ir este o succesiune de caractere scrise ntre ghilimele, de exemplu
ABCD.
Ghilimelele nu fac parte din ir; ele servesc numai pentru delimitarea
irului. Caracterul (ghilimele) poate aprea ntr-un ir dac se utilizeaz
secvena de evitare ". n interiorul unui ir pot folosite i alte secvene de
evitare pentru constante caracter, de asemenea poate folosit caracterul
(backslash) la sfritul unui rnd pentru a da posibilitatea continurii unui ir
pe mai multe linii, situaie n care caracterul nsui va ignorat.
Pentru irul de caractere se mai folosete denumirea constant ir sau
constant de tip ir.
Cnd un ir apare ntr-un program C, compilatorul creeaz un masiv de
caractere care conine caracterele irului i plaseaz automat caracterul
NULL () la sfritul irului, astfel ca programele care opereaz asupra
irurilor s poat detecta sfritul acestora. Aceast reprezentare nseamn
c, teoretic, nu exist o limit a lungimii unui ir, iar programele trebuie s
parcurg irul, analizndu-l pentru a-l determina lungimea. Se admit i iruri
de lungime zero.
Tehnic, un ir este un masiv ale crui elemente sunt caractere. El are
tipul masiv de caractere i clasa de memorie static (vezi seciunea 3.1). Un
ir este iniializat cu caracterele date (vezi seciunea 5.4).
La alocare, memoria zic cerut este cu un octet mai mare dect
numrul de caractere scrise ntre ghilimele, datorit adugrii automate a
caracterului null la sfritul ecrui ir.
Exemplu. Funcia strlen (s) returneaz lungimea irului de caractere s,
excluznd caracterul terminal null.
Int strlen (char s []) {
/*returneaz lungimea irului */
Int i;
I=0;
While (s [i]! =)
+i;
Return i;
}
Atragem atenia asupra diferenei dintre o constant caracter i un ir
care conine un singur caracter. x nu este acelai lucru cu x. x este un
singur caracter, folosit pentru a genera pe un octet valoarea numeric a

literei x, din setul de caractere al calculatorului. x este un ir de caractere,


care n calculator se reprezint pe doi octei, dintre care primul conine un
caracter (litera x), iar al doilea caracterul NULL care indic sfritul de ir.
2.5. Operatori
Limbajul C prezint un numr mare de operatori care pot clasicai
dup diverse criterii. Exist operatori unari, binari i ternari, operatori
aritmetici, logici, operatori pe bii etc.
ntr-un capitol separat vom prezenta clasele de operatori care
corespund la diferite nivele de prioritate.
2.6. Separatori
Un separator este un caracter sau un ir de caractere care separ
unitile lexicale ntr-un program scris n C.
Separatorul cel mai frecvent este aa numitul spaiu alb (blanc) care
conine unul sau mai multe spaii, tab-uri, new-line-uri sau comentarii.
Aceste construcii sunt 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 inueneaz cu nimic
semnicaia programului, scopul lui ind doar o documentare a programului.
Nu se admit comentarii imbricate.
3. Variabile
O variabil este un obiect de programare cruia i se atribuie un nume.
Variabilele ca i constantele sunt elemente de baz cu care opereaz
un program scris n C.
Variabilele se deosebesc dup denumire i pot primi diferite valori.
Numele variabilelor sunt identicatori. Numele de variabile se scriu de obicei
cu litere mici (fr a obligatoriu).
Variabilele n limbajul C sunt caracterizate prin dou atribute: clas de
memorie i tip.
Aceste dou atribute i sunt atribuite unei variabile prin intermediul
unei declaraii. Declaraiile listeaz variabilele care urmeaz a folosite,
stabilesc clasa de memorie, tipul variabilelor i eventual valorile iniiale, dar
despre declaraii vom discuta n capitolul 5.

3.1. Clase de memorie


Limbajul C prezint patru clase de memorie: automatic, extern,
static, registru.
Variabile automatice
Variabilele automatice sunt variabile locale ecrui bloc (seciunea 6.2)
sau funcii (capitolul 7). Ele se declar prin specicatorul 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 ecare 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 ecare intrare.
Dac nu sunt 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.
Variabile externe
Variabilele externe sunt variabile cu caracter global. Ele se denesc n
afara oricrei funcii i pot apelate prin nume din oricare funcie care intr
n alctuirea programului.
n declaraia de deniie aceste variabile nu necesit specicarea nici
unei clase de memorie.
La ntlnirea unei deniii de variabil extern compilatorul aloc i
memorie pentru aceast variabil.
ntr-un ier surs domeniul de deniie i aciune al unei variabile
externe este de la locul de declaraie pn la sfritul ierului.
Aceste variabile exist i i pstreaz valorile de-a lungul execuiei
ntregului program.
Pentru ca o funcie s poat utiliza o variabil extern, numele
variabilei trebuie fcut cunoscut funciei printr-o declaraie. Declaraia poate
fcut e explicit prin utilizarea specicatorului extern, e implicit prin
context.
Dac deniia unei variabile externe apare n ierul surs naintea
folosirii ei ntr-o funcie particular, atunci nici o declaraie ulterioar nu este
necesar, dar poate fcut.
Dac o variabil extern este referit ntr-o funcie nainte ca ea s e
denit, sau dac este denit ntr-un ier surs diferit de ierul n care
este folosit, atunci este obligatorie o declaraie extern pentru a lega
apariiile variabilelor respective.
Dac o variabil extern este denit ntr-un ier surs diferit de cel n
care ea este referit, atunci o singur declaraie extern dat n afara oricrei
funcii este sucient pentru toate funciile care urmeaz declaraiei.
Funciile sunt considerate n general variabile externe afar de cazul
cnd se specic altfel.
Variabilele externe se folosesc adeseori n locul listelor de argumente
pentru a comunica date ntre funcii, chiar dac funciile sunt compilate
separat.

Variabile statice
Variabilele statice se declar prin specicatorul de clas de memorie
static. Aceste variabile sunt la rndul lor de dou feluri: interne i externe.
Variabilele statice interne sunt locale unei funcii i se denesc n
interiorul unei funcii, dar spre deosebire de variabilele auto, ele i pstreaz
valorile tot timpul execuiei programului. Variabilele statice interne nu sunt
create i distruse de ecare 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 declarate i implicit prin context; de exemplu irurile de
caractere care apar n interiorul unei funcii cum ar argumentele funciei
printf (vezi capitolul 11) sunt variabile statice interne.
Variabilele statice externe se denesc n afara oricrei funcii i orice
funcie are acces la ele. Aceste variabile sunt ns globale numai pentru
ierul surs n care ele au fost denite. Nu sunt recunoscute n alte iere.
n concluzie, variabila static este extern dac este denit n afara
oricrei funcii i este static intern dac este denit n interiorul unei
funcii.
n general, funciile sunt considerate obiecte externe. Exist ns i
posibilitatea s declarm o funcie de clas static. Aceasta face ca numele
funciei s nu e recunoscut n afara ierului n care a fost declarat.
Variabile registru
O variabil registru se declar prin specicatorul de clas de memorie
register. Ca i variabilele auto ele sunt locale unui bloc sau funcii i valorile
lor se pierd la ieirea din blocul sau funcia respectiv. Variabilele declarate
register indic compilatorului c variabilele respective vor 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 numai variabilele automatice sau parametrii
formali ai unei funcii. Practic exist cteva restricii asupra variabilelor
register care reect realitatea hardware-ului de baz. Astfel:
Numai cteva variabile din ecare funcie pot pstrate n regitri
(de obicei 2 sau 3); declaraia register este ignorat pentru celelalte
variabile;
Numai tipurile de date int, char i pointer sunt admise;
Nu este posibil referirea la adresa unei variabile register.
3.2. Tipuri de variabile
Limbajul C admite numai cteva tipuri fundamentale de variabile:
caracter, ntreg, otant.
Tipul caracter
O variabil de tip caracter se declar prin specicatorul de tip char.
Zona de memorie alocat unei variabile de tip char este de un octet. Ea este

sucient 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 memorate n variabile de tip char, dar
implementarea este dependent de sistemul de calcul.
Ordinul de mrime al variabilelor caracter este ntre -l28 i 127.
Caracterele setului ASCII sunt toate pozitive, dar o constant caracter
specicat printr-o secven de evitare poate i negativ, de exemplu 77
are valoarea -l. 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).
Tipul ntreg
Variabilele ntregi pozitive sau negative pot declarate prin
specicatorul de tip int. Zona de memorie alocat unei variabile ntregi poate
de cel mult trei dimensiuni.
Relaii despre dimensiune sunt furnizate de calicatorii short, long i
unsigned, care pot aplicai tipului int.
Calicatorul short se refer totdeauna la numrul minim de octei pe
care poate reprezentat un ntreg, n cazul nostru 2.
Calicatorul long se refer la numrul maxim de octei pe care poate
reprezentat un ntreg, n cazul nostru 4.
Tipul int are dimensiunea natural sugerat de sistemul de calcul.
Scara numerelor ntregi reprezentabile n main depinde de asemenea de
sistemul de calcul: un ntreg poate lua valori ntre-32768 i 32767 (sisteme
de calcul pe 16 bii) sau ntre-2147483648 i2147483647 (sisteme de calcul
pe 32 de bii).
Calicatorul unsigned alturi de declaraia de tip int determin ca
valorile variabilelor astfel declarate s e considerate ntregi fr semn.
Numerele de tipul unsigned respect legile aritmeticii modulo 2n, unde
n este numrul de bii din reprezentarea unei variabile de tip int. Numerele
de tipul unsigned sunt totdeauna pozitive.
Declaraiile pentru calicatori sunt de forma:
Short int x;
Long int y;
Unsigned int z;
Cuvntul int poate omis n aceste situaii.
Tipul otant (virgul mobil)
Variabilele otante pot n simpl precizie i atunci se declar prin
specicatorul de tip oat sau n dubl precizie i atunci se declar prin
specicatorul double. Majoritatea sistemelor de calcul admit i reprezentarea
n precizie extins; o variabil n precizie extins se declar prin specicatorul
long double.
Tipul void (tip neprecizat)

Un pointer poate declarat de tip void. n aceast situaie pointerul nu


poate folosit pentru indirectare (derefereniere) fr un cast explicit i
aceasta deoarece compilatorul nu poate determina mrimea obiectului pe
care pointerul l indic.
Int x;
Float r;
Void *p = &x; /* p indic pe x */
Int main () {
*(int *) p = 2;
P = &r; /* p indic pe r */
*(oat *) p = 1.1;
}
Dac o funcie este declarat de tip void, aceasta nu va returna o
valoare:
Void hello (char *name) {
Printf (Hello, %s., name);
}
Tipuri derivate
n afar de tipurile aritmetice fundamentale, exist, n principiu, o clas
innit 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
aplicate recursiv. Amnunte despre tipurile derivate sunt date n seciunea
5.3.
3.3. Obiecte i valori-stnga
Alte dou noiuni folosite n descrierea limbajului C sunt 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 identicator. 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 El = E2 operandul stng El trebuie s e
o expresie valoare-stnga. n paragraful de descriere a operatorilor se va
indica dac operanzii sunt valori-stnga sau dac rezultatul operaiei este o
valoare-stnga.
3.4. Conversii de tip

Un numr mare de operatori pot cauza conversia valorilor unui operand


de la un tip la altul. Dac ntr-o expresie apar operanzi de diferite tipuri, ei
sunt 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 otant,
ntr-o expresie de forma f+i. Expresii care nu au sens, ca de exemplu un
numr otant ca indice, nu sunt 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 exibilitate considerabil n anumite tipuri de transformri de
caractere. Un astfel de exemplu este funcia atoi descris n seciunea 7.5
care convertete un ir de cifre n echivalentul lor numeric.
Expresia:
S [i] 0
Produce valoarea numeric a caracterului (cifr) memorat n ASCII.
Atragem atenia c atunci cnd o variabil de tip char este convertit la
tipul int, se poate produce un ntreg negativ, dac bitul cel mai din stnga al
octetului conine 1. Caracterele din setul de caractere ASCII nu devin
niciodat negative, dar anumite conguraii 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 sunt convertii automat la int. Conversia ntregilor
se face cu extensie de semn; ntregii sunt 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 otante
Toate operaiile aritmetice n virgul mobil se execut n precizie
extins. Conversia de la oat la int se face prin trunchierea prii fracionare.
Conversia de la int la oat 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 216 sau 232). ntr-o
reprezentare la complementul fa de 2 (deci pentru numere negative),
conversia este conceptual, nu exist nici o schimbare real a conguraiei
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 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<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 || sunt denite ca avnd valoarea 1 dac sunt adevrate i 0
dac sunt 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 specicat dup
regulile precizate mai sus. Mai precis aceasta este echivalent cu atribuirea
expresiei respective unei variabile de un tip specicat 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.
Notm ns c, coninutul real al lui n nu este alterat. Operatorul cast are
aceeai preceden ca i oricare operator unar.
Expresia constant
O expresie constant este o expresie care conine numai constante.
Aceste expresii sunt evaluate n momentul compilrii i nu n timpul
execuiei; ele pot astfel utilizate n orice loc unde sintaxa cere o constant,
ca de exemplu:
#dene MAXLINE 1000
Char line [MAXLINE+1]; 4. Operatori i expresii
Limbajul C prezint un numr mare de operatori, caracterizai prin
diferite nivele de prioritate sau preceden.
n acest capitol descriem operatorii n ordinea descresctoare a
precedenei lor. Operatorii descrii n acelai paragraf au aceeai preceden.
Vom specica de ecare dat dac asociativitatea este la stnga sau la
dreapta.
Expresiile combin variabile i constante pentru a produce valori noi i
le vom introduce pe msur ce vom prezena operatorii.
4.1. Expresii primare
Expresie-primar:
Identicator

Constant
ir (expresie)
Expresie-primar [expresie]
Expresie-primar (list-expresii<opt>)
Valoare-stnga. Identicator
Expresie-primar -> identicator
List-expresii:
Expresie
List-expresii, expresie
Un identicator este o expresie-primar, cu condiia c el s fost
declarat corespunztor. Tipul su este specicat n declaraia sa.
Dac tipul unui identicator este masiv de., atunci valoarea expresieildenticator este un pointer la primul obiect al masivului, iar tipul expresiei
este pointer la.. Mai mult, un identicator de masiv nu este o expresie
valoare-stnga.
La fel, un identicator declarat de tip funcie care returneaz., care
nu apare pe poziie de apel de funcie este convertit la pointer la funcie
care returneaz..
O constant este o expresie-primar. Tipul su poate int, long sau
double. Constantele caracter sunt de tip int, constantele otante sunt de tip
long double.
Un ir este o expresie-primar. Tipul su original este masiv de
caractere, dar urmnd aceleai reguli descrise mai sus pentru identicatori,
acesta este modicat n pointer la caracter i rezultatul este un pointer la
primul caracter al irului. Exist cteva excepii n anumite iniializri (vezi
paragraful 5.4).
O expresie ntre paranteze rotunde este o expresie-primar, al crei tip
i valoare sunt identice cu cele ale expresiei din interiorul parantezelor
(expresia din paranteze poate i o valoare-stnga).
O expresie-primar urmat de o expresie ntre paranteze ptrate este
o expresie-primar. Sensul intuitiv este de indexare. De obicei expresiaprimar are tipul pointer la., expresia-lndice are tipul int, iar rezultatul are
tipul .. O expresie de forma El[E2] este identic (prin deniie) cu *(El)
+(E2), unde * este operatorul de indirectare.
Un apel de funcie este o expresie-primar. Ea const dintr-o expresieprimar urmat de o pereche de paranteze rotunde, care conin o listexpresii separate prin virgule. Lista-expresii constituie argumentele reale ale
funciei; aceast list poate i vid. Expresia-primar trebuie s e de tipul
funcie care returneaz., iar rezultatul apelului de funcie va de tipul ..
naintea apelului, oricare argument de tip oat este convertit la tipul
double, oricare argument de tip char sau short este convertit la tipul int.
Numele de masive sunt convertite n pointeri la nceputul masivului. Nici o
alt conversie nu se efectueaz automat.
Dac este necesar pentru ca tipul unui argument actual s coincid cu
cel al argumentului formal, se va folosi un cast (vezi seciunea 3.4).
Sunt permise apeluri recursive la orice funcie.

O valoare-stnga urmat de un punct i un identicator este o expresieprimar. Valoarea-stnga denumete o structur sau o reuniune (vezi
capitolul 10) iar identicatorul denumete un membru din structur sau
reuniune. Rezultatul este o valoare-stnga care se refer la membrul denumit
din structur sau reuniune.
O expresie-primar urmat de o sgeat (constituit dintr-o liniu i
semnul > urmat de un identicator este o expresie-primar. Prima expresie
trebuie s e un pointer la o structur sau reuniune, iar identicatorul trebuie
s e 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 El->E2 este identic din punctul de vedere al rezultatului cu
(*El). E2
Descriem n continuare operatorii limbajului C mpreun cu expresiile
care se pot constitui cu aceti operatori.
4.2. Operatori unari
Toi operatorii unari au aceeai preceden, iar expresiile unare se
grupeaz de la dreapta la stnga.
Expresie-unar:
* expresie
& valoare-stnga
Expresie
! Expresie
~ expresie
+ valoare-stnga
Valoare-stnga
Valoare-stnga +
Valoare-stnga (nume-tip) expresie
Sizeof (nume-tip)
Operatorul unar * este operatorul de indirectare. Expresia care-l
urmeaz trebuie s e un pointer, iar rezultatul este o valoare-stnga care se
refer la obiectul ctre care indic expresia. Dac tipul expresiei este pointer
la. atunci tipul rezultatului este .. Acest operator trateaz operandul su
ca o adres, face acces la ea i i obine coninutul.
Exemplu: instruciuneay = *px;atribuie lui y coninutul adresei ctre
care indic px.
Operatorul unar & este operatorul de obinere a adresei unui obiect sau
de obinere a unui pointer la obiectul respectiv. Operandul este o valoarestnga iar rezultatul este un pointer la obiectul referit de valoarea-stnga.
Dac tipul valorii-stnga este . atunci tipul rezultatului este pointer la..
Exemplu. Fie x o variabil de tip int i px un pointer creat ntr-un anumit
fel (vezi capitolul 9). Atunci prin instruciunea
Px = &x;
Se atribuie variabilei de tip pointer la int px adresa variabilei x;
putem spune acum c px indic spre x. Secvena:
Px = &x;y = *px;

Este echivalent cu
Y = x;
Operatorul & poate aplicat numai la variabile i la elemente de masiv.
Construcii de forma & (x+1) i &3 nu sunt admise. De asemenea nu se
admite ca variabila s e de clas register.
Operatorul unar & ajut la transmiterea argumentelor de tip adres n
funcii.
Operatorul unar este operatorul de negativare. Operandul su este o
expresie, iar rezultatul este negativarea operandului. n acest caz sunt
aplicate conversiile aritmetice obinuite. Negativarea unui ntreg de tip
unsigned se face scznd valoarea sa din 2n, unde n este numrul de bii
rezervai tipului int.
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.
Operatorul unar ~ (tilda) este operatorul de complementare la unu. El
convertete ecare bit 1 la 0 i invers. El este un operator logic pe bii.
Operandul su trebuie s e de tip ntreg. Se aplic conversiile
aritmetice obinuite.
Operatorul unar + este operatorul de incrementare. Operandul su este
o valoare-stnga. Operatorul produce incrementarea operandului cu 1. Acest
operator prezint un aspect deosebit deoarece el poate folosit ca un
operator prex (naintea variabilei: +n) sau ca un operator postx (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 folosit, dar ntr-un context n care i valoarea lui n este
folosit +n i n+ furnizeaz dou valori distincte.
Exemplu: dac n este 5, atunci
X = n+;atribuie lui x valoarea 5
X = +n;atribuie lui x valoarea 6
n ambele cazuri n devine 6.
Rezultatul operaiei nu este o valoare-stnga, dar tipul su este tipul
valorii-stnga.
Operatorul unar este operatorul de decrementare. Acest operator este
analog cu operatorul + doar c produce decrementarea cu 1 a operandului.
Operatorul (nume-tip) este operatorul de conversie de tip. Prin numetip nelegem unul dintre tipurile fundamentale admise n C. Operandul
acestui operator este o expresie. Operatorul produce conversia valorii
expresiei la tipul denumit. Aceast construcie se numete cast.
Operatorul 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 o are n comunicarea cu rutinele de
alocare a memoriei sau rutinele I/O sistem.
Operatorul sizeof poate aplicat i unui nume-tip ntre paranteze. n
acest caz el furnizeaz dimensiunea n octei a unui obiect de tipul indicat.
Construcia sizeof (nume-tip) este luat ca o unitate, astfel c expresia
Sizeof (nume-tip)-2
Este acelai lucru cu (sizeof (nume-tip)-2 4.3. Operatori multiplicativi
Operatorii multiplicativi */i % sunt operatori aritmetici binari i se
grupeaz de la stnga la dreapta.
Expresie-multiplicativ:
Expresie * expresie
Expresie/expresie
Expresie % expresie
Operatorul binar * indic nmulirea. Operatorul este asociativ, dar n
expresiile n care apar mai muli operatori de nmulire, ordinea de evaluare
nu se specic. Compilatorul rearanjeaz chiar i un calcul cu paranteze.
Astfel a*(b*c) poate 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 nu pot de tip oat. Restul are totdeauna semnul
dempritului. Totdeauna (a/b)*b+a%b este egal cu a (dac b este diferit de
0). Sunt executate conversiile aritmetice obinuite.
4.4. Operatori aditivi
Operatorii aditivi + i sunt operatori aritmetici binari i se grupeaz
de la stnga la dreapta. Se execut conversiile aritmetice obinuite,
Expresie-aditiv:
Expresie + expresie
Expresie expresie
Operatorul binar + produce suma operanzilor si. El este asociativ i
expresiile care conin mai muli operatori pot rearanjate la fel ca n cazul
operatorului de nmulire.
Operatorul binar produce diferena operanzilor si.
4.5. Operatori de deplasare
Operatorii de deplasare < i > sunt operatori logici pe bii. Ei se
grupeaz de la stnga la dreapta.
Expresie-deplasare:
Expresie < expresie
Expresie > expresie
Operatorul < produce deplasarea la stnga a operandului din stnga cu
un numr de poziii binare dat de operandul din dreapta.

Operatorul > produce deplasarea la dreapta a operandului din stnga


cu un numr de poziii binare dat de operandul din dreapta.
n ambele cazuri se execut conversiile aritmetice obinuite asupra
operanzilor, ecare dintre ei trebuind s e de tip ntreg. Operandul din
dreapta este convertit la int; tipul rezultatului este cel al operandului din
stnga. Rezultatul este nedenit dac operandul din dreapta este negativ sau
mai mare sau egal cu lungimea obiectului, n bii. Astfel valoarea expresiei
El<E2 este El (interpretat ca i conguraie de bii) deplasat la stnga cu
E2 poziii bit; biii eliberai devin zero. Expresia El>E2 este El deplasat la
dreapta cu E2 poziii binare. Deplasarea la dreapta este logic (biii eliberai
devin 0) dac El este de tip unsigned; altfel ea este aritmetic (biii eliberai
devin copii ale bitului semn).
Exemplu: x<2 deplaseaz pe x la stnga cu 2 poziii, biii eliberai devin
0; aceasta este echivalent cu multiplicarea lui x cu 4.
4.6. Operatori relaionali
Operatorii relaionali <, >, <=, >= se grupeaz de la stnga la
dreapta.
Expresie-relaional:
Expresie < expresie
Expresie > expresie
Expresie <= expresie
Expresie >= expresie
Operatorii < (mai mic), > (mai mare), <= (mai mic sau egal) i >=
(mai mare sau egal) produc valoarea 0 dac relaia specicat 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, astfel c
expresia i<x-l se consider i< (x-l) aa dup cum ne ateptam.
4.7. Operatori de egalitate
Expresie-egalitate:
Expresie = expresie
Expresie! = expresie
Operatorii = (egal cu) i! = (diferit de) sunt analogi cu operatorii
relaionali, dar precedena lor este mai mic. Astfel a<b = c<d este 1, dac
a<b i c<d au aceeai valoare de adevr.
4.8. Operatorul I pe bii
Expresie-I:
Expresie & expresie
Operatorul & este operatorul I logic pe bii. El este asociativ i
expresiile care conin operatorul & pot 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:
Operatorul & este deseori folosit pentru a masca o anumit mulime de
bii: de exemplu:
C = n & 0177;

Pune pe zero toi biii afar de ultimii 7 bii de ordin inferior ai lui n, fr
a afecta coninutul lui n.
4.9. Operatorul SAU-exclusiv pe bii
Expresie-SAU-exclusiv:
Expresie ^ expresie
Operatorul ^ este operatorul SAU-exclusiv logic pe bii. El este
asociativ i expresiile care-l conin pot 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: 4.10.
Operatorul SAU-lnclusiv pe bii
Expresie-SAU-lnclusiv:
Expresie | expresie
Operatorul | este operatorul SAU-lnclusiv logic pe bii. El este
asociativ i expresiile care-l conin pot rearanjate. Rezultatul este funcia
logic SAU-lnclusiv pe bii aplicat operanzilor si. Operatorul se aplic
numai la operanzi de tipuri ntregi. Legea dup care funcioneaz este:
Operatorul | este folosit pentru a poziiona bii; de exemplu:
X = x | MASK;
Pune pe 1 toi biii din x care corespund la bii poziionai pe 1 din
MASK. Se efectueaz conversiile aritmetice obinuite.
4.11. Operatorul I-logic
Expresie-I-logic:
Expresie & expresie
Operatorul & este operatorul I-logic i el se grupeaz de la stnga la
dreapta. Rezultatul este 1 dac ambii operanzi sunt 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 ecare
trebuie s aib unul dintre tipurile fundamentale sau pointer. Rezultatul este
totdeauna de tip int.
4.12. Operatorul SAU-logic
Expresie-SAU-logic:
Expresie || expresie
Operatorul || este operatorul SAU-logic i el se grupeaz de la stnga
la dreapta. Rezultatul este 1 dac cel puin unul dintre operanzi este diferit
de zero i 0 n rest.
Spre deosebire de operatorul SAU-lnclusiv 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 ecare
trebuie s aib unul dintre tipurile fundamentale sau pointer. Rezultatul este
totdeauna de tip int.
4.13. Operatorul condiional
Expresie-condiional:
Expresie? Expresie: expresie

Operatorul condiional? Este un operator ternar. Prima expresie se


evalueaz i dac ea este diferit de zero sau adevrat, rezultatul este
valoarea celei de-a doua expresii, altfel rezultatul este valoarea expresiei a
treia. De exemplu expresia:
Z = (a>b)? A: b;
Calculeaz maximul dintre a i b i l atribuie lui z. Se evalueaz mai
nti prima expresie a>b. Dac ea este adevrat se evalueaz a doua
expresie i valoarea ei este rezultatul operaiei, aceast valoare se atribuie
lui z. Dac prima expresie nu este adevrat atunci z ia valoarea lui b.
Expresia condiional poate 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 sunt
pointeri de acelai tip, rezultatul are i el acelai tip; dac numai o expresie
este un pointer, cealalt trebuie sa e constanta 0, iar rezultatul este de tipul
pointerului. ntotdeauna numai una dintre expresiile a doua i a treia este
evaluat.
Dac f este otant i n ntreg, atunci expresia (h>0)? F: n
Este de tip double indiferent dac n este pozitiv sau negativ.
Parantezele nu sunt necesare deoarece precedena operatorului?: este mai
mic, dar ele pot folosite pentru a face expresia condiional mai vizibil.
4.14. Operatori de atribuire
Exist mai muli operatori de atribuire, care se grupeaz toi de la
dreapta la stnga. Operandul stng este o valoare-stnga, operandul drept
este o expresie. Tipul expresiei de atribuire este tipul operandului stng.
Rezultatul este valoarea memorat n operandul stng dup ce
atribuirea a avut loc. Cele dou pri ale unui operator de atribuire compus
sunt uniti lexicale (simboluri) distincte.
Expresie-atribuire:
Valoare-stnga = expresie
Valoare-stnga op= expresie
Unde op poate 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 El op= E2 se interpreteaz ca ind echivalente cu
expresiile de forma El = El op E2; totui El este evaluat o singur dat.
Exemplu: expresiax *= y+1este echivalent cu
X = x * (y+1)i nu cux = x * y + 1
Pentru operatorii += i -=, operandul stng poate i un pointer, n
care caz operandul din dreapta este convertit la ntreg (vezi capitolul 9). Toi
operanzii din dreapta i toi operanzii din stnga care nu sunt pointeri trebuie
s e de tip aritmetic.
Atribuirea prescurtat este avantajoas n cazul cnd n membrul stng
avem expresii complicate, deoarece ele se evalueaz o singur dat.

4.15. Operatorul virgul


Expresie-virgul:
Expresie, expresie
O pereche de expresii separate prin virgul se evalueaz de la stnga
la dreapta i valoarea expresiei din stnga se neglijeaz. Tipul i valoarea
rezultatului sunt cele ale operandului din dreapta. Aceti operatori se
grupeaz de la stnga la dreapta. n contextele n care virgula are un sens
special, (de exemplu ntr-o list de argumente reale ale unei funcii i lista de
iniializare), operatorul virgul descris aici poate aprea numai n paranteze.
De exemplu funcia:
F (a, (t=3, t+2), c)
Are trei argumente, dintre care al doilea are valoarea 5. Expresia
acestui argument este o expresie virgul. n calculul valorii lui se evalueaz
nti expresia din stnga i se obine valoarea 3 pentru t, apoi cu aceast
valoare se evalueaz a doua expresie i se obine t=5. Prima valoare a lui t
se pierde.
4.16. Precedena i ordinea de evaluare
Tabelul de la sfritul acestei seciuni constituie un rezumat al regulilor
de preceden i asociativitate ale tuturor operatorilor.
Operatorii din aceeai linie au aceeai preceden; liniile sunt 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 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 utilizate variabile temporare explicite, pentru a fora ordinea de
evaluare.
Limbajul C, ca i multe alte limbaje, nu specic n ce ordine sunt
evaluai operanzii unui operator. De exemplu ntr-o instruciune de forma:
X = f () + g ();
F poate evaluat nainte sau dup evaluarea lui g; dac f sau g
altereaz o variabil extern de care cealalt depinde, x poate depinde de
ordinea de evaluare. Din nou rezultate intermediare trebuie memorate n
variabile temporare pentru a asigura o secven particular.
Operator
Asociativitate ()[]->.
Stnga la dreapta
(tip) * & sizeof
Dreapta la stnga
*/%
Stnga la dreapta

Stnga la dreapta <>


Stnga la dreapta <=>=
Stnga la dreapta

=! =
Stnga la dreapta
&
Stnga la dreapta
^
Stnga la dreapta
|
Stnga la dreapta
&
Stnga la dreapta
||
Stnga la dreapta
?:
Dreapta la stnga
=op=
Dreapta la stnga ,
Stnga la dreapta 5. Declaraii
Declaraiile se folosesc pentru a specica interpretarea pe care
compilatorul trebuie s o dea ecrui identicator.
Declaraie:
Specicator-declaraielista-declarator<opt>;
Specicator-declaraie:
Specicator-tipspecicator-declaraie<opt>
Specicator-clas-memorie specicator-declaraie<opt>
Specicator-tip:
Char
Short
Int
Long
Unsigned
Float
Double
Void
Specicator-structur-sau-reuniune
Typedef-nume
Specicator-clas-memorie:
Auto
Static
Extern
Register
Const
Typedef
Lista-declarator:
Declarator-cu-lniializare
Declarator-cu-lniializare, lista-declarator
Declarator-cu-lniializare:

Declaratoriniializator<opt>
Declarator:
Identicator (declarator)
* declarator
Declarator ()
Declarator [expresie-constant<opt>]
Declaraiile listeaz toate variabilele care urmeaz a folosite ntr-un
program. O declaraie specic tipul, clasa de memorie i eventual valorile
iniiale pentru una sau mai multe variabile de acelai tip. Declaraiile se fac
sau n afara oricrei funcii sau la nceputul unei funcii naintea oricrei
instruciuni.
Nu orice declaraie rezerv i memorie pentru un anumit identicator,
de aceea deosebim:
Declaraia de deniie a unei variabile, care se refer la locul unde
este creat variabila i unde i se aloc memorie;
Declaraia de utilizare a unei variabile, care se refer la locul unde
numele variabilei este declarat pentru a anuna proprietile variabilei care
urmeaz a folosit.
5.1. Specicatori de clas de memorie
Specicatorii de clas de memorie sunt:
Autostaticextemregister
Consttypedef
Specicatorul typedef nu rezerv memorie i este denumit specicator
de clas de memorie numai din motive sintactice; el va discutat n
capitolul 10.
Semnicaia diferitelor clase de memorie a fost discutat deja n
paragraful 3.1.
Declaraiile cu specicatorii auto, static i register determin i
rezervarea unei zone de memorie corespunztoare. Declaraia cu
specicatorul extern presupune o deniie extern pentru identicatorii dai,
undeva n afara funciei sau ierului n care ei sunt declarai.
ntr-o declaraie poate s apar cel mult un specicator de clas de
memorie. Dac specicatorul de clas lipsete din declaraie, el se consider
implicit auto n interiorul unei funcii i deniie extern n afara funciei.
Excepie fac funciile care nu sunt niciodat automatice. De exemplu liniile:
Int sp;
Double val [MAXVAL];
Care apar ntr-un program n afara oricrei funcii, denesc variabilele
externe sp de tip int i val de tip masiv de double. Ele determin alocarea
memoriei i servesc de asemenea ca declaraii ale acestor variabile n tot
restul ierului surs. Pe de alt parte liniile:
Extern int sp;
Extern double val [];
Declar pentru restul ierului surs c variabilele sp i val sunt
externe, sp este de tip int i val este un masiv de double i c ele au fost

denite n alt parte, unde li s-a alocat i memorie. Deci aceste declaraii nu
creeaz aceste variabile i nici nu le aloc memorie.
5.2. Specicatori de tip
Specicatorii de tip sunt:
Charshortintlongunsigned
Floatdoublevoid
Specicator-structur-sau-reuniune
Typedef-nume
Cuvintele long, short i unsigned pot considerate i ca adjective;
urmtoarele combinaii sunt acceptate:
Short int
Long int
Unsigned int
Unsigned long int
Long double
ntr-o declaraie se admite cel mult un specicator de tip, cu excepia
combinaiilor amintite mai sus. Dac specicatorul de tip lipsete din
declaraie, el se consider implicit int.
Specicatorii de structuri i reuniuni sunt prezentai n seciunea 10.9,
iar declaraiile cu typedef n seciunea 10.10.
5.3. Declaratori
Lista-declarator care apare ntr-o declaraie este o succesiune de
declaratori separai prin virgule, ecare dintre ei putnd avea un iniializator.
Declaratorii din lista-declarator sunt identicatorii care trebuie
declarai.
Fiecare declarator este considerat ca o armaie care, atunci cnd
apare o construcie de aceeai form cu declaratorul, produce un obiect de
tipul i de clasa de memorie indicat. Fiecare declarator conine un singur
identicator. Gruparea declaratorilor este la fel ca i la expresii.
Dac declaratorul este un identicator simplu, atunci el are tipul indicat
de specicatorul din declaraie.
Un declarator ntre paranteze este tot un declarator, dar legtura
declaratorilor compleci poate alterat de paranteze.
S considerm acum o declaraie de forma:
TD1
Unde T este un specicator de tip (ca de exemplu int) i D1 un
declarator. S presupunem c aceast declaraie face ca identicatorul s
aib tipul . T unde . este vid dac D1 este un identicator simplu (aa
cum tipul lui x n int x este int). Dac D1 are forma:
*D
Atunci tipul identicatorului pe care-l conine acest declarator este
pointer la T.
Dac D1 are forma:
D ()
Atunci identicatorul pe care-l conine are tipul funcie care returneaz
T.

Dac D1 are forma:


D [expresie-constant]sauD []
Atunci identicatorul pe care-l conine are tipul masiv de T.
n primul caz expresia constant este o expresie a crei valoare este
determinabil la compilare i al crei tip este int. Cnd mai muli identicatori
masiv de T sunt adiaceni, se creeaz un masiv multidimensional; expresiile
constante care specic marginile masivelor pot lipsi numai pentru primul
membru din secven. Aceast omisiune este util cnd masivul este extern
i deniia real care aloc memoria este n alt parte (vezi seciunea 5.1).
Prima expresie constant poate lipsi de asemenea cnd declaratorul este
urmat de iniializare. n acest caz dimensiunea este calculat la compilare din
numrul elementelor iniiale furnizate.
Un masiv poate construit din obiecte de unul dintre tipurile de baz,
din pointeri, din reuniuni sau structuri, sau din alte masive (pentru a genera
un masiv multidimensional).
Nu toate posibilitile admise de sintaxa de mai sus sunt permise.
Restriciile sunt urmtoarele: funciile nu pot returna masive, structuri,
reuniuni sau funcii, dei ele pot returna pointeri la astfel de obiecte; nu
exist masive de funcii, dar pot masive de pointeri la funcii. De
asemenea, o structur sau reuniune nu poate conine o funcie, dar ea poate
conine un pointer la funcie. De exemplu, declaraia
Int i, *ip, f (), *p (), (*p)();
Declar un ntreg i, un pointer ip la un ntreg, o funcie f care
returneaz un ntreg, o funcie p care returneaz un pointer la un ntreg, un
pointer p la o funcie care returneaz un ntreg. Prezint interes compararea
ultimilor doi declaratori.
Construcia *p () este *(p (), astfel c declaraia sugereaz apelul
funciei p i apoi utiliznd indirectarea prin intermediul pointerului se obine
un ntreg.
n declaratorul (*p)(), parantezele externe sunt necesare pentru arta
c indirectarea printr-un pointer la o funcie furnizeaz o funcie, care este
apoi apelat; ea returneaz un ntreg.
Declaraiile de variabile pot explicite sau implicite prin context. De
exemplu declaraiile:
Int a, b, c;
Char d, m [100];
Specic un tip i o list de variabile. Aici clasa de memorie nu este
declarat explicit, ea se deduce din context. Dac declaraia este fcut n
afara oricrei funcii atunci clasa de memorie este extern; dac declaraia
este fcut n interiorul unei funcii atunci implicit clasa de memorie este
auto.
Variabilele pot distribuite n declaraii n orice mod; astfel listele le
mai sus pot scrise i sub forma:
Int a;
Int b;
Int c;

Char d;
Char m [100];
Aceasta ultim form ocup mai mult spaiu dar este mai convenabil
pentru adugarea unui comentariu pentru ecare declaraie sau pentru
modicri ulterioare.
5.4. Modicatorul const
Valoarea unei variabile declarate cu acest modicator nu poate
modicat.
Sintaxa:
Const nume-variabil = valoare;
Nume-funcie (., const tip *nume-variabil,.)
n prima variant, modicatorul atribuie o valoare iniial unei variabile
care nu mai poate ulterior modicat de program. De exemplu,
Const int vrsta = 39;
Orice atribuire pentru variabila vrsta va genera o eroare de compilare.
Atenie! O variabil declarat cu const poate indirect modicat prin
intermediul unui pointer:
Int *p = &vrsta;
*p = 35;
n a doua variant modicatorul const este folosit mpreun cu un
parametru pointer ntr-o list de parametri ai unei funcii. Funcia nu poate
modica variabila pe care o indic pointerul:
Int printf (const char *format,.); 5.5. Iniializare
Un declarator poate specica o valoare iniial pentru identicatorul
care se declar. Iniializatorul este precedat de semnul = i const dintr-o
expresie sau o list de valori incluse n acolade.
Iniializator:
Expresie {list-lniializare}
List-lniializare:
Expresie
List-lniializare, list-lniializare {list-lniializare}
Toate expresiile dintr-un iniializator pentru variabile statice sau externe
trebuie s e expresii constante (vezi seciunea 3.4) sau expresii care se
reduc la adresa unei variabile declarate anterior, posibil oset-ul unei expresii
constante. Variabilele de clas auto sau register pot iniializate cu expresii
oarecare, nu neaprat expresii constante, care implic constante sau
variabile declarate anterior sau chiar funcii.
n absena iniializrii explicite, variabilele statice i externe sunt
iniializate implicit cu valoarea 0. Variabilele auto i register au valori iniiale
nedenite (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 ecare
intrare n funcie sau bloc.
Dac un iniializator se aplic unui scalar (un pointer sau un obiect de
tip aritmetic) el const dintr-o singur expresie, eventual n acolade. Valoarea

iniial a obiectului este luat din expresie; se efectueaz aceleai operaii ca


n cazul atribuirii.
Pentru iniializarea masivelor i masivelor de pointeri vezi seciunea
9.8. Pentru iniializarea structurilor vezi seciunea 10.3.
Dac masivul sau structura conine sub-masive sau sub-structuri regula
de iniializare se aplic recursiv la membrii masivului sau structuri.
5.6. Nume-tip
n cele expuse mai sus furnizarea unui nume-tip a fost necesar n dou
contexte:
Pentru a specica conversii explicite de tip prin intermediul unui cast
(vezi seciunea 3.4);
Ca argument al lui sizeof (vezi seciunea 4.2).
Un nume-tip este n esen o declaraie pentru un obiect de acest tip,
dar care omite numele obiectului.
Nume-tip:
Specicator-tipdeclarator-abstract
Declarator-abstract:
Vid (declarator-abstract)
*declarator-abstract
Declarator-abstract ()
Declarator-abstract [expresie-constant<opt>]
Pentru a evita ambiguitatea, n construcia: (declarator-abstract)
Declaratorul abstract se presupune a nu vid. Cu aceast restricie,
este posibil s identicm n mod unic locul ntr-un declarator-abstract, unde
ar putea aprea un identicator, dac aceast construcie a fost un
declarator ntr-o declaraie. Atunci tipul denumit este acelai ca i tipul
identicatorului ipotetic. De exemplu:
Int
Int*
Int *[3]
Int (*)[3]
Int *()
Int (*)()
Denumete respectiv tipurile int, pointer la ntreg, masiv de 3
pointeri la ntregi, pointer la un masiv de 3 ntregi, funcie care
returneaz pointer la ntreg i pointer la o funcie care returneaz ntreg.
6. Instruciuni
ntr-un program scris n limbajul C instruciunile se execut secvenial,
n afar de cazul n care se indic altfel.
Instruciunile pot scrise cte una pe o linie pentru o lizibilitate mai
bun, dar nu este obligatoriu.
6.1. Instruciunea expresie
Cele mai multe instruciuni sunt instruciuni expresie. O expresie
devine instruciune dac ea este urmat de punct i virgul.
Format:
Expresie;

De obicei instruciunile expresie sunt atribuiri sau apeluri de funcie; de


exemplu:
X = 0;
Printf (.);
n limbajul C punct i virgula este un terminator de instruciune i este
obligatoriu.
6.2. Instruciunea compus sau blocul
Instruciunea compus este o grupare de declaraii i instruciuni
nchise ntre acolade. Ele au fost introduse cu scopul de a folosi mai
multeinstruciuni acolo unde sintaxa cere o instruciune. Instruciunea
compus sau blocul sunt echivalente sintactic cu o singur instruciune.
Format:
Instruciune-compus: {list-declaratori<opt>list-lnstruciuni<opt>}
List-declaratori:
Declaraie
Declaraielist-declaratori
List-lnstruciuni:
Instruciune
Instruciunelist-lnstruciuni
Dac anumii identicatori din lista-declaratori au fost declarai
anterior, atunci declaraia exterioar este salvat pe durata blocului, dup
care i reia sensul su.
Orice iniializare pentru variabile auto i register se efectueaz la
ecare 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 punct i virgul.
6.3. Instruciunea condiional
Sintaxa instruciunii condiionale admite dou formate:
If (expresie)
Instruciune-l
If (expresie)
Instruciune-l
Else instruciune-2
Instruciunea condiional se folosete pentru a lua decizii. n ambele
cazuri se evalueaz expresia i dac ea este adevrat (deci diferit de
zero) se execut instruciune-l. Dac expresia este fals (are valoarea zero)
i instruciunea if are i parte de else atunci se executinstruciune-2.
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 imbricat. Aceasta
se rezolv asociind else cu ultimul if care nu are else.
Exemplu:
If (n>0)
If (a>b)
Z = a;
Else
Z = b;
Partea else aparine if-ului din interior. Dac nu dorim acest lucru atunci
folosim acoladele pentru a fora asocierea:
If (n>0) {
If (a>b)
Z = a;
}
Else
Z = b;
Instruciunea condiional admite i construcia else-lf de forma:
If (expresie-l)
Instruciune-l
Else if (expresie-2)
Instruciune-2
Else if (expresie-3)
Instruciune-3
Else
Instruciune-4
Aceast secven de if 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 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
partea
Else instruciune-4
Poate s lipseasc.
Funcia binary din seciunea 7.5 este un exemplu de decizie multipl de
ordinul 3.
Pot exista un numr arbitrar de construcii:
Else if (expresie)
Instruciune
Grupate ntre un if iniial i un else nal.
ntotdeauna un else se leag cu ultimul if ntlnit.

6.4. Instruciunea while


Format:
While (expresie)
Instruciune
Instruciunea se execut repetat atta timp ct valoarea expresiei este
diferit de zero. Testul are loc naintea ecrei execuii a instruciunii. Prin
urmare ciclul este urmtorul: se testeaz condiia din paranteze dac ea este
adevrat, deci expresia din paranteze are o valoare diferit de zero, se
execut corpul instruciunii while, se veric din nou condiia, dac ea este
adevrat se execut din nou corpul instruciunii. Cnd condiia devine fals,
adic valoarea expresiei din paranteze este zero, se face un salt la
instruciunea de dup corpul instruciunii while, deci instruciunea while se
termin.
6.5. Instruciunea do
Format:
Do instruciune while (expresie);
Instruciunea se execut repetat pn cnd valoarea expresiei devine
zero. Testul are loc dup ecare execuie a instruciunii.
6.6. Instruciunea for
Format:
For (expresie-l<opt>; expresie-2<opt>; expresie-3<opt>)
Instruciune
Aceast instruciune este echivalent cu:
Expresie-l;
While (expresie-2){
Instruciune;
Expresie-3;
}
Expresie-l constituie iniializarea ciclului i se execut o singur dat
naintea ciclului. Expresie-2 specic testul care controleaz ciclul. El se
execut naintea ecrei 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 modicarea 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 sunt 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.
6.7. 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:
Switch (expresie) instruciune
Expresia este supus la conversiile aritmetice obinuite dar rezultatul
evalurii trebuie s e de tip int.
Fiecare instruciune din corpul instruciunii switch poate etichetat cu
una sau mai multe prexe case astfel:
Case expresie-constant:
Unde expresie-constant trebuie s e de tip int.
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 ecare constant din ecare case.
Dac se gsete o constant case egal cu valoarea expresiei, atunci
se execut instruciunea care urmeaz dup case-ul respectiv.
Dac nici o constant case nu este egal cu valoarea expresiei i dac
exist un prex default, atunci se execut instruciunea de dup el, altfel nici
o instruciune din switch nu se execut.
Prexele case i default nu altereaz uxul de control, care continu
printre astfel de prexe.
Pentru ieirea din switch se folosete instruciunea break (vezi
seciunea 6.8) sau return (vezi seciunea 6.10).
De obicei instruciunea care constituie corpul unui switch este o
instruciune compus. La nceputul acestei instruciuni pot aprea i
declaraii, dar iniializarea variabilelor automatice i registru este inefectiv.
Na = nb = ne = 0;
While (c=s [i+])
Switch (c) {
Case 0:
Case 1:
Case 2:
Case 3:
Case 4:
Case 5:
Case 6:
Case T:
Case 8:
Case 9:
Ne [c-0]+;
Break;
Case :
Case :
Case :
Nb+;

Break;
Default:
Na+;
Break;
}
Printf (cifre: );
For (i=0; i<10; i+)
Printf ( %d, ne [i]);
Printf (albe: %d, altele: %d,
Nb, na);
n acest exemplu se parcurg toate caracterele dintr-un ir, se numr
cifrele, spaiile albe i alte caractere i se aeaz aceste numere nsoite de
comentarii.
Instruciunea while este cea care asigur parcurgerea irului pn la
sfrit. Pentru ecare caracter se execut corpul instruciunii while care
const dintr-o singur instruciune switch.
Se evalueaz expresia ntreag din paranteze (n cazul nostru
caracterul c) i se compar valoarea sa cu toate constantele-case. n
momentul cnd avem egalitate se ncepe execuia de la case-ul respectiv.
Aarea rezultatelor se face prin intermediul instruciunii for i a
funciei printf (vezi capitolul 11).
6.8. Instruciunea break
Format:
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.
6.9. Instruciunea continue
Format:
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.
Mai precis n ecare 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 (vezi seciunea
6.11).
Poriunea de program din exemplul urmtor 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 */
} 6.10. 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;
n primul caz valoarea returnat nu este denit. n al doilea caz
valoarea expresiei este returnat apelantului funciei. Dac se cere, expresia
este convertit, ca ntr-o atribuire, la tipul funciei n care ea apare.
6.11. Instruciunea vid
Format:
;
Instruciunea vid este util pentru a introduce o etichet naintea unei
acolade drepte, ntr-o instruciune compus, sau pentru a introduce un corp
nul ntr-o instruciune de ciclare care cere corp al instruciunii, ca de exemplu
while sau for.
Exemplu:
For (ne=0; s [ne]! =0; +ne);
Aceast instruciune numr caracterele unui ir. Corpul lui for este vid,
deoarece tot lucrul se face n partea de test i actualizare dar sintaxa lui for
cere un corp al instruciunii. Instruciunea vid satisface acest lucru.
7. Funciile i structura unui program
Funciile sunt elementele de baz ale unui program scris n C; orice
program, de orice dimensiune, const din una sau mai multe funcii, care
specic operaiile care trebuie efectuate.
O funcie ofer un mod convenabil de ncapsulare a anumitor calcule
ntr-o cutie neagr care poate utilizat apoi fr a avea grija coninutului ei.
Funciile sunt ntr-adevr singurul mod de a face fa complexitii
programelor mari. Funciile permit desfacerea programelor mari n unele mici
i dau utilizatorului posibilitatea de a dezvolta programe, folosind ceea ce alii
au fcut deja n loc s o ia de la nceput.
Limbajul C a fost conceput s permit denirea de funcii eciente 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 mprit n mai multe iere surs n mod convenabil, iar ierele
surs pot compilate separat.
Mai precis, un program C const dintr-o secven de deniii externe.
Acestea sunt deniii de funcii i de date. Sintaxa deniiilor externe este
aceeai ca i a tuturor declaraiilor.
7.1. Deniia funciilor
Sintaxa deniiei unei funcii este urmtoarea:
Deniia-funciei:
Tip-funcie<opt> nume-funcie (lista-parametri) corp-funcie
Lista-parametri:
Declaraie-parametru
Declaraie-parametru,lista-parametri
Corpul-funciei:
Instruciune-compus
Dintre specicatorii de clas de memorie numai extern i static sunt
admii. Un declarator de funcie este similar cu un declarator pentru funcie
care returneaz. cu excepia c el listeaz parametrii formali ai funciei care
se denete.
Dup cum se observ, diferitele pri pot s lipseasc; o funcie minim
este:
Dummy () {}
Funcia care nu face nimic. Aceast funcie poate util n programe,
innd locul unei alte funcii n dezvoltarea ulterioar a programului.
Numele funciei poate precedat de un tip, dac funcia returneaz
altceva dect o valoare ntreag (vezi seciunea 7.2).
Numele funciei poate de asemenea precedat de clasa de memorie
extern sau static. Dac nici un specicator de clas de memorie nu este
prezent, atunci funcia este automat declarat extern, deoarece n C nu se
admite ca o funcie s e denit n interiorul altei funcii.
Declaraia unei funcii se face implicit la apariia ei. Se interpreteaz ca
funcie orice identicator nedeclarat anterior i urmat de o parantez (.
Pentru a evita surprize neplcute datorit neconcordanei dintre tipurile de
parametri aceast practic nu este recomandat.
Dm mai jos un exemplu de funcie complet denit:
Int max (int a, int b, int c) {
Int m;
M = (a>b)? A: b;
Return (m>c)? M: c;
}
Aceast funcie determin maximul dintre 3 numere date:
Int este un specicator de tip care indic faptul c funcia max
returneaz o valoare ntreag;
Max (int a, int b, int c) este declaraia funciei i a parametrilor
formali;
{.} este corpul funciei.

S ilustrm mecanismul denirii unei funcii scriind funcia putere


power (m, n) care ridic un ntreg m la o putere ntreag pozitiv n. Astfel
valoarea lui power (2,5) este 32. Aceast funcie desigur nu este complet,
deoarece calculeaz numai puteri pozitive de ntregi mici.
Prezentm mai jos funcia power i un program principal care o
apeleaz, pentru a putea vedea i structura unui program.
Power (int x, int n) {
Int i, p;
P = 1;
For (i=1; i<=n; +i)
P = p * x;
Return p;
}
Main () {
Int i;
For (i=0; i<10; +i)
Printf ( %d %d, i, power (2, i),
Power (-3, i);
}
Funcia power este apelat n programul principal de dou ori. Fiecare
apel transmite funciei dou argumente.
Rezultatul este aat de funcia de bibliotec printf (vezi capitolul 11).
Numele funciei este urmat obligatoriu de paranteze, chiar dac lista
parametrilor este vid.
n general numele funciei este ales dup dorina utilizatorului. Totui n
ecare program trebuie s existe o funcie cu numele impus main; orice
program i ncepe execuia cu funcia main. Celelalte funcii sunt apelate din
interiorul funciei main. Unele dintre funciile apelate sunt denite n acelai
program, altele sunt coninute ntr-o bibliotec de funcii.
Comunicarea ntre funcii se face prin argumente i valori returnate de
funcii. Comunicarea ntre funcii poate fcut i prin intermediul
variabilelor externe.
O funcie poate declarat i static. n acest caz ea poate apelat
numai din ierul unde a fost denit.
Funciile n C pot folosite recursiv, deci o funcie se poate apela pe ea
nsi e direct, e indirect.
n general ns recursivitatea nu face economie de memorie, deoarece
trebuie meninut o stiv cu valorile de prelucrat.
7.2. Apelul funciilor
n limbajul C orice funcie este apelat prin numele ei, urmat de lista
real a argumentelor, nchis ntre paranteze.
Dac ntr-o expresie numele funciei nu este urmat imediat de o
parantez (, adic funcia nu apare pe poziia de apel de funcie atunci nu
se realizeaz imediat apelul funciei respective ci se genereaz un pointer la
funcie (vezi seciunea 9.11).
7.3. Revenirea din funcii

Revenirea dintr-o funcie se face prin intermediul unei instruciuni


return.
Valoarea pe care o funcie o calculeaz poate returnat prin
instruciunea return, care dup cum am vzut are dou formate:
Return;
Return expresie;
Ca argument poate aprea orice expresie admis n C. Funcia
returneaz valoarea acestei expresii funciei apelante.
O funcie nu returneaz n mod obligatoriu o valoare. O instruciune
return, fr expresie ca parametru, cauzeaz numai transferul controlului
funciei apelante nu i o valoare util.
La rndul su funcia apelant poate ignora valoarea returnat.
De obicei o funcie returneaz o valoare de tip ntreg. Dac se dorete
ca funcia s returneze un alt tip, atunci numele tipului trebuie s precead
numele funciei, iar programul trebuie s conin o declaraie a acestei funcii
att n ierul n care funcia este denit ct i n ierul unde funcia este
apelat.
Pentru a evita orice confuzie se recomand ca tipul valorii returnate de
funcie s e ntotdeauna precizat, iar dac dorim n mod expres ca funcia s
nu returneze o valoare s folosim tipul void.
De exemplu, funcia atof (s) din biblioteca asociat compilatorului
convertete irul s de cifre n valoarea sa n dubl precizie. Vom declara
funcia sub forma:
Double atof (char s []);
Sau mpreun cu alte variabile de tip double:
Double sum, atof (char s []);
Funciile nu pot returna masive, structuri, reuniuni sau funcii.
Dac o funcie returneaz o valoare de tip char, nu este nevoie de nici
o declaraie de tip din cauza conversiilor implicite. Totdeauna tipul char este
convertit la int n expresii.
7.4. Argumentele funciei i transmiterea
Parametrilor
O metod de a comunica datele ntre funcii este prin argumente.
Parantezele mici care urmeaz dup numele funciei nchid lista
argumentelor.
n limbajul C argumentele funciilor sunt transmise prin valoare.
Aceasta nseamn c n C funcia apelat primete valorile argumentelor sale
ntr-o copie particular de variabile temporare (n realitate pe stiv).
Funcia apelat nu poate altera dect variabilele sale particulare, adic
copiile temporare.
Apelul prin valoare este o posibilitate, dar nu i o obligativitate. Apelul
prin valoare conduce la programe mai compacte cu mai puine variabile
externe, deoarece argumentele pot tratate ca variabile locale i care pot
modicate convenabil n rutina apelat.
Ca exemplu, prezentm o versiune a funciei putere care face uz de
acest fapt:

Power (int x, int n) {


Int p;
For (p=1; n>0; n)
P = p * x;
Return p;
}
Argumentul n este utilizat ca o variabil temporar i este decrementat
pn devine zero; astfel nu este nevoie de nc o variabil i. Orice operaii sar face asupra lui n n interiorul funciei, ele nu au efect asupra argumentului
pentru care funcia a fost apelat.
Dac totui se dorete alterarea efectiv a unui argument al funciei
apelante, acest lucru se realizeaz cu ajutorul pointerilor sau a variabilelor
declarate externe.
n cazul pointerilor, funcia apelant trebuie s furnizeze adresa
variabilei de modicat (tehnic printr-un pointer la aceast variabil), iar
funcia apelat trebuie s declare argumentul corespunztor ca ind un
pointer. Referirea la variabila de modicat se face prin adresare indirect
(vezi capitolul 9).
Printre argumentele funciei pot aprea i nume de masive. n acest caz
valoarea transmis funciei este n realitate adresa de nceput a masivului
(elementele masivului nu sunt copiate). Prin indexarea acestei valori funcia
poate avea acces i poate modica orice element din masiv.
7.5. Funcii cu numr variabil de parametri
Pentru funciile cu numr variabil de parametri standardul ANSI
denete o construcie special., numit elips. Acesta este de exemplu cazul
funciilor de citire i scriere cu format (familiile. scanf i. printf a se vedea
capitolul 11). n acest caz, parametrii ci sunt vericai la compilare, iar cei
variabili sunt transmii ca i cnd nu ar exista prototip de funcie.
Pentru simplitatea expunerii am pstrat conveniile denite de
standardele mai vechi ale limbajului, pstrate i de standardul ANSI C. O
funcie poate declarat fr nici un parametru, compilatorul deducnd de
aici c funcia respectiv poate avea orici parametri de orice tip. Nu se
recomand totui o asemenea practic deoarece este posibil s se creeze
confuzii care conduc la erori n execuia programelor, erori care se corecteaz
foarte greu n cazul programelor mari.
7.6. Exemple de funcii i programe 1. Acest exemplu este un program
de cutare i imprimare a tuturor liniilor dintr-un text care conin o anumit
conguraie de caractere, de exemplu cuvntul englezesc the. Structura de
baz a acestui program este:
While (mai exist linii surs)
If (linia conine conguraia de caractere)
Imprim linia
n scopul scrierii acestui program vom scrie trei funcii. Funcia getline
citete o linie din textul surs i returneaz lungimea ei. Aceast funcie are
dou argumente. Argumentul s indic numele masivului unde se va citi linia,

iar argumentul lim reprezint lungimea maxim a unei linii care poate
citit.
Funcia index caut conguraia de caractere dorit n interiorul liniei i
furnizeaz poziia sau indexul n irul s, unde ncepe irul t, sau -l dac linia
nu conine irul t. Argumentele funciei sunt s care reprezint adresa irului
de studiat i t care reprezint conguraia cutat.
Funcia care realizeaz aarea este printf din bibliotec.
Programul care realizeaz structura de mai sus este:
#dene MAXLINE 1000
#dene EOF -l
Getline (char s [], int lim) {
/*citete o linie n s; returneaz lungimea */
Int c, i;
I = 0;
While (-lim>0 & (c=getchar ()! = EOF &
C! =)
S [i+] =c;
If (c=) s [i+] =c;
S [i] =;
Return i;
}
Index (char s [], char t []) {
/*returneaz poziia din irul s unde ncepe irul t, sau -l */
Int i, j, k;
For (i=0; s [i]! =; i+) {
For (j=i, k=0; t [k]! = &
S [j] =t [k]; j+, k+)
;
If (t [k] =)
Return i;
}
Return -l;
}
Main () {
/* imprim toate liniile care conin cuvntul the */
Char line [MAXLINE];
While (getline (line, MAXLINE) >0)
If (index (line,the) >=0)
Printf (%s, line);
} 2. Funcia atof convertete un ir de cifre din formatul ASCII ntr-un
numr otant n precizie dubl.
Double atof (char s []) {
/* convertete irul s n dubl precizie */
Double val, power;
Int i, sign;
For (i=0; s [i] = || s [i] = ||

S [i] =; i+); /* sare peste spaii albe */


Sign = 1;
If (s [i] =+ || s [i] =-)
Sign = (s [i+] =+)? 1: -l;
For (val=0; s [i] >=0 & s [i] <=9; i+)
Val = 10 * val + s [i] 0;
If (s [i] = .)/* punct zecimal */
I+;
For (power=1; s [i] >=0 & s [i] <=9; i+) {
Val = 10 * val +s [i] 0:
Power *= 10;
}
Return sign * val/power;
} 3. Funcia atoi convertete un ir de cifre n echivalentul lor numeric
ntreg.
Atoi (char s []) {/* convertete irul s la un ntreg */
Int i, n;
N = 0;
For (i=0; s [i] >=0 & s [i] <=9; i+)
N = 10 * n +s [i] 0;
Return n;
} 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.
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.
Binary (int x, int v [], int n) {
/*caut x n v0, v1 vn-l */
Int low, high, mid;
Low = 0;
High = n 1;
While (low<=high) {
Mid = (low + high) /2;
If (x < v [mid])
High = mid 1;
Else if (x > v [mid])
Low = mid + 1;
Else/* s-a gsit o intrare */
Return (mid);
}
Return -l;

}
Funcia returneaz poziia lui x (un numr ntre 0 i n-l), dac x apare n
v sau -l altfel.
Exemplul ilustreaz o decizie tripl, dac x este mai mic, mai mare sau
egal cu elementul din mijlocul irului v [mid].
8. Liniile de control ale compilatorului
Limbajul C ofer o facilitate de extensie a limbajului cu ajutorul unui
preprocesor de macro-operaii simple. Folosirea liniilor de forma:
#dene
#include
Este cea mai uzual metod pentru extensia limbajului, caracterul
# (diez) indicnd faptul c aceste linii vor tratate de ctre preprocesor.
Liniile precedate de caracterul # au o sintax independent de restul
limbajului i pot aprea oriunde n ierul surs, avnd efect de la punctul de
deniie pn la sfritul ierului.
8. L. nlocuirea simbolurilor; substituii macro
O deniie de forma:
#dene identicator ir-simboluri
Determin ca preprocesorul s nlocuiasc toate apariiile ulterioare ale
identicatorului cu ir-simboluri dat (ir-simboluri nu se termin cu ;
deoarece n urma substituiei identicatorului cu acest ir ar aprea prea
multe caractere ;).
ir-simboluri sau textul de nlocuire este arbitrar. n mod normal el este
tot restul liniei ce urmeaz dup identicator. O deniie ns poate
continuat pe mai multe linii, introducnd ca ultim caracter n linia de
continuat caracterul \u8217? (backslash).
Un identicator care este subiectul unei linii #dene poate redenit
ulterior n program printr-o alt linie #dene. n acest caz, prima deniie are
efect numai pn la deniia urmtoare. Substituiile nu se realizeaz n cazul
n care identicatorii sunt ncadrai ntre ghilimele. De exemplu e deniia:
#dene ALFA 1
Oriunde va aprea n programul surs identicatorul ALFA el va
nlocuit cu constanta 1, cu excepia unei situaii de forma:
Printf (ALFA);
n care se va tipri chiar textul ALFA i nu constanta 1, deoarece
identicatorul este ncadrat ntre ghilimele.
Aceast facilitate este deosebit de valoroas pentru denirea
constantelor simbolice ca n:
#dene TABSIZE 100
Int tab [TABSIZE];
Deoarece ntr-o eventual modicare a dimensiunii tabelului tab se va
modica doar o singur linie n ierul surs. O linie de forma:
#dene identif (identif-l identif-n) ir-simboluri
n care nu exist spaiu ntre primul identicator i caracterul ( este o
deniie 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 denim o macro-operaie numit max n felul urmtor:
#dene max (a, b) (a) > (b)? (a): (b)
Atunci linia dintr-un program surs:
X = max (p+q, r+s);
Va nlocuit cu:
X = (p+q) > (r+s)? (p+q): (r+s);
Aceast macro-deniie 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, neind 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 ind evaluate de dou ori,
n cazul n care ele conin operaii ce genereaz efecte colaterale (apelurile
de funcii, operatorii de incrementare) se pot obine rezultate total eronate.
De asemenea, trebuie avut mare grij la folosirea parantezelor pentru
a face sigur ordinea evalurii dorite. De exemplu macro-operaia square (x)
denit prin:
#dene 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 +.
8.2. Includerea ierelor
O linie de forma:
#include nume-ier
Realizeaz nlocuirea liniei respective cu ntregul coninut al ierului
nume-ier. Fiierul denumit este cutat n primul rnd n directorul ierului
surs curent i apoi ntr-o succesiune de locuri standard, cum ar biblioteca
I/O standard asociat compilatorului. Alternativ, o linie de forma:
#include <nume-ier>
Caut ierul nume-ier numai n biblioteca standard i nu n
directorul ierului surs.
Deseori, o linie sau mai multe linii, de una sau ambele forme apar la
nceputul ecrui ier surs pentru a include deniii comune (prin declaraii
#dene i declaraii externe pentru variabilele globale).
Facilitatea de includere a unor iere ntr-un text surs este deosebit de
util pentru gruparea declaraiilor unui program mare. Ea va asigura faptul c
toate ierele surs vor primi aceleai deniii i declaraii de variabile, n
felul acesta eliminndu-se un tip particular de erori. Dac se modic un
ier inclus printr-o linie #include, toate ierele care depind de el trebuie
recompilate.
8.3. Compilarea condiionat
O linie de control a compilatorului de forma:
#if expresie-constant

Veric dac expresia constant este evaluat la o valoare diferit de


zero.
O linie de control de forma:
#ifdef identicator
Veric dac identicatorul a fost subiectul unei linii de control de
forma #dene.
O linie de control de forma:
#ifndef identicator
Veric dac identicatorul este nedenit n preprocesor.
Toate cele trei forme de linii de control precedente pot 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 vericrii este adevrat, atunci orice linie ntre
#else i #endif este ignorat. Dac condiia este fals atunci toate liniile ntre
testul de vericare i un #else sau n lipsa unui #else pn la #endif sunt
ignorate.
Toate aceste construcii pot imbricate.
8.4. Utilizarea dirctivelor de compilare
Prezentm n continuare un exemplu didactic de utilizare a directivelor
de compilare n dezvoltarea unor proiecte.
Se citete de la tastatur o pereche de numere naturale p i q. S se
determine dac ecare din cele dou numere este prim sau nu i s se
calculeze cel mai mare divizor comun.
S rezolvm aceast problem folosind dou iere surs. Primul
conine funcia main i apeleaz dou funcii: eprim i cmmdc. Al doilea ier
implementeaz cele dou funcii.
Prezentm mai nti un ier header (numere. H) care conine deniiile
celor dou funcii.
#ifndef _Numere_H
#dene _Numere_H
Unsigned eprim (unsigned n);
Unsigned cmmdc (unsigned p, unsigned q);
#endif
Fiierul surs princ. C este foarte scurt.
#include <stdio. H>
#include numere. H
Static void citire (unsigned *n) {
Scanf (%u, n);
If (eprim (*n)
Printf (%u e prim, *n);
Else
Printf (%u nu e prim, *n);
}
Int main () {

Unsigned p, q, k;
Citire (&p);
Citire (&q);
K=cmmdc (p, q);
Printf (Cmmdc: %u, k);
Return 0;
}
Fiierul surs numere. C este prezentat n continuare.
#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;
}
}
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 iere se poate
utiliza o singur comand de compilare:
Ce princ. C numere. C opiuni-de-compilare
Unde Ce este numele compilatorului folosit (exemplu: bcc, gcc).
Opiunile de compilare (atunci cnd sunt necesare) sunt specice mediului de
programare folosit.
Proiectele programe de dimensiuni mari sunt compuse de cele mai
multe ori din module care se elaboreaz i se pun la punct separat. Uneori
pot identicate funcii care prezint un interes general mai mare i care pot
utilizate n elaborarea unor tipuri de aplicaii foarte diverse. Acestea sunt
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 sunt
incluse n proiecte nu se mai pierde timp cu compilarea modulelor surs. n
schimb modulele care le apeleaz au nevoie de modul cum sunt descrise
aceste funcii i acesta se pstreaz n iere header.
9. Pointeri i masive
Un pointer este o variabil care conine adresa unei alte variabile.
Pointerii sunt foarte mult utilizai n programe scrise n C, pe de o parte

pentru c uneori sunt unicul mijloc de a exprima un calcul, iar pe de alt


parte pentru c ofer posibilitatea scrierii unui program mai compact i mai
ecient dect ar putea obinut prin alte ci.
9.1. Pointeri i adrese
Deoarece un pointer conine adresa unui obiect, cu ajutorul lui putem
avea acces, n mod indirect, la acea variabil (obiect).
S presupunem c x este o variabil de tip ntreg i px un pointer la
aceast variabil. Atunci aplicnd operatorul unar & lui x, instruciunea:
Px = &x;
Atribuie variabilei px adresa variabilei x; n acest fel spunem c px
indic (pointeaz) spre x.
Invers, dac px conine adresa variabilei x, atunci instruciunea:
Y = *px;
Atribuie variabilei y coninutul locaiei pe care o indic px.
Evident toate variabilele care sunt implicate n aceste instruciuni
trebuie declarate. Aceste declaraii sunt:
Int x, y;
Int *px;
Declaraiile variabilelor x i y sunt deja cunoscute. Declaraia
pointerului px este o noutate. A doua declaraie indic faptul c o combinaie
de forma *px este un ntreg, iar variabila px care apare n contextul *px este
echivalent cu un pointer la o variabil de tip ntreg. n locul tipului ntreg
poate aprea oricare dintre tipurile admise n limbaj i se refer la obiectele
pe care le indic px.
Pointerii pot aprea i n expresii, ca de exemplu n expresia urmtoare:
Y = *px + 1;
Unde variabilei y i se atribuie coninutul variabilei x plus 1.
Instruciunea:
D = sqrt (double)*px);
Are ca efect convertirea coninutului variabilei x pe care o indic px n
tip double i apoi depunerea rdcinii ptrate a valorii astfel convertite n
variabila d.
Referiri la pointeri pot aprea de asemenea i n partea stng a
atribuirilor. Dac, de exemplu, px indic spre x, atunci:
*px = 0;
Atribuie variabilei x valoarea zero, iar:
*px += 1;
Incrementeaz coninutul variabilei x cu 1, ca i n expresia: (*px)+;
n acest ultim exemplu parantezele sunt obligatorii deoarece, n lipsa
lor, expresia ar incrementa pe px n loc de coninutul variabilei pe care o
indic (operatorii unari *, + au aceeai preceden i sunt evaluai de la
dreapta spre stnga).
9.2 Pointeri i argumente de funcii
Deoarece n limbajul C transmiterea argumentelor la funcii se face
prin valoare (i nu prin referin), funcia apelat nu are posibilitatea de a

altera o variabil din funcia apelant. Problema care se pune este cum
procedm dac totui dorim s schimbm un argument?
De exemplu, o rutin de sortare poate schimba ntre ele dou elemente
care nu respect ordinea dorit, cu ajutorul unei funcii swap. Fie funcia
swap denit astfel:
Swap (int x, int y) {/* greit */
Int temp;
Temp = x;
X = y;
Y = temp;
}
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 :
Swap (&a, &b);
Iar forma corect a lui swap este:
Swap (int *px, int *py) {/* interschimb *px i *py */
Int temp;
Temp = *px;
*px = *py;
*py = temp;
} 9.3. Pointeri i masive
n limbajul C exist o strns legtur ntre pointeri i masive. Orice
operaie care poate realizat prin indicarea masivului poate de asemenea
fcut prin pointeri, care, n plus, conduce i la o accelerare a operaiei.
Declaraia:
Int a [10];
Denete un masiv de dimensiune 10, care reprezint un bloc de 10
obiecte consecutive numite a [0],. a [9]. Notaia a [i] reprezint al i-lea
element al masivului sau elementul din poziia i+1, ncepnd cu primul
element. Dac pa este un pointer la un ntreg declarat sub forma:
Int *pa;
Atunci atribuirea:
Pa = &a [0];
ncarc variabila pa cu adresa primului element al masivului a.
Atribuirea:
X = *pa;
Copiaz coninutul lui a [0] n x.
Dac pa indic un element particular al unui masiv a, atunci prin
deniie pa+i indic un element cu i poziii dup elementul pe care l indic
pa, dup cum pa-l 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 sunt adevrate indiferent de tipul variabilelor din


masivul a.
ntreaga aritmetic cu pointeri are n vedere faptul c expresia pa+i
nseamn de fapt nmulirea lui i cu lungimea elementului pe care l indic pa
i adunarea apoi la pa, obinndu-se astfel adresa elementului de indice i al
masivului.
Corespondena dintre indexarea ntr-un masiv i aritmetica de pointeri
este foarte strns. De fapt, o referire la un masiv este convertit de
compilator ntr-un pointer la nceputul masivului. Efectul este c un nume de
masiv este o expresie pointer, deoarece numele unui masiv este identic cu
numele elementului de indice zero din masiv.
Atribuirea:
Pa = &a [0];
Este identic cu:
Pa = a;
De asemenea, expresiile a [i] i *(a+i) sunt 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
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, decipa = a i pa+ sunt
instruciuni corecte. Dar un nume de masiv este o constant i deci
construcii de forma a = pa, a+ saup = &a sunt ilegale.
Cnd se transmite unei funcii un nume de masiv, ceea ce se transmite
de fapt este adresa primului element al masivului. Aadar, un nume de
masiv, argument al unei funcii, este n realitate un pointer, adic o variabil
care conine o adres. Fie de exemplu funcia strlen care calculeaz lungimea
irului s:
Strlen (char *s) {/* returneaz lungimea irului */
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)
Transmit funciei f adresa elementului a [2], deoarece &a [2] i a+2
sunt 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 []) {}


Sau
F (int *arr) {}
Declaraiile int arr [] i int *arr sunt echivalente, opiunea pentru una
din aceste forme depinznd de modul n care vor scrise expresiile n
interiorul funciei.
9.4. Aritmetica de adrese
Dac p este un pointer, atunci p += i incrementeaz pe p pentru a
indica cu i elemente dup elementul pe care l indic n prealabil p. Aceast
construcie i altele similare sunt cele mai simple i comune formule ale
aritmeticii de adrese, care constituie o caracteristic puternic a limbajului C.
S ilustrm cteva din proprietile aritmeticii de adrese scriind un alocator
rudimentar de memorie. Fie rutina alloc (n) care returneaz un pointer p la o
zon de n caractere consecutive care vor folosite de rutina apelant pentru
memorarea unui ir de caractere. Fie rutina free (p) care elibereaz o zon
ncepnd cu adresa indicat de pointerul p pentru a putea refolosit mai
trziu. Zona de memorie folosit de rutinele alloc i free este o stiv
funcionnd pe principiul ultimul intrat primul ieit, iar apelul la free trebuie
fcut n ordine invers cu apelul la alloc. S considerm c funcia alloc va
gestiona stiva ca pe elementele unui masiv pe care l vom numi allocbuf. Vom
mai folosi un pointer la urmtorul element liber din masiv, pe care-l vom
numi allocp. Cnd se apeleaz rutina alloc pentru n caractere, se veric
dac exist sucient spaiu liber n masivul allocbuf. Dac da, alloc va returna
valoarea curent a lui allocp, adic adresa de nceput a blocului cerut, dup
care va incrementa pe allocp cu n pentru a indica urmtoarea zon liber.
Free (p) actualizeaz allocp cu valoarea p, dac p indic n interiorul lui
allocbuf.
#dene NULL 0
/*valoarea pointerului pentru semnalizarea erorii */
#dene ALLOCSIZE 1000
/*dimensiunea spaiului disponibil */
Static char allocbuf [ALLOCSIZE];
/* memoria pentru alloc */
Static char *allocp = allocbuf;
/* urmtoarea poziie liber */
Char *alloc (int n) {
/*returneaz pointer la n caractere */
If (allocp+n<=allocbuf+ALLOCSIZE) {
Allocp += n; /* dimensiunea satisfcut */
Return allocp-n; /* vechea valoare */
}
Else
Return NULL; /* nu este spaiu sucient */
}
Free (char *p) {/* elibereaz memoria indicat de p */
If (p>=allocbuf & p<allocbuf+ALLOCSIZE)

Allocp = p;
}
Testulif (allocp+n<=allocbuf+ALLOCSIZE)
Veric dac exist spaiu sucient pentru satisfacerea cererii de
alocare a n caractere. Dac cererea poate satisfcut, alloc revine cu un
pointer la zona de n caractere consecutive. Dac nu, alloc trebuie s
semnaleze lipsa de spaiu pe care o face returnnd valoarea constantei
simbolice NULL. Limbajul C garanteaz c nici un pointer care indic corect o
dat nu va conine zero, prin urmare o revenire cu valoarea zero poate
folosit pentru semnalarea unui eveniment anormal (n cazul nostru, lipsa de
spaiu). Atribuirea valorii zero unui pointer este deci un caz special.
Observm de asemenea c variabilele allocbuf i allocp sunt declarate
static cu scopul ca ele s e locale numai ierului surs care conine
funciile alloc i free.
Exemplul de mai sus demonstreaz cteva din facilitile aritmeticii de
adrese (pointeri). n primul rnd, pointerii pot comparai n anumite situaii.
Dac p i q sunt pointeri la membri unui acelai masiv, atunci relaiile <, <=,
>, >=, =,! = sunt valide. Relaia p<q, de exemplu, este adevrat dac p
indic un element mai apropiat de nceputul masivului dect elementul
indicat de pointerul q. Comparrile ntre pointeri pot duce ns la rezultate
imprevizibile, dac ei se refer la elemente aparinnd la masive diferite.
Se observ c pointerii i ntregii pot 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 ind determinat din declaraia lui p (scara de aliniere este 1
pentru char, 2 pentru 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 o alt versiune a
funciei strlen folosind aceast ultim observaie:
Strlen (char *s) {/* returneaz lungimea unui ir */
Char *p;
P = s;
While (*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 ecare dat. Diferena p-s
dintre adresa ultimului element al irului i adresa primului element al irului
indic numrul de elemente.
n afar de operaiile binare menionate (adunarea sau scderea
pointerilor cu ntregi i scderea sau compararea a doi pointeri), celelalte
operaii cu pointeri sunt ilegale. Nu este permis adunarea, nmulirea,

mprirea sau deplasarea pointerilor, dup cum nici adunarea lor cu


constante de tip double sau oat.
Sunt admise de asemenea incrementrile i decrementrile precum i
alte combinaii ca de exemplu *+p i *-p.
9.5. Pointeri la caracter i funcii
O constant ir, de exemplu:
Buna dimineata
Este un masiv de caractere, care n reprezentarea intern este terminat
cu caracterul , 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:
Printf (Buna dimineata);
Funcia printf primete de fapt un pointer la masivul de caractere.
n prelucrarea unui ir de caractere sunt 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 (s, t) care
copiaz irul t peste irul s. O prim versiune a programului ar urmtoarea:
Strcpy (char s [], char t []) {/* copiaz t peste s */
Int t;
I = 0;
While (s [i] =t [i])! = )
I+;
}
O a doua versiune cu ajutorul pointerilor este urmtoarea:
Strcpy (char *s, char *t) {
/* versiune cu pointeri */
While (*s+=*t+)! = );
}
Aceast versiune cu pointeri modic prin incrementare pe s i t n
partea de test. Valoarea lui *t+ este caracterul indicat de pointerul t, nainte
de incrementare. Notaia postx + asigur c t va modicat dup
depunerea coninutului indicat de el, la vechea poziie a lui s, dup care i s
se incrementeaz. Efectul este c se copiaz caracterele irului t n irul s
pn la caracterul terminal inclusiv.
Am mai putea face o observaie legat de redundana comparrii cu
caracterul , redundan care rezult din structura instruciunii while.
i atunci forma cea mai prescurtat a funciei strcpy (s, t) este:
Strcpy (char *s, char *t) {
While (*s+ = *t+);
}

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


caracterele irurilor s i t i returneaz o valoare negativ, zero sau pozitiv,
dup cum irul s este lexicograc mai mic, egal sau mai mare ca irul t.
Valoarea returnat se obine prin scderea caracterelor primei poziii n care
s i t difer.
O prim versiune a funciei strcmp (s, t) este urmtoarea:
Strcmp (char s, char t) {/* compar irurile s i t */
Int i;
I = 0;
While (s [i] =t [i])
If (s [i+] =)
Return 0;
Return s [i]-t [i];
}
Versiunea cu pointeri a aceleiai funcii este:
Strcmp (char *s, char *t) {
For (; *s=*t; s+, t+)
If (*s=)
Return 0;
Return *s-*t;
}
n nal prezentm funcia strsav care copiaz un ir dat prin
argumentul ei ntr-o zon obinut printr-un apel la funcia alloc. Ea
returneaz un pointer la irul copiat sau NULL, dac nu mai exist sucient
spaiu pentru memorarea irului.
Char *strsav (char *s) {/* copiaz irul s */
Char *p;
P = alloc (strlen (s)+1);
If (p! =NULL)
Strcpy (p, s);
Return p;
} 9.6. Masive multidimensionale
Limbajul C ofer facilitatea utilizrii masivelor multidimensionale, dei
n practic ele sunt folosite mai puin dect masivele de pointeri.
S considerm problema conversiei datei, de la zi din lun, la zi din an
i invers, innd cont de faptul c anul poate s e bisect sau nu. Denim
dou funcii care s realizeze cele dou conversii.
Funcia day_of_year convertete ziua i luna n ziua anului i funcia
month_day convertete ziua anului n lun i zi.
Ambele funcii au nevoie de aceeai informaie i anume un tabel cu
numrul zilelor din ecare 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 day_tab [2][13] = {
};
Masivul day_tab trebuie s e declarat extern pentru a putea folosit
de ambele funcii.
n limbajul C, prin deniie, un masiv cu dou dimensiuni este n
realitate un masiv cu o dimensiune ale crui elemente sunt masive. De aceea
indicii se scriu sub forma [i][j] n loc de [i, j], cum se procedeaz n cele mai
multe limbaje. Un masiv bidimensional poate tratat n acelai fel ca i n
celelalte limbaje, n sensul c elementele sunt 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; ecare linie a unui masiv bidimensional se iniializeaz cu
ajutorul unei subliste de iniializatori. n cazul exemplului nostru, masivul
day_tab ncepe cu o coloan zero, pentru ca numerele lunilor s e ntre 1 i
12 i nu ntre 0 i 11, aceasta pentru a nu face modicri n calculul indicilor.
i atunci funciile care realizeaz conversiile cerute de exemplul nostru
sunt:
Day_of_year (int year, int month, int day) {/* ziua anului din lun i zi */
Int i, leap;
Leap = (year%4=0) & (year%100! =0) || (year%400=0);
For (i=1; i<month; i+)
Day += day_tab [leap][i];
Return day;
}
Deoarece variabila leap poate lua ca valori numai zero sau unu dup
cum expresia: (year%4=0) & (year%100! =0) || (year%400=0)
Este fals sau adevrat, ea poate folosit ca indice de linie n tabelul
day_tab care are doar dou linii n exemplul nostru.
Month_day (int year, int yearday,
Int *pmonth, int *pday) {
Int i, leap;
Leap = (year%4=0) & (year%100! =0) || (year%400=0);
For (i=1; yearday>day_tab [leap][i]; i+)
Yearday -= day_tab [leap][i];
*pmonth = i;
*pday = yearday;
}
Deoarece aceast ultim funcie returneaz dou valori, argumentele
lun i zi vor pointeri.
Exemplu: month_day (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 day_tab trebuie transmis unei funcii
f, atunci declaraia lui f poate :
F (int (*day_tab)[13])
Unde declaraia (*day_tab)[13]) indic faptul c argumentul lui f este
un pointer la un masiv de 13 ntregi.
n general deci, un masiv d-dimensional a [i][j]. [p] de rangul i*j*. *p
este un masiv d-l dimensional de rangul j*k*. *p ale crui elemente, ecare,
sunt masive d-2 dimensionale de rang k*. *p ale crui elemente, ecare,
sunt masive d-3 dimensionale .a.m.d. Oricare dintre expresiile a [i], a [i][j].,
a [i][j]. [p] pot aprea n expresii. Prima are tipul masiv, ultima are tipul int,
de exemplu, dac masivul este de tipul int. Vom mai reveni asupra acestei
probleme cu detalii.
9.7. Masive de pointeri i pointeri la pointeri
Deoarece pointerii sunt 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 lexicograc liniile de lungimi
diferite ale unui text, linii care spre deosebire de ntregi nu pot comparate
sau schimbate printr-o singur operaie.
Dac memorm liniile textului una dup alta ntr-un masiv lung de
caractere (gestionat de funcia alloc), atunci ecare linie poate accesibil
cu ajutorul unui pointer la primul ei caracter. Pointerii tuturor liniilor, la rndul
lor, pot memorai sub forma unui masiv. Atunci dou linii de text pot
comparate transmind pointerii lor funciei strcmp. Dac dou linii care nu
respect ordinea trebuie s e schimbate, se schimb doar pointerii lor din
masivul de pointeri i nu textul efectiv al liniilor.
Procesul de sortare l vom realiza n trei pai: 1) se citesc toate liniile
textului de la intrare; 2) se sorteaz liniile n ordine lexicograc; 3) se
tipresc liniile sortate n noua ordine.
Vom scrie programul prin funciile sale, ecare funcie realiznd unul
din cei trei pai de mai sus. O rutin principal va controla cele trei funcii. Ea
are urmtorul cod:
#dene LINES 100/* nr maxim de linii de sortat */
Main () {/* sorteaz liniile de la intrare */
Char *lineptr [LINES]; /* pointeri la linii */
Int nlines; /* nr linii intrare citite */
If (nlines=readlines (lineptr, LINES) >=0) {
Sort (lineptr, nlines);
Writelines (lineptr, nlines);
}
Else printf (Intrarea prea mare pentru sort);
}
Cele 3 funcii care realizeaz ntregul proces sunt: readlines, sort i
writelines.
Rutina de intrare readlines trebuie s memoreze caracterele ecrei
linii i s construiasc un masiv de pointeri la liniile citite. Trebuie, de

asemenea, s numere liniile din textul de la intrare, deoarece aceast


informaie este necesar n procesul de sortare i de imprimare. ntruct
funcia de intrare poate prelucra numai un numr nit de linii de intrare, ea
poate returna un numr ilegal, cum ar -l, 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:
#dene MAXLEN 1000
#dene NULL 0
#dene EOF -l
Readlines (char *lineptr [], int maxlines) {
/*citete liniile */
Int len, nlines;
Char *p, *alloc (), line [MAXLEN];
Nlines = 0;
While (len=getline (line, MAXLEN) >0)
If (nlines>=maxlines)
Return -l;
Else if (p=alloc (len) =NULL)
Return -l;
Else {
Line [len-l] = ;
Strcpy (p, line);
Lineptr [nlines+] = p;
}
Return nlines;
}
Instruciunea line [len-l] = ; terge caracterul <LF> de la sfritul
ecrei linii ca s nu afecteze ordinea n care sunt sortate liniile i depune n
locul lui caracterul ca marc de sfrit de ir.
Rutina care tiprete liniile n noua lor ordine este writelines i are
urmtorul cod:
Writelines (char *lineptr [], int nlines) {
/* scrie liniile sortate */
Int i;
For (i=0; i<nlines; i+)
Printf (%s, lineptr [i]);
}
Declaraia nou care apare n aceste programe este:
Char *lineptr [LINES];
Care indic faptul c lineptr este un masiv de LINES elemente, ecare
element al masivului ind un pointer la un caracter. Astfel lineptr [i] este un
pointer la un caracter, iar *lineptr [i] permite accesul la caracterul respectiv.
Deoarece lineptr este el nsui un masiv, care se transmite ca argument
funciei writelines, el va tratat ca un pointer (vezi seciunea 9.3) i atunci
funcia writelines mai poate scris i astfel:

Writelines (char *lineptr [], int nlines) {


While (-nlines>=0)
Printf (%s, *lineptr+);
}
n funcia printf, lineptr indic iniial prima linie de imprimat; ecare
incrementare avanseaz pe *lineptr la urmtoarea linie de imprimat, n timp
ce nlines se micoreaz dup ecare tiprire a unei linii cu 1.
Funcia care realizeaz sortarea efectiv a liniilor se bazeaz pe
algoritmul de njumtire i are urmtorul cod:
#dene NULL 0
#dene LINES 100/* nr maxim de linii de sortat */
Sort (char *v [], int n) {
/* sorteaz irurile v0,v1,. vn-l n ordine cresctoare */
Int gap, i, j;
Char *temp;
For (gap=n/2; gap>0; gap/=2)
For (i=gap; i<n; i+)
For (j=i-gap; j>=0; j-=gap) {
If (strcmp (v [j], v [j+gap]) <=0)
Break;
Temp = v [j];
V [j] = v [j+gap];
V [j+gap] = temp;
}
}
Deoarece ecare element al masivului v (care este de fapt masivul
lineptr) este un pointer la primul caracter al unei linii, variabila temp va i
ea un pointer la un caracter, deci operaiile de atribuire din ciclu dup
variabila j sunt admise i ele realizeaz reinversarea pointerilor la linii dac
ele nu sunt n ordinea cerut.
S reinem deci urmtoarele lucruri legate de masive i pointeri. De
cte ori apare ntr-o expresie un identicator de tip masiv el este convertit
ntr-un pointer la primul element al masivului. Prin deniie, operatorul de
indexare [] este interpretat astfel nct El[E2] este identic cu *(El)+(E2). Dac
El este un masiv, iar E2 un ntreg, atunci El[E2] se refer la elementul de
indice E2 al masivului El.
O regul corespunztoare se aplic i masivelor multi-dimensionale.
Dac El este un masiv d-dimensional, de rangul i*j*. *k, atunci ori de cte ori
el apare ntr-o expresie, el va convertit ntr-un pointer la un masiv d-l
dimensional de rangul j*. *k, ale crui elemente sunt masive. Dac operatorul
* se aplic acestui pointer, rezultatul este masivul d-l dimensional, care se
va converti imediat ntr-un pointer la un masiv d-2 dimensional .a.m.d.
Raionamentul se poate aplica n mod inductiv pn cnd, n nal, 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 3*5. Cnd x apare ntr-o expresie,
el este convertit ntr-un pointer la (primul din cele trei) masive de 5 ntregi.
n expresia x [i], care este echivalent cu expresia *(x+i), x este
convertit ntr-un pointer la un masiv, ale crui elemente sunt la rndul lor
masive de 5 elemente; apoi i se convertete la tipul x, adic indicele i se
nmulete cu lungimea elementului pe care l indic x (adic 5 ntregi) i apoi
rezultatele se adun. Se aplic operatorul * pentru obinerea masivului i (de
5 ntregi) care la rndul lui este convertit ntr-un pointer la primul ntreg din
cei cinci.
Se observ deci c primul indice din declaraia unui masiv nu joac rol
n calculul adresei.
9.8. Iniializarea masivelor i masivelor de
Pointeri
Iniializatorul unei variabile declarate masiv const dintr-o list de
iniializatori separai prin virgul i nchii ntre acolade, corespunztori
tuturor elementelor masivului. Ei sunt scrii n ordinea cresctoare a indicilor
masivului. Dac masivul conine sub-masive atunci regula se aplic recursiv
membrilor masivului. Dac n lista de iniializare exist mai puini iniializatori
dect elementele masivului, restul elementelor neiniializate se iniializeaz
cu zero. Nu se admite iniializarea unui masiv de clas cu automatic.
Acoladele {i} se pot omite n urmtoarele situaii:
Dac iniializatorul ncepe cu o acolad stng ({), atunci lista de
iniializatori, separai prin virgul, va iniializa elementele masivului; nu se
accept s existe mai muli iniializatori dect numrul elementelor
masivului;
Dac ns iniializatorul nu ncepe cu acolad stng ({), atunci se
iau din lista de iniializatori atia iniializatori ci corespund numrului de
elemente ale masivului, restul iniializatorilor vor iniializa urmtorul membru
al masivului, care are ca parte (sub-masiv) masivul deja iniializat.
Un masiv de caractere poate iniializat cu un ir, caz n care
caracterele succesive ale irului iniializeaz elementele masivului.
Exemple: 1)int x [] = {1,3,5};
Aceast declaraie denete i iniializeaz pe x ca un masiv
unidimensional cu trei elemente, n ciuda faptului c nu s-a specicat
dimensiunea masivului. Prezena iniializatorilor nchii ntre acolade
determin dimensiunea masivului.
2) Declaraia
Int y [4][3] = {
};
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 sunt 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:


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
sunt folosii pentru iniializarea lui y [0], restul iniializatorilor ind folosii
pentru iniializarea masivelor y [1] i respectiv y [2].
4) Declaraia:
Int y [4][3] = {
};
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 month_name care returneaz un pointer la un ir de
caractere care indic numele unei luni a anului. Funcia dat conine un
masiv de iruri de caractere i returneaz un pointer la un astfel de ir, cnd
ea este apelat.
Codul funciei este urmtorul:
Char *month_name (int n) {
/*returneaz numele lunii a n-a */
Static char *name [] = {
luna ilegala, ianuarie,
februarie,martie, aprilie,
mai, iunie, iulie, august,
septembrie, octombrie, noiembrie,
decembrie
}
Return (n<1) || (n>12)? Name [0]:
Name [n];
}
n acest exemplu, name este un masiv de pointeri la caracter, al crui
iniializator este o list de iruri de caractere. Compilatorul aloc o zon de
memorie pentru memorarea acestor iruri i genereaz cte un pointer la
ecare din ele pe care apoi i introduce n masivul name. Deci name [i] va
conine un pointer la irul de caractere avnd indice i al iniializatorului.
Dimensiunea masivului name nu este necesar a specicat deoarece
compilatorul o calculeaz numrnd iniializatorii furnizai i o completeaz n
declaraia masivului.
9.9. Masive de pointeri i masive
Multidimensionale
Adesea se creeaz confuzii n ceea ce privete diferena dintre un
masiv bidimensional i un masiv de pointeri. Fie date declaraiile:

Int a [10][10];
Int *b [10];
n aceast declaraie a este un masiv de ntregi cruia i se aloc spaiu
pentru toate cele 100 de elemente, iar calculul indicilor se face n mod
obinuit pentru a avea acces la oricare element al masivului.
Pentru masivul b, declaraia aloc spaiu numai pentru zece pointeri,
ecare trebuind s e ncrcat cu adresa unui masiv de ntregi.
Presupunnd c ecare 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 similar n
sensul c a [5][5] i b [5][5], de exemplu, se refer ambele la unul i acelai
ntreg (dac ecare element b [i] este iniializat cu adresa masivului a [i]).
Astfel, masivul de pointeri utilizeaz mai mult spaiu de memorie dect
masivele bidimensionale i pot cere un pas de iniializare explicit. Dar
masivele de pointeri prezint dou avantaje i anume: accesul la un element
se face cu adresare indirect, prin intermediul unui pointer, n loc de
procedura obinuit folosind nmulirea i apoi adunarea, iar al doilea avantaj
const n aceea c dimensiunea masivelor pointate poate variabil. Acest
lucru nseamn c un element al masivului de pointeri b poate indica un
masiv de zece elemente, altul un masiv de dou elemente i altul de exemplu
poate s nu indice nici un masiv.
Cu toate c problema prezentat n acest paragraf am descris-o n
termenii ntregilor, ea este cel mai frecvent utilizat n memorarea irurilor de
caractere de lungimi diferite (ca n funcia month_name prezentat mai sus).
9.10. Argumentele unei linii de comand
n sistemul de calcul care admite limbajul C trebuie s existe
posibilitatea ca n momentul execuiei unui program scris n acest limbaj s i
se transmit acestuia argumente sau parametri prin linia de comand. Cnd
un program este lansat n execuie i funcia main este apelat, apelul va
conine dou argumente. Primul argument (numit convenional argc)
reprezint numrul de argumente din linia de comand care a lansat
programul. Al doilea argument (argv) 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 imprime la terminal argumentele
lui luate din linia de comand, imprimarea fcndu-se pe o linie, iar
argumentele imprimate s e separate prin spaii.
Comanda:
Pri succes colegi
Va avea ca rezultat imprimarea la terminal a textului
Succes colegi
Prin convenie, argv [0] este un pointer la numele pri al programului
apelat, astfel c argc, care specic numrul de argumente din linia de
comand este cel puin 1.

n exemplul nostru, argc este 3, iar argv [0], argv [1] i argv [2] sunt
pointeri la pri, succes i respectiv colegi. Primul argument real este
argv [1] iar ultimul esteargv [argc-l]. Dac argc este 1, nseamn c linia de
comand nu are nici un argument dup numele programului.
Atunci programul pri are urmtorul cod:
Main (int argc, char *argv []) {
/* tiprete argumentele */
Int i;
For (i=1; i<argc; i+)
Printf (%s%c, argv [i], (i<argc-l)?
:);
}
Deoarece argv este un pointer la un masiv de pointeri, exist mai multe
posibiliti de a scrie acest program. S mai scriem dou versiuni ale acestui
program.
Main (int argc, char *argv []) {
/*versiunea a doua */
While (-argc>0)
Printf (%s%c, *+argv, (argv>1)?
:);
}
Deoarece argv este un pointer la un masiv de pointeri, incrementndul, (+argv), el va pointa la argv [1] n loc de argv [0]. Fiecare incrementare
succesiv poziioneaz pe argv la urmtorul argument, iar *argv este
pointerul la argumentul irului respectiv. n acelai timp argc este
decrementat pn devine zero, moment n care nu mai sunt argumente de
imprimat.
Alternativ:
Main (int argc, char *argv []) {
/*versiunea a treia */
While (-argc>0)
Printf (argc>1)? %s :%s, *+argv);
}
Aceast versiune arat c argumentul funciei printf poate o expresie
ca oricare alta, cu toate c acest mod de utilizare nu este foarte frecvent.
Ca un al doilea exemplu, s reconsiderm programul din seciunea 7.5,
care imprim ecare linie a unui text care conine un ir specicat de
caractere (schem).
Dorim acum ca aceast schem s poat modicat dinamic, de la
execuie la execuie. Pentru aceasta o specicm printr-un argument n linia
de comand.
i atunci programul care caut schema dat de primul argument al
liniei de comand este:
#dene MAXLINE 1000
Main (int argc, char *argv []) {
/* gsete schema din primul argument */

Char line [MAXLINE];


If (argc! =2)
Printf (Linia de comanda eronata);
Else
While (getline (line, MAXLINE) >0)
If (index (line, argv [1]) >=0)
Printf (%s, line);
}
Unde linia de comand este de exemplu: nd limbaj n care nd
este numele programului, iar limbaj este schema cutat. Rezultatul va
imprimarea 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 tipreasc toate liniile cu excepia acelora
care conin schema i al doilea care s precead ecare linie tiprit cu
numrul ei de linie.
O convenie pentru programele scrise n limbajul C este ca argumentele
dintr-o linie de comand care ncep cu un semn - s introduc un parametru
opional. Dac alegem, de exemplu.
X 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.
Va produce tiprirea liniei a doua, precedat de numrul ei, deoarece
aceast linie nu conine schema la.
Argumentele opionale sunt 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 sunt
concatenate, ca n comanda:
Find -xn la
Caracterele x respectiv n indic doar absena sau prezena acestor
opiuni (switch) i nu sunt tratate din punct de vedere al valorii lor.
Fie programul care caut schema la n liniile de la intrare i le
tiprete pe acelea, care nu conin schema, precedate de numrul lor de
linie. Programul trateaz corect, att prima form a liniei de comand ct i
a doua.
#dene MAXLINE 1000
Main (int argc, char *argv []) {

/* caut schema */
Char line [MAXLINE], *s;
Long line0;
Int except, number;
Line0 = 0;
Number = 0;
While (-argc>0 & (*+argv)[0] =-)
For (s=argv [0]+1; *s! =; s+)
Switch (*s) {
Case x: except = 1; break;
Case n: number = 1; break;
Default:
Printf (nd: optiune ilegala %c,
*s);
Argc = 0;
Break;
}
If (argc! =1)
Printf (Nu exista argumente sau schema);
Else
While (getline (line, MAXLINE) >0) {
Line0+;
If (index (line, *argv) >=0)! =except) {
If (number)
Printf (%d:, line0);
Printf (%s, line);
}
}
}
Dac nu exist erori n linia de comand, atunci la sfritul primului
ciclu while argc trebuie s e 1, iar *argv conine adresa schemei. *+argv
este un pointer la un ir argument, iar (*+argv)[0] este primul caracter al
irului. n aceast ultim expresie parantezele sunt necesare deoarece fr
ele expresia nseamn *+(argv [0]) ceea ce este cu totul altceva (i greit): al
doilea caracter din numele programului. O alternativ corect pentru (*+argv
[0]) este *+argv.
9.11. Pointeri la funcii
n limbajul C o funcie nu este o variabil, dar putem deni un pointer la
o funcie, care apoi poate prelucrat, transmis unor alte funcii, introdus ntrun 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 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. Deniia funciei g va
:
G (int (*funcpt) () { (*funcpt)();
}
Funcia f trebuie declarat explicit n rutina apelant(int f ();), deoarece
apariia ei n g (f) nu a fost urmat de parantez stng (. n expresia g (f) f
nu apare pe poziia de apel de funcie. n acest caz, pentru argumentul
funciei g se genereaz un pointer la funcia f. Deci g apeleaz funcia f printrun 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 ();
nseamn 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 faptul c funcpt este un pointer la o funcie, *funcpt este funcia,
iar (*funcpt)() este apelul funciei.
O form echivalent simplicat de apel este urmtoarea:
Funcpt ();
Ca un exemplu, s considerm procedura de sortare a liniilor de la
intrare, descris n seciunea 9.7, dar modicat n sensul ca dac
argumentul opional -n apare n linia de comand, atunci liniile se vor sorta
nu lexicograc ci numeric, liniile coninnd grupe de numere.
O sortare const adesea din trei pri: o comparare care determin
ordinea oricrei perechi de elemente, un schimb care inverseaz ordinea
elementelor implicate i un algoritm de sortare care face comparrile i
inversrile pn cnd elementele sunt aduse n ordinea cerut. Algoritmul de
sortare este independent de operaiile de comparare i inversare, astfel nct
transmind diferite funcii de comparare i inversare funciei de sortare,
elementele de intrare se pot aranja dup diferite criterii.
Compararea lexicograc a dou linii se realizeaz prin funciile strcmp
i swap. Mai avem nevoie de o rutin numcmp care s compare dou linii pe
baza valorilor numerice i care s returneze aceiai indicatori ca i rutina
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:
#dene LINES 100/* nr maxim de linii de sortat */
Main (int argc, char *argv []) {
Char *lineptr [LINES]; /* pointeri la linii text */
Int nlines; /* numr de linii citite */
Int strcmp (), numcmp (); /* funcii de comparare */

Int swap (); /* funcia de inversare */


Int numeric;
Numeric = 0; /* 1 dac sort numeric */
If (argc>1 & argv [1][0] =- &
Argv [1][1] =n)
Numeric = 1;
If (nlines=readlines (lineptr, LINES) >=0) {
If (numeric)
Sort (lineptr, nlines, numcmp, swap);
Else
Sort (lineptr, nlines, strcmp, swap);
Writelines (lineptr, nlines);
}
Else
Printf (Nr de linii de intrare prea mare);
}
n apelul funciei sort, argumentele strcmp, numcmp i swap sunt
adresele funciilor respective. Deoarece ele au fost declarate funcii care
returneaz un ntreg, operatorul & nu este necesar s precead numele
funciilor, compilatorul ind cel care gestioneaz transmiterea adreselor
funciilor.
Funcia sort care aranjeaz liniile n ordinea cresctoare se va modica
astfel:
Sort (char *v [], int n, int (*comp)(),
Int (*exch)() {/* sorteaz v0, v1 vn-l */
Int gap, i, j;
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;
Exch (v+j, v+j+gap);
}
}
S studiem declaraiile din aceast funcie.
Int (*comp)(), (*exch)();
Indic faptul c comp i exch sunt pointeri la funcii care returneaz un
ntreg (primul set de paranteze este necesar).
If (comp (v [j], v [j+gap]) <=0)
nseamn apelul funciei comp (adic strcmp sau numcmp), deoarece
comp este un pointer la funcie, *comp este funcia, iar
Comp (v [j], v [j+gap])
Este apelul funciei.
Exch (v+j, v+j+gap)

Este apelul funciei swap, de inversare a dou linii, inversare care


realizeaz interschimbarea adreselor liniilor implicate (vezi seciunea 9.2).
Funcia numcmp este urmtoarea:
Numcmp (char *s1, char *s2) {
/* compar s1 i s2 numeric */
Double atof (), v1, v2;
V1 = atof (s1);
V2 = atof (s2);
If (v1<v2)
Return -l;
Else
If (v1>v2)
Return 1;
Else
Return 0;
}
Pentru ca programul nostru s e complet s mai prezentm i codul
funciei swap, care schimb ntre ei pointerii a dou linii.
Swap (char *px [], char *py []) {
Char *temp;
Temp = *px;
*px = *py;
*py = temp;
} 10. Structuri i reuniuni
O structur este o colecie de una sau mai multe variabile, de acelai
tip sau de tipuri diferite, grupate mpreun sub un singur nume pentru a
putea tratate mpreun (n alte limbaje, structurile se numesc articole).
Structurile ajut la organizarea datelor complicate, n special n
programele mari, deoarece permit unui grup de variabile legate s e tratate
ca o singur entitate. Vom ilustra n acest capitol modul de utilizare a
structurilor.
10.1. Elemente de baz
S revenim asupra rutinei de conversie a datei prezentat n capitolul
9.
O dat const din zi, lun i an, eventual numrul zilei din an i numele
lunii. Aceste cinci variabile pot grupate ntr-o singur structur astfel:
Struct date {
Int day;
Int month;
Int year;
Int yearday;
Char mon_name [4];
};
Cuvntul cheie struct introduce o declaraie de structur care este o
list de declaraii nchise n acolade.

Cuvntul struct poate urmat opional de un nume, numit marcaj de


structur sau etichet de structur, cum este n exemplul nostru numele
date. Acest marcaj denumete acest tip de structur i poate 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 conicte,
deoarece ele vor ntotdeauna deosebite una de alta din context.
Acolada dreapt care ncheie o list de membri ai unei structuri poate
urmat de o list de variabile, la fel ca i n cazul tipurilor de baz. De
exemplu:
Struct {.} x, y, z;
Este din punct de vedere sintactic analog cu:
Int x, y, z;
n sensul c ecare declaraie declar pe x, y i z ca variabile de tipul
numit (structur n primul caz i ntreg n al doilea) i cauzeaz alocarea de
spaiu pentru ele.
O declaraie de structur care nu este urmat de o list de variabile nu
aloc memorie; ea descrie numai un ablon, o form de structur. Dac
structura este marcat sau etichetat, atunci marcajul ei poate folosit mai
trziu pentru denirea unor alte variabile de tip structur, cu acelai ablon
ca structura marcat. De exemplu, ind dat declaraia:
Struct date d;
Ea denete variabila d, ca o structur de acelai fel (ablon) ca
structura date.
O structur extern sau static poate iniializat, atand dup
deniia ei o list de iniializatori pentru componente, de exemplu:
Struct date d = {4,7,1984,185,iulie};
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 e atribuirea:
Leap = (d. year%4=0) & (d. year%100! =0)
|| (d. year%400=0);
Sau vericarea numelui lunii:
If (strcmp (d. mon_name,august) =0).
Structurile pot imbricate; o nregistrare de stat de plat, de exemplu,
poate de urmtoarea form:
Struct person {
Char name [NAMESIZE];
Char address [ADRSIZE];
Long zipcode;
Long ss_number;
Double salary;
Struct date birthdate;

Struct date hiredate;


};
Structura person conine dou structuri de ablon date. Declaraia:
Struct person emp;
Denete i aloc o structur cu numele emp de acelai ablon ca i
person. Atunci:
Emp. Birthdate. Month
Se refer la luna de natere. Operatorul de membru de structur .
este asociativ de la stnga la dreapta.
10.2. Structuri i funcii
Exist un numr de restricii asupra structurilor n limbajul C. Singurele
operaii care se pot aplica unei structuri sunt accesul la un membru al
structurii i considerarea adresei ei cu ajutorul operatorului &. Acest lucru
implic faptul c structurile nu pot atribuite sau copiate ca entiti i c ele
nu pot transmise ca argumente la funcii i nici returnate din funcii.
Structurile de clas automatic, ca i masivele de aceeai clas, nu pot
iniializate; pot iniializate numai structurile externe i statice, regulile de
iniializare ind aceleai ca pentru masive.
Pointerii la structuri nu se supun ns acestor restricii, motiv pentru
care structurile i funciile pot coexista i conlucra prin intermediul
pointerilor.
Ca un exemplu, s rescriem programul de conversie a datei, care
calculeaz ziua anului, din lun i zi.
Day_of_year (struct date *pd) {
/*calculul zilei anului */
Int i, day, leap;
Day = pd->day;
Leap = (pd->year%4=0) & (pd->year%100! =0) || (pd->year%400=0);
For (i=1; i<pd->month; i+)
Day += day_tab [leap][i];
Return day;
}
Declaraia:
Struct date * pd;
Indic faptul c pd este un pointer la o structur de ablonul lui date.
Notaia:
Pd->year
Indic faptul c se refer membrul year al acestei structuri. n
general, dac p este un pointer la o structur p->membru-structur se refer
la un membru particular (operatorul -> se formeaz din semnul minus
urmat de semnul mai mare).
Deoarece pd este pointer la o structur, membrul year poate de
asemenea referit prin: (*pd). Year
Notaia -> se impune ca un mod convenabil de prescurtare. n notaia
(*pd). Year, parantezele sunt necesare deoarece precedena operatorului
membru de structur . este mai mare dect cea a operatorului *.

Ambii operatori . i -> sunt asociativi de la stnga la dreapta, astfel


nct:
P->q->membru
Emp. Birthdate. Month
Sunt de fapt: (p->q)->membru (emp. Birthdate). Month
Operatorii -> i . ai structurilor, mpreun cu () pentru listele de
argumente i [] pentru indexare se gsesc n vrful listei de preceden (vezi
seciunea 4.16), ind din acest punct de vedere foarte apropiai. Astfel, ind
dat declaraia:
Struct {
Int x;
Int *y;} *p;
Unde p este un pointer la o structur, atunci expresia:
+p->x
Incrementeaz pe x, nu pointerul p, deoarece operatorul -> are o
preceden mai mare dect +. Parantezele pot folosite pentru a modica
ordinea operatorilor dat de precedena. Astfel: (+p)->x
Incrementeaz mai nti pe p i apoi acceseaz elementul x, din
structura nou pointat.
n expresia (p+)->x se acceseaz mai nti x, apoi se incrementeaz
pointerul p.
n mod analog, *p->y indic coninutul adresei pe care o indic y.
Expresia *p->y+ acceseaz mai nti ceea ce indic y i apoi incrementeaz
pe y. Expresia (*p->y)+ incrementeaz ceea ce indic y. Expresia *p+->y
acceseaz ceea ce indic y i apoi incrementeaz pointerul p.
10.3. Masive de structuri
Structurile sunt n mod special utile pentru tratarea masivelor de
variabile legate prin context. Pentru exemplicare vom considera un program
care numr intrrile ecrui cuvnt cheie dintr-un text. Pentru aceasta avem
nevoie de un masiv de iruri de caractere, pentru pstrarea numelor
cuvintelor cheie i un masiv de ntregi pentru a memora numrul apariiilor.
O posibilitate este de a folosi dou masive paralele keyword i keycount
declarate prin:
Char *keyword [NKEYS];
Int keycount [NKEYS];
Respectiv unul de pointeri la iruri de caractere i cellalt de ntregi.
Fiecrui cuvnt cheie i corespunde perechea:
Char *keyword;
Int keycount;
Astfel nct putem considera cele dou masive ca ind un masiv de
perechi. Atunci, declaraia de structur:
Struct key {
Char *keyword;
Int keycount;
} keytab [NKEYS];

Denete un masiv keytab de structuri de acest tip i aloc memorie


pentru ele. Fiecare element al masivului keytab este o structur de acelai
ablon ca i structura key.
Deniia masivului keytab poate scris i sub forma:
Struct key {
Char *keyword;
Int keycount;
};
Struct key keytab [NKEYS];
Deoarece masivul de structuri keytab conine, n cazul nostru, o
mulime constant de cuvinte cheie, este mai uor de iniializat o dat pentru
totdeauna chiar n locul unde este denit. Iniializarea structurilor este o
operaie analoag cu iniializarea unui masiv n sensul c deniia este
urmat de o list de iniializatori nchii n acolade.
Atunci iniializarea masivului de structuri keytab va urmtoarea:
Struct key {
Char * keyword;
Int keycount;
} keytab [] = {
break, 0,
case, 0,
char, 0,
/*. */
while, 0};
Iniializatorii sunt perechi care corespund la membrii structurii.
Iniializarea ar putea fcut i incluznd iniializatorii ecrei structuri din
masiv n acolade ca n: {break, 0}, {case, 0}.
Dar parantezele interioare nu sunt necesare dac iniializatorii sunt
variabile simple sau iruri de caractere i dac toi iniializatorii sunt prezeni.
Compilatorul va calcula, pe baza iniializatorilor, dimensiunea masivului
de structuri keytab motiv pentru care, la iniializare, nu este necesar
indicarea dimensiunii masivului.
Programul de numrare a cuvintelor cheie ncepe cu denirea masivului
de structuri keytab. Rutina principal main citete textul de la intrare prin
apel repetat la o funcie getword, care extrage din intrare cte un cuvnt la
un apel. Fiecare cuvnt este apoi cutat n tabelul keytab cu ajutorul unei
funcii de cutare binary, descris n seciunea 7.5. Lista cuvintelor cheie
trebuie s e n ordine cresctoare pentru ca funcia binary s lucreze corect.
Dac cuvntul cercetat este un cuvnt cheie atunci funcia binary returneaz
numrul de ordine al cuvntului n tabelul cuvintelor cheie, altfel returneaz l.
#dene MAXWORD 20
Binary (char *word, struct key tab [],
Int n) {
Int low, high, mid, cond;
Low = 0;

High = n 1;
While (low<=high) {
Mid = (low + high) /2;
If ( (cond=strcmp (word, tab [mid]. Keyword)
High = mid 1;
Else
If (cond>0) low = mid + 1;
Else
Return mid;
}
Return -l;
}
Main () {/* numr cuvintele cheie */
Int n, t;
Char word [MAXWORD];
While (t=getword (word, MAXWORD)! =EOF)
If (t=LETTER)
If ( (n=binary (word, keytab, NKEYS)
Keytab [n]. Keycount+;
For (n=0; n<NKEYS; n+)
If (keytab [n]. Keycount>0)
Printf (%4d %s,
Keytab [n]. Keycount,
Keytab [n]. Keyword);
}
nainte de a scrie funcia getword este sucient s spunem c ea
returneaz constanta simbolic LETTER de ecare dat cnd gsete un
cuvnt n textul de intrare i copiaz cuvntul n primul ei argument.
Cantitatea NKEYS este numrul cuvintelor cheie din keytab
(dimensiunea masivului de structuri). Dei putem calcula acest numr
manual, este mai simplu i mai sigur s-o facem cu calculatorul, mai ales dac
lista cuvintelor cheie este supus modicrilor. O posibilitate de a calcula
NKEYS cu calculatorul este de a termina lista iniializatorilor cu un pointer
NULL i apoi prin ciclare pe keytab s detectm sfritul lui. Acest lucru este
mai mult dect 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 descris n seciunea 4.2 furnizeaz dimensiunea n
octei a argumentului su.
n cazul nostru, numrul cuvintelor cheie este dimensiunea masivului
keytab mprit la dimensiunea unui element de masiv. Acest calcul este
fcut ntr-o linie #dene pentru a da o valoare identicatorului NKEYS:
#dene NKEYS (sizeof (keytab) /
Sizeof (struct key)

S revenim acum la funcia getword. Programul pe care-l vom da


pentru aceast funcie este mai general dect este necesar n aplicaia
noastr, dar nu este mult mai complicat.
Funcia getword citete cuvntul urmtor din textul de intrare, printr-un
cuvnt nelegndu-se e un ir de litere i cifre, cu primul caracter liter, e
un singur caracter. Funcia returneaz constanta simbolic LETTER dac a
gsit un cuvnt, EOF dac a detectat sfritul ierului sau caracterul nsui,
dac el nu a fost alfabetic.
Getword (char *w, int lim) {/* citete un cuvnt */
Int t;
While (-lim>0) {
T = type (*w+=getchar ();
If (t=EOF)
Return EOF;
If (t! =LETTER & t! =DIGIT)
Break;
}
*(w-l) = ;
Return LETTER;
}
Funcia getword apeleaz funcia type pentru identicarea tipului
ecrui caracter citit la intrare cu ajutorul funciei getchar. Versiunea funciei
type pentru caractere ASCII este:
Type (int c) {/* returneaz tipul caracterului */
If (c>=a & c<=z || c>=A & c<=Z)
Return LETTER;
If (c>=0 & c<=9)
Return DIGIT;
Return c;
}
Constantele simbolice LETTER i DIGIT pot avea orice valoare care nu
vine n contradicie cu caracterele nealfabetice i EOF. Valori posibile pot :
#dene LETTER a
#dene DIGIT 0 10.4. Pointeri la structuri
Pentru a ilustra modul de corelare dintre pointeri i masivele de
structuri, s rescriem programul de numrare a cuvintelor cheie dintr-un text,
de data aceasta folosind pointeri, n loc de indici de masiv.
Declaraia extern a masivului de structuri keytab nu necesit
modicri, n timp ce funciile main i binary da. Prezentm, n continuare,
aceste funcii modicate.
#dene MAXWORD 20
Struct key *binary (char *word, struct key
Tab [], int n) {
/*caut cuvnt */
Int cond;
Struct key * low;

Struct key *high;


Struct key *mid;
Low = &tab [0];
High = &tab [n-l];
While (low<=high) {
Mid = low + (high-low) /2;
If (cond=strcmp (word, mid->keyword) <0)
High = mid 1;
Else if (cond>0)
Low = mid + 1;
Else return mid;
}
Return NULL;
}
Main () {/* numr cuvintele cheie, versiune cu pointeri */
Int t;
Char word [MAXWORD];
Struct key *binary (), *p;
While (t=getword (word. MAXWORD)! =EOF)
If (t=LETTER)
If (p=binary (word, keytab, NKEYS)
! = NULL)
P->keycount+;
For (p=keytab; p<keytab+NKEYS; p+)
If (p->keycount>0)
Printf (%4d %s, p->keycount,
P->keyword);
}
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 key, n loc de un ntreg. Acest
lucru este declarat att n funcia principal main ct i n funcia binary.
Dac binary gsete un cuvnt n structura key, atunci returneaz un pointer
la el; dac nu-l gsete, returneaz NULL. n funcie de aceste dou valori
returnate, funcia main semnaleaz gsirea cuvntului prin incrementarea
cmpului keycount corespunztor cuvntului sau citete urmtorul cuvnt.
n al doilea rnd, toate operaiile de acces la elementele masivului de
structuri keytab se fac prin intermediul pointerilor. Acest lucru determin o
modicare semnicativ n funcia binary. Calculul elementului mijlociu nu se
mai poate face simplu prin:
Mid = (low + high) /2
Deoarece adunarea a doi pointeri este o operaie ilegal, nedenit.
Aceast instruciune trebuie modicat n:
Mid = low + (high-low) /2
Care face ca mid s pointeze elementul de la jumtatea distanei dintre
low i high.

S mai observm iniializarea pointerilor low i high, care este perfect


legal, deoarece este posibil iniializarea unui pointer cu o adres a unui
element deja denit.
n funcia main avem urmtorul ciclu:
For (p=keytab; p<keytab+NKEYS; p+).
Dac p este un pointer la un masiv de structuri, orice operaie asupra
lui p ine cont de dimensiunea unei structuri, astfel nct p+ incrementeaz
pointerul p la urmtoarea structur din masiv, adunnd la p dimensiunea
corespunztoare a unei structuri. Acest lucru nu nseamn c dimensiunea
structurii este egal cu suma dimensiunilor membrilor ei deoarece din cerine
de aliniere a unor membri se pot genera goluri ntr-o structur.
n sfrit, cnd o funcie returneaz un tip complicat i are o list
complicat de argumente, ca n:
Struct key *binary (char *word, struct key
Tab, int n)
Funcia poate mai greu vizibil i detectabil cu un editor de texte.
Din acest motiv, se poate opta i pentru urmtoarea form:
Struct key *binary (word, tab, n)
Char *word; struct key tab; int n;
Unde nainte de acolada de deschidere se precizeaz tipul ecrui
parametru.
Alegei forma care v convine i care vi se pare mai sugestiv.
10.5. Structuri auto-referite
S considerm problema mai general, a numrului apariiilor tuturor
cuvintelor dintr-un text de intrare. Deoarece lista cuvintelor nu este
cunoscut dinainte, n-o putem sorta folosind algoritmul de cutare binar, ca
n paragraful precedent. Nu putem utiliza nici o cutare liniar pentru ecare
cuvnt, pe msura apariiei lui pentru a vedea dac a mai fost prezent sau
nu, pentru c timpul de execuie al programelor ar crete ptratic cu numrul
cuvintelor de la intrare.
Un mod de a organiza datele pentru a lucra ecient cu o list de
cuvinte arbitrare este de a pstra mulimea de cuvinte, tot timpul sortat,
plasnd ecare 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 ecient a acestei probleme vom folosi o structur de date
numit arbore binar.
Fiecare nod al arborelui va reprezenta un cuvnt distinct din intrare i
va conine urmtoarea informaie:
Un pointer la cuvnt;
Un contor pentru numrul de apariii;
Un pointer la descendentul stng al cuvntului;
Un pointer la descendentul drept al cuvntului. Nici un nod al
arborelui nu va avea mai mult dect doi descendeni dar poate avea un
descendent sau chiar niciunul.

Arborele se construiete astfel nct pentru orice nod, sub-arborele


stng al su conine numai cuvintele care sunt mai mici dect cuvntul din
nod, iar sub-arborele drept conine numai cuvinte, care sunt mai mari dect
cuvntul din nod, compararea fcndu-se din punct de vedere lexicograc.
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 inserat pe poziia
descendentului corespunztor. Se observ c acest proces de cutare este
recursiv, deoarece cutarea din ecare nod utilizeaz o cutare ntr-unul
dintre descendenii si.
Prin urmare se impune de la sine ca rutinele de inserare n arbore i de
imprimare s e recursive.
Revenind la descrierea unui nod, el apare ca ind o structur cu patru
componente:
Struct tnode {/* nodul de baz */
Char *word; /* pointer la cuvnt */
Int count; /* numrtor de apariii */
Struct tnode *left; /* descendent stng */
Struct tnode *right; /* descendent drept */
};
Aceast declaraie recursiv a unui nod este perfect legal, deoarece
o structur nu poate conine ca i component o intrare a ei nsi dar poate
conine un pointer la o structur de acelai ablon cu ea.
Declaraia:
Struct tnode *left;
Declar pe left ca ind un pointer la structur (nod) i nu o structur
nsi.
n program vom folosi rutinele getword, pentru citirea unui cuvnt din
intrare, alloc pentru rezervarea de spaiu necesar memorrii unui cuvnt i
alte cteva rutine pe care le cunoatem deja. Rutina principal main citete
prin intermediul rutinei getword un cuvnt i l plaseaz n arbore prin rutina
tree.
#dene MAXWORD 20
Main () {/* contorizare apariii cuvinte */
Struct tnode *root, *tree ();
Char word [MAXWORD];
Int t;
Root = NULL;
While (t=getword (word, MAXWORD)! =EOF)
If (t=LETTER)

Root = tree (root, word);


Treeprint (root);
}
Rutina main gestioneaz ecare cuvnt din intrare ncepnd cu cel mai
nalt nivel al arborelui (rdcina). La ecare pas, cuvntul din intrare este
comparat cu cuvntul asociat rdcinii i este apoi transmis n jos, e
descendentului stng, e celui drept, printr-un apel recursiv la rutina tree. n
acest proces, cuvntul e exist deja, undeva n arbore, caz n care contorul
lui de numrare a apariiilor se incrementeaz, e cutarea continu pn la
ntlnirea unui pointer NULL, caz n care nodul trebuie creat i adugat
arborelui. Cnd se creeaz un nod nou, rutina tree returneaz un pointer la
el, care apoi este introdus n nodul de origine (adic n nodul al crui
descendent este noul nod) n cmpul left sau right dup cum noul cuvnt
este mai mic sau mai mare fa de cuvntul origine. Rutina tree, care
returneaz un pointer la o structur de ablon tnode are urmtorul cod:
Struct tnode *tree (struct tnode *p,
Char *w) {/* introduce cuvntul w n nodul p */
Struct tnode *talloc (int n);
Char *strsav (char *s);
Int cond;
If (p=NULL) {/* a sosit un nou cuvnt */
P = talloc (); /* creeaz un nod nou */
P->word = strsav (w);
P->count = 1;
P->left = p->right = NULL;
}
Else
If (cond=strcmp (w, p->word) =0)
P->count+;
Else
If (cond<0)/* noul cuvnt mai mic */
P->left = tree (p->left, w);
Else/* noul cuvnt mai mare */
P->right = tree (p->right, w);
Return p;
}
Memoria pentru noul nod se aloc de ctre rutina talloc, care este o
adaptare a rutinei alloc, pe care am vzut-o deja. Ea returneaz un pointer la
un spaiu liber, n care se poate nscrie noul nod al arborelui. Vom discuta
rutina talloc mai trziu. Noul cuvnt se copiaz n acest spaiu cu ajutorul
rutinei strsav, care returneaz un pointer la nceputul cuvntului, contorul de
apariii se iniializeaz la 1 i pointerii ctre cei doi descendeni se fac NULL.
Aceast parte de cod se execut numai cnd se adaug un nou nod.
Rutina treeprint tiprete arborele astfel nct pentru ecare nod se
imprim sub-arborele lui stng, adic toate cuvintele mai mici dect cuvntul
curent, apoi cuvntul curent i la sfrit sub-arborele drept, adic toate

cuvintele mai mari dect cuvntul curent. Rutina treeprint este una din cele
mai tipice rutine recursive.
Treeprint (struct tnode *p) {
/*tiprete arborele p recursiv */
If (p! =NULL) {
Treeprint (p->left);
Printf (%5d %s, p->count, p->word);
Treeprint (p->right);
}
}
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 ecare ieire din rutina tree, din cauza recursivitii, se
parcurge acelai drum, de data aceasta de la nodul gsit spre rdcina
arborelui, refcndu-se toi pointerii drumului parcurs.
Dac considerai ca nu ai neles sucient de bine recursivitatea,
desenai-v un arbore i imprimai-l cu ajutorul rutinei treeprint, avnd grij
s memorai ecare ieire din tree i treeprint.
O observaie legat de acest exemplu: dac arborele este nebalansat
, adic cuvintele nu sosesc n ordine aleatoare din punct de vedere
lexicograc, atunci timpul de execuie al programului poate deveni foarte
mare. Cazul limit n acest sens este acela n care cuvintele de la intrare sunt
deja n ordine, (cresctoare sau descresctoare), caz n care programul
nostru simuleaz o cutare liniar ntr-un mod destul de costisitor.
S ne oprim puin asupra alocrii de memorie. Cu toate c se aloc
diferite tipuri de obiecte, este de preferat s existe un singur alocator de
memorie ntr-un program. Relativ la acest alocator de memorie se pun doua
probleme: n primul rnd cum poate satisface el condiiile de aliniere ale
obiectelor de un anumit tip (de exemplu ntregii trebuie alocai la adrese
pare); n al doilea rnd cum se poate declara c alocatorul returneaz
pointeri la tipuri diferite de obiecte.
Cerinele de aliniere pot n general rezolvate cu uurin pe seama
unui spaiu care se pierde, dar care este nesemnicativ ca dimensiune. De
exemplu, alocatorul alloc returneaz totdeauna un pointer la o adres par.
n cazul n care cererea de alocare poate satisfcut i de o adres impar
(pentru iruri de caractere, de exemplu) se pierde un caracter.
n ceea ce privete declararea tipului alocatorului alloc (adic a tipului
de obiect pe care l indic pointerul returnat de alloc), un foarte bun procedeu
n limbajul C este de a declara c funcia alloc returneaz un pointer la char i
apoi s convertim explicit acest pointer la tipul dorit printr-un cast. Astfel
dac p este declarat n forma:
Char *p;
Atunci: (struct tnode *) p;

Convertete pe p dintr-un pointer la char ntr-un pointer la o structur


de ablon tnode, dac el apare ntr-o expresie. i atunci, o versiune a
alocatorului talloc poate urmtoarea:
Struct tnode *talloc () {
Char *alloc ();
Return (struct tnode *)alloc (sizeof (struct tnode);
} 10.6. Cutare n tabele
O alt problem legat de denirea i utilizarea structurilor este
cutarea n tabele. Cnd se ntlnete de exemplu, o linie de forma:
#dene YES 1
Simbolul YES i textul de substituie 1 se memoreaz ntr-o tabel. Mai
trziu, ori de cte ori textul YES va aprea n instruciuni, el se va nlocui cu
constanta 1.
Crearea i gestionarea tabelelor de simboluri este o problem de baz
n procesul de compilare. Exist dou rutine principale care gestioneaz
simbolurile i textele lor de substituie. Prima, install (s, t) nregistreaz
simbolul s i textul de substituie t ntr-o tabel, s i t ind iruri de caractere.
A doua, lookup (s) caut irul s n tabel i returneaz e un pointer la locul
unde a fost gsit, e NULL dac irul s nu gureaz n tabel.
Algoritmul folosit pentru crearea i gestionarea tabelei de simboluri
este o cutare pe baz de hashing. Fiecrui simbol i se calculeaz un cod
hash astfel: se adun codurile ASCII ale caracterelor simbolului i se ia restul
provenit din mprirea numrului obinut din adunare i dimensiunea
tabelului. Astfel, ecrui simbol i se asociaz un cod hash H care veric
relaia: 0<=H<0x100 (n hexazecimal)
Codul hash astfel obinut va folosit apoi ca un indice ntr-o tabel de
pointeri. Un element al acestei tabele (masiv) indic nceputul unui lan de
blocuri care descriu simboluri cu acelai cod hash. Dac un element al tabelei
este NULL nseamn c nici un simbol nu are valoarea respectiv de hashing.
Un bloc dintr-un lan indicat de un element al tabelei este o structur
care conine un pointer la simbol, un pointer la textul de substituie i un
pointer la urmtorul bloc din lan. Un pointer NULL la urmtorul bloc din lan
indic sfritul lanului.
ablonul unei structuri (nod) este urmtorul:
Struct nlist {
Char *name;
Char *def;
Struct nlist *next; /* urmtoarea intrare n lan */
};
Tabelul de pointeri care indic nceputurile lanului de blocuri ce descriu
simboluri de acelai cod hash este:
#dene HASHSIZE 0x100
Static struct nlist *hashtab [HASHSIZE];
Algoritmul de hashing pe care-l prezentm nu este cel mai bun posibil,
dar are meritul de a extrem de simplu:
Hash (char *s) {

/* formeaz valoarea hash pentru irul s */


Int hashval;
For (hashval=0; *s! =;)
Hashval += *s+;
Return hashval % HASHSIZE;
}
Algoritmul de hashing produce un indice n masivul de pointeri hashtab.
n procesul de cutare a unui simbol, dac el exist, el trebuie s e n lanul
de blocuri care ncepe la adresa coninut de elementul din hashtab cu
indicele respectiv.
Cutarea n tabela de simboluri hashtab se realizeaz cu funcia lookup.
Dac simbolul cutat este prezent undeva n lan, funcia returneaz un
pointer la el; altfel returneaz NULL.
Struct nlist *lookup (char *s) {
/* caut irul s n hashtab */
Struct nlist *np;
For (np=hashtab [hash (s)]; np! =NULL;
Np=np->next)
If (strcmp (s, np->name) =0)
Return np; /* s-a gsit s */
Return NULL; /* nu s-a gsit s */
}
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
deniie anterioar pentru acest simbol, ea trebuie nlocuit cu deniia 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 sucient spaiu pentru crearea unui bloc unu.
Struct nlist *install (char *name, char
*def) {/* scrie (nume, def) n htab */
Struct nlist *np, *lookup ();
Char *strsav (), *alloc ();
Int hashval;
If (np=lookup (name) =NULL) {/* nu s-a gsit */
Np = (struct nlist*) alloc (sizeof (*np);
If (np=NULL)
Return NULL; /* nu exist spaiu */
If (np->name=strsav (name) =NULL)
Return NULL;
Hashval = hash (np->name);
Np->next = hashtab [hashval];
Hashtab [hashval] = np;
}
Else/* nodul exist deja */
Free (np->def); /* elibereaz deniia veche */
If (np->def=strsav (def) =NULL)

Return NULL;
Return np;
}
Deoarece apelurile la funciile alloc i free pot aprea n orice ordine i
deoarece alinierea conteaz, versiunea simpl a funciei alloc, prezentat n
capitolul 9 nu este adecvat aici. n biblioteca standard exist funcii de
alocare fr restricii, care se apeleaz implicit sau explicit de ctre utilizator
dintr-un program scris n C pentru a obine spaiul de memorie necesar.
Deoarece i alte aciuni dintr-un program pot cere spaiu de memorie ntr-o
manier asincron, spaiul de memorie gestionat de funcia alloc poate s e
necontiguu. Astfel, spaiul liber de memorie este pstrat sub forma unui lan
de blocuri libere, ecare bloc coninnd o dimensiune, un pointer la urmtorul
bloc i spaiul liber propriu-zis. Blocurile sunt pstrate n ordinea cresctoare
a adreselor iar, ultimul bloc, de adresa cea mai mare, indic primul bloc, prin
pointerul lui la blocul urmtor din lan, astfel nct lanul este circular.
Cnd se lanseaz o cerere, se examineaz lista spaiului liber, pn se
gsete un bloc sucient de mare pentru cererea respectiv. Dac blocul are
exact dimensiunea cerut, el se elibereaz din lanul blocurilor libere i este
returnat utilizatorului. Dac blocul este mai mare se descompune, astfel nct
partea cerut se transmite utilizatorului, iar partea rmas se introduce
napoi n lista de spaiu liber. Dac nu se gsete un bloc sucient de mare
pentru cererea lansat se caut un alt bloc de memorie.
Eliberarea unei zone de memorie prin intermediul rutinei free cauzeaz,
de asemenea, o cutare n lista de spaiu liber, pentru a gsi locul
corespunztor de inserare a blocului de memorie eliberat. Dac blocul de
memorie eliberat este adiacent cu un bloc din lista de spaiu liber la orice
parte a sa, el este alipit la acel bloc, crendu-se un bloc mai mare, astfel ca
memoria s nu devin prea fragmentat. Determinarea adiacenei este
uurat de faptul c lista de spaiu liber se pstreaz n ordinea cresctoare
a adreselor de memorie.
Exemplul de utilizare a acestor funcii iniializeaz elementele
masivului hashtab cu NULL. n continuare se ateapt de la tastatur
introducerea unui nume i a unei deniii pentru acest nume. Dac numele
introdus nu exist n lista hashtab atunci se aeaz un mesaj corespunztor,
altfel se aeaz vechea deniie care este apoi nlocuit de noua deniie
introdus.
Main () {
Char num [30], def [30];
Int i;
Struct nlist *np;
For (i=0; i<HASHSIZE; i+)
Hashtab [i] = NULL;
Do {
Getword (num); getword (def);
If (np=lookup (num) =NULL)
Printf (New name);

Else
Printf (Old denition: %s,
Np->def);
Install (num, def);
} while (1);
} 10.7. Cmpuri
Un cmp se denete ca ind o mulime de bii consecutivi dintr-un
cuvnt sau ntreg. Adic din motive de economie a spaiului de memorie, este
util mpachetarea unor obiecte ntr-un singur cuvnt main. Un caz
frecvent de acest tip este utilizarea unui set de aguri, ecare pe un bit,
pentru tabela de simboluri a unui compilator.
Fiecare simbol dintr-un program are anumite informaii asociate lui,
cum sunt de exemplu, clasa de memorie, tipul, dac este sau nu cuvnt cheie
.a.m.d. Cel mai compact mod de a codica aceste informaii este folosirea
unui set de aguri, de cte un bit, ntr-un singur ntreg sau caracter.
Modul cel mai uzual pentru a face acest lucru este de a deni un set de
mti, ecare masc ind corespunztoare poziiei bitului m interiorul
caracterului sau cuvntului. De exemplu:
#dene KEYWORD 01
#dene EXTERNAL 02
#dene STATIC 04
Denesc 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 e puteri ale lui 2.
Expresii de forma:
Flags | = EXTERNAL | STATIC;
Apar frecvent i ele seteaz biii 1 i 2 din caracterul sau ntregul ags
(n exemplul nostru)
n timp ce expresia:
Flags &= (EXTERNAL | STATIC);
Selecteaz biii 1 i 2 din ags.
Expresia:
If (ags & (EXTERNAL | STATIC).
Este adevrat cnd cel puin unul din biii 1 sau 2 din ags este unu.
Expresia:
If (! (ags & (EXTERNAL | STATIC).
Este adevrat cnd biii 1 i 2 din ags sunt ambii zero.
Limbajul C ofer aceste expresii, ca o alternativ, pentru posibilitatea
de a deni i de a accesa biii dintr-un cuvnt, n mod direct, folosind
operatorii logici pe bii.
Sintaxa deniiei cmpului i a accesului la el se bazeaz pe structuri.
De exemplu construciile #dene din exemplul de mai sus pot nlocuite prin
denirea a trei cmpuri:
Struct {
Unsigned is_keyword: 1;

Unsigned is_external:1;
Unsigned is_static: 1;
} ags;
Aceast construcie denete variabila ags care conine 3 cmpuri,
ecare de cte un bit. Numrul care urmeaz dup : reprezint lungimea
cmpului n bii. Cmpurile sunt declarate unsigned pentru a sublinia c ele
sunt cantiti fr semn. Pentru a ne referi la un cmp individual din variabila
ags 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
scrise mai natural sub forma urmtoare:
Flags.is_extern = ags.is_static = 1;
Pentru setarea biilor 1 i 2 din variabila ags,
Flags.is_extern = ags.is_static = 0;
Pentru tergerea biilor, iar:
If (ags.is_extern=0 &
Flags.is_static=0)
Pentru testarea lor.
Un cmp nu trebuie s depeasc limitele unui cuvnt. n caz contrar,
cmpul se aliniaz la limita urmtorului cuvnt. Cmpurile nu necesit s e
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 folosit pentru forarea alinierii
urmtorului cmp la limita unui nou cuvnt, el ind 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 mai lung dect un
cuvnt. Cmpurile se atribuie de la dreapta la stnga.
Cmpurile nu pot constitui masive, nu au adrese, astfel nct operatorul
& nu se poate aplica asupra lor.
10.8. Reuniuni
O reuniune este o variabil care poate conine, la momente diferite,
obiecte de diferite tipuri i dimensiuni; compilatorul este cel care ine
evidena dimensiunilor i aliniamentului.
Reuniunile ofer posibilitatea ca mai multe tipuri diferite de date s e
tratate ntr-o singur zon de memorie, fr a folosi n program vreo
informaie dependent de main.
S relum exemplul tabelei de simboluri a unui compilator,
presupunnd c constantele pot de tip int, oat sau iruri de caractere.
Valoarea unei constante particulare trebuie memorat ntr-o variabil
de tip corespunztor, cu toate c este mai convenabil, pentru gestiunea
tabelei de simboluri, ca valoarea s e memorat n aceeai zon de
memorie, indiferent de tipul ei i s ocupe aceeai cantitate de memorie.
Acesta este scopul unei reuniuni: de a furniza o singur variabil care s
poat conine oricare dintre valorile unor tipuri de date. Ca i n cazul

cmpurilor, sintaxa deniiei i accesului la o reuniune se bazeaz pe


structuri. Fie deniia:
Union u_tag. {int ival;
Float fval;
Char *pval;
} uval;
Variabila uval va sucient de mare ca s poat pstra pe cea mai
mare dintre cele trei tipuri de componente. Oricare dintre tipurile de mai sus
poate 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 sunt accesibili printr-o construcie de
forma:
Nume-reuniune. Membru
Sau
Pointer-la-reuniune->membru
Dac variabila utype este utilizat pentru a ine evidena tipului curent
memorat n uval, atunci e urmtorul cod:
If (utype=INT)
Printf (%d, uval. Ival);
Else if (utype= FLOAT)
Printf (%f, uval. Fval);
Else if (utype=STRING)
Printf (%s, uval. Pval);
Else
Printf (tip incorect %d n utype,
Utype);
Reuniunile pot aprea n structuri i masive i invers. Sintaxa pentru
accesarea unui membru al unei reuniuni, dintr-o structur, sau invers este
identic cu cea pentru structurile imbricate. Pe exemplu, n masivul de
structuri symtab [NSYM] denit de:
Struct {
Char * name;
Int ags;
Int 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 pointat de pval prin:
*symtab [i]. Uval. Pval

De fapt, o reuniune este o structur n care toi membrii au


deplasamentul zero, structura ind sucient de mare pentru a putea pstra
pe cel mai mare membru. Alinierea este corespunztoare pentru toate
tipurile reuniunii. Ca i la structuri, singurele operaii permise cu reuniuni sunt
accesul la un membru al reuniunii i considerarea adresei ei.
Reuniunile nu pot atribuite, transmise la funcii sau returnate de ctre
acestea. Pointerii la reuniuni pot folosii n mod similar cu pointerii la
structuri.
10.9. Declaraii de structuri, reuniuni i cmpuri
Deoarece specicatorii de structuri, reuniuni i cmpuri au aceeai
form vom prezenta sintaxa lor general n acest paragraf.
Specicator-structur-sau-reuniune:
Struct-sau-union {lista-declaraiilor}
Struct-sau-unionidenticator{lista-declaraiilor}
Struct-sau-unionidenticator
Struct-sau-union:
Struct
Union
Lista-declaraiilor este o secven de declaraii pentru membrii
structurii sau reuniunii.
Lista-declaraiilor:
Declaraie-structur
Declaraie-structur, lista-declaraiilor
Declaraie-structur:
Specicator-tip, lista-declarator;
Lista-declarator:
Declarator-structur
Declarator-structur, lista-declarator
n mod obinuit, un declarator-structur este chiar un declarator pentru
un membru al structurii sau reuniunii. Un membru al structurii poate
constituit dintr-un numr specicat de bii, caz n care avem de-a face cu un
cmp. Lungimea lui se separ de nume prin caracterul : Atunci:
Declarator-structur:
Declarator
Declarator: expresie-constant
: expresie-constant
ntr-o structur ecare membru care nu este un cmp ncepe la o
adres corespunztoare tipului su. Astfel ntr-o structur pot exista zone fr
nume neutilizate, rezultate din motive de aliniere.
Limbajul C nu introduce restricii privind tipurile obiectelor care pot
declarate cmpuri.
Un specicator-structur-sau-reuniune de forma a doua declar un
identicator ca ind eticheta (marcajul) structurii sau reuniunii. Atunci o
declaraie ulterioar poate folosi forma a treia a unui specicator-structursau-reuniune.

Etichetele de structuri permit denirea structurilor auto-referite; de


asemenea permit ca partea de declaraie a corpului structurii s e dat o
singur dat i folosit de mai multe ori. Este interzis declararea recursiv a
unei structuri sau reuniuni, dar o structur sau o reuniune poate conine un
pointer la ea.
Dou structuri pot partaja o secven iniial comun de membri; adic
acelai membru poate aprea n dou structuri diferite dac el are acelai tip
n ambele structuri i dac toi membri precedeni lui sunt identici n cele
dou structuri.
10.10. Typedef
Limbajul C ofer o facilitate numit typedef pentru a crea noi nume de
tipuri de date. Specicatorul de tip typedef-nume are sintaxa:
Typedef-nume:
Declarator
ntr-o declaraie implicnd typedef ecare identicator care apare ca
parte a unui declarator devine sintactic echivalent cu cuvntul cheie rezervat
pentru tipul asociat cu identicatorul. De exemplu, declaraia:
Typedef int LENGTH;
l face pe LENGTH sinonim cu int. Tipul LENGTH poate folosit ulterior
n declaraii n acelai mod ca i tipul int.
LENGTH len, maxlen;
LENGTH *length [];
n mod similar, declaraia:
Typedef char *STRING;
l face pe STRING sinonim cu char*, adic pointer la caracter, care apoi
poate utilizat n declaraii de tipul:
STRING p, lineptr [LINES], alloc ();
Se observ c tipul care se declar prin typedef apare pe poziia
numelui de variabil nu imediat dup cuvntul rezervat typedef. Sintactic
typedef este sinonim cu clasele de memorie extern, static etc, dar nu rezerv
memorie pentru variabilele respective.
Ca un exemplu mai complicat s relum declaraia unui nod al unui
arbore, de data aceasta folosind typedef pentru a crea un nou nume pentru
un tip structur (vezi seciunea 10.5).
Typedef struct tnode {
Char *word; /* pointer la text */
Int count; /* numr apariii */
Struct tnode *left; /* descendent stng */
Struct tnode *right; /* descendent drept */
} TREENODE, *TREEPTR;
Aceast declaraie creeaz dou nume noi de tipuri, numite TREENODE,
care este o structur i TREEPTR, care este un pointer la o structur. Atunci
rutina talloc poate scris sub forma:
TREEPTR talloc () {
Char *alloc ();
Return (TREEPTR) alloc (sizeof (TREENODE);

}
Trebuie subliniat faptul c declaraia typedef nu creeaz noi tipuri n
nici un caz; ea adaug doar sinonime pentru anumite tipuri de date, deja
existente. Variabilele declarate n acest fel au exact aceleai proprieti ca i
cele declarate explicit. De fapt, typedef se aseamn cu #dene, cu excepia
faptului c n timp ce #dene este tratat de preprocesor, typedef este tratat
de ctre compilator. De exemplu:
Typedef int (*PFI)();
Creeaz numele PFI pentru pointer la o funcie care returneaz un
ntreg, tip care poate folosit ulterior ntr-un context de tipul:
PFI strcmp, numcmp, swap;
n programul de sortare din capitolul 9.
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 sunt dependente de main, atunci pentru
o compilare pe un alt sistem de calcul este necesar modicarea doar a
acestor declaraii nu i a datelor din program.
Al doilea const n faptul c prin crearea de noi nume de tipuri se ofer
posibilitatea folosirii unor nume mai sugestive n program, deci o mai rapid
nelegere a programului.
11. Intrri/ieiri
ntruct limbajul C nu a fost dezvoltat pentru un sistem particular de
operare i datorit faptului c s-a dorit realizarea unei portabiliti ct mai
mari, att a unui compilator C, ct i a programelor scrise n acest limbaj, el
nu posed faciliti de intrare/ieire.
Exist totui un sistem de intrare/ieire (sistemul I/O) constituit dintr-un
numr de subprograme care realizeaz funcii de intrare/ieire pentru
programe scrise n C, dar care nu fac parte din limbajul C. Aceste
subprograme se gsesc n biblioteca C.
Scopul acestui capitol este de a descrie cele mai utilizate subprograme
de intrare/ieire i interfaa lor cu programele scrise n limbajul C.
11.1. Intrri i ieiri standard; iere
Sistemul I/O ofer utilizatorului trei iere standard de lucru. Cuvntul
ier a fost pus ntre ghilimele, deoarece limbajul nu denete acest tip de
dat i pentru c ierele reprezint mai degrab nite uxuri de intrare/
ieire standard puse la dispoziia utilizatorului. Aceste iere sunt:
Fiierul standard de intrare (stdin);
Fiierul standard de ieire (stdout);
Fiierul standard de aare a mesajelor (stderr).
Toate aceste trei iere sunt secveniale i n momentul execuiei unui
program C sunt implicit denite i deschise.
Stdin i stdout sunt asociate n mod normal terminalului de la care a
fost lansat programul n execuie. Sistemul I/O permite redirectarea acestor
iere pe alte periferice sau nchiderea lor dup lansarea programului.
Redirectarea ierului stdin se specic prin construcia: <specicator-ier
n linia de comand prin care a fost lansat programul.

Redirectarea ierului stdout se specic prin construcia:


>specicator-ier
n linia de comand prin care a fost lansat programul.
Redirectarea ierului stdout pe un alt periferic, n scopul efecturii
unei operaii de adugare (append) se specic prin construcia:
>specicator-ier
Stderr este ntotdeauna asociat terminalului de la care a fost lansat
programul n execuie i nu poate redirectat.
Pentru a se putea face o referire la aceste iere orice program C
trebuie s conin ierul stdio. H, care se include printr-o linie de forma:
#include <stdio. H>
Dac acest ier se a n biblioteca standard.
Pentru claritatea i lizibilitatea programelor scrise n C, ct i pentru
crearea unei imagini sugestive asupra lucrului cu iere, n ierul de deniii
standard stdio. H s-a denit un nou nume de tip de dat i anume FILE care
este o structur. Pentru a referi un ier, este necesar o declaraie de forma:
FILE *fp;
Unde fp va numele de dat cu care se va referi ierul n orice
operaie de intrare/ieire asociat. Iat cteva informaii pstrate de
structura FILE:
Un identicator de ier pe care sistemul de operare l asociaz
uxului pe durata prelucrrii; acesta poate aat cu ajutorul funciei leno;
Adresele zonelor tampon asociate; poziia curent n aceste zone;
Indicatorii de sfrit de ier i de eroare;
Alte informaii.
11.2. Accesul la iere; deschidere i nchidere
Nume
Fopen deschide un ux
Declaraie
FILE *fopen (const char *path,
Const char *mode);
Descriere
Funcia fopen deschide ierul al crui nume este un ir indicat de path
i i asociaz un ux.
Argumentul mode indic un ir care ncepe cu una din secvenele
urmtoare:
Rdeschide un ier pentru citire;
R+deschide pentru citire i scriere;
Wtrunchiaz ierul la lungime zero sau creeaz un ier pentru
scriere;
W+deschide pentru adugare la sfrit, n citire i scriere; ierul este
creat dac nu exist, altfel este trunchiat;
Adeschide pentru adugare la sfrit, n scriere; ierul este creat dac
nu exist;
A+deschide pentru adugare la sfrit, n citire i scriere; ierul este
creat dac nu exist;

Dup deschidere, n primele patru cazuri indicatorul poziiei n ux este


la nceputul ierului, n ultimele dou la sfritul acestuia.
irul mode include de asemenea litera b (deschide un ier binar) sau t
(deschide un ier text) e pe ultima poziie e pe cea din mijloc.
Operaiile de citire i scriere pot alterna n cazul uxurilor 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 ier. Aceast operaie poate inefectiv cum ar
fseek (ux, 0L, SEEK_CUR) apelat cu scop de sincronizare.
Valori returnate
n caz de succes se returneaz un pointer de tip FILE. n caz de eroare
se returneaz NULL i variabila global ermo indic codul erorii.
Nume
Fclose nchide un ux
Declaraie
Int fclose (FILE *ux);
Descriere
Funcia fclose nchide ierul asociat uxului ux. Dac ux a fost
deschis pentru ieire, orice date aate n zone tampon sunt scrise n ier n
prealabil cu un apel ush.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz EOF i
variabila global ermo indic codul erorii.
Nume
Tmple creeaz un ier temporar
Declaraie
FILE *tmple ();
Descriere
Funcia tmple genereaz un nume unic de ier temporar. Acesta este
deschis n mod binar pentru scriere/citire (wb+). Fiierul va ters automat
la nchidere sau la terminarea programului.
Valoare returnat
Funcia returneaz un descriptor de ux n caz de succes, sau NULL
dac nu poate generat un nume unic de ier sau dac ierul nu poate
deschis. n caz de eroare variabila global ermo indic codul erorii.
Nume
Fush foreaz scrierea n ux
Declaraie
Int ush (FILE *ux);
Descriere
Funcia ush foreaz o scriere a tuturor datelor aate n zone tampon
ale uxului ux. Fluxul rmne deschis.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz EOF i
variabila global ermo indic codul erorii.

Nume
Fseek, ftell, rewind repoziioneaz un ux
Declaraie
Int fseek (FILE *ux, long oset,
Int reper);
Long ftell (FILE *ux);
Void rewind (FILE *ux);
Descriere
Funcia fseek seteaz indicatorul de poziie pentru ierul asociat
uxului ux. Noua poziie, dat n octei, se obine adunnd oset octei la
poziia specicat de reper. Dac reper este SEEK_SET, SEEK_CUR, sau
SEEK_END, oset este relativ la nceputul ierului, poziia curent a
indicatorului, respectiv sfritul ierului. Funcia fseek terge indicatorul de
sfrit de ier.
Funcia ftell obine valoarea curent a indicatorului de poziie pentru
ierul asociat uxului ux.
Funcia rewind poziioneaz indicatorul de poziie pentru ierul asociat
uxului ux la nceputul ierului. Este echivalent cu: (void) fseek (ux, 0L,
SEEK_SET)
Cu completarea c funcia rewind terge i indicatorul de eroare al
uxului.
Valori returnate
Funcia rewind nu returneaz nici o valoare. n caz de succes, fseek
returneaz 0 i ftell returneaz oset-ul curent. n caz de eroare se
returneaz EOF i variabila global ermo indic codul erorii.
11.3. Citire i scriere fr format
Nume
Fgets citete un ir de caractere dintr-un ux text
Declaraie
Char *fgets (char *s, int size, FILE *ux);
Descriere
Funcia fgets cel mult size-l caractere din ux i le memoreaz n zona
indicat de s. Citirea se oprete la detectarea sfritului de ier sau newline. Dac se citete caracterul new-line acesta este memorat n s. Dup
ultimul caracter se memoreaz null.
Apeluri ale acestei funcii pot combinate cu orice apeluri ale altor
funcii de intrare din bibliotec (fscanf, de exemplu) pentru un acelai ux de
intrare.
Valori returnate
Funcia returneaz adresa s n caz de succes, sau NULL n caz de eroare
sau la ntlnirea sfritului de ier dac nu s-a citit nici un caracter.
Nume
Fputs scrie un ir de caractere ntr-un ux text
Declaraie
Int fputs (const char *s, FILE *ux);
Descriere

Funcia fputs scrie irul s n ux fr caracterul terminator null.


Apeluri ale acestei funcii pot combinate cu orice apeluri ale altor
funcii de ieire din bibliotec (fprintf, de exemplu) pentru un acelai ux 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 uxuri binare
Declaraie
Unsigned fread (void *ptr, unsigned size,
Unsigned nel, FILE *ux);
Unsigned fwrite (const void *ptr, unsigned
Size, unsigned nel, FILE *ux);
Descriere
Funcia fread citete nel elemente, ecare avnd mrimea size octei,
din uxul indicat de ux i le memoreaz n zona indicat de ptr.
Funcia fwrite scrie nel elemente, ecare avnd mrimea size octei,
din uxul indicat de ux, 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
ier, valoarea returnat este mai mic dect nel (posibil zero).
11.4. Citire cu format
Nume
Scanf, fscanf, sscanf citire cu format
Declaraie
Int scanf (const char *format,.);
Int fscanf (FILE *ux, const char *format,
);
Int sscanf (const char *str, const char
*format,.);
Descriere
Familia de funcii scanf scaneaz intrarea n concordan cu irul de
caractere format dup cum se descrie mai jos. Acest format poate conine
specicatori de conversie; rezultatele unor astfel de conversii (dac se
efectueaz) se memoreaz prin intermediul argumentelor pointer. Funcia
scanf citete irul de intrare din uxul standard stdin, fscanf din ux i sscanf
din irul indicat de str.
Fiecare argument pointer trebuie s corespund n ordine ca tip cu
ecare specicator de conversie (dar a se vedea suprimarea mai jos). Dac
argumentele nu sunt suciente comportamentul programului este
imprevizibil. Toate conversiile sunt 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 niciunul) 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.
HConversia este de tip dioux sau n i argumentul asociat este un
pointer la short (n loc de int).
LConversia este de tip dioux sau n i argumentul asociat este un
pointer la long (n loc de int), sau conversia este de tip efg i argumentul
asociat este un pointer la double (n loc de oat).
LConversia este de tip efg 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 innit (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 sunt contorizate n mrimea cmpului.
Sunt 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.
DPotrivire cu un ntreg zecimal (eventual cu semn); argumentul asociat
trebuie s e un pointer la int.
IPotrivire cu un ntreg (eventual cu semn); argumentul asociat trebuie
s e 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. Sunt
folosite numai caracterele care corespund bazei respective.
OPotrivire cu un ntreg octal fr semn; argumentul asociat trebuie s
e un pointer la unsigned.
UPotrivire cu un ntreg zecimal fr semn; argumentul asociat trebuie
s e un pointer la unsigned.
XPotrivire cu un ntreg hexazecimal fr semn; argumentul asociat
trebuie s e un pointer la unsigned.
FPotrivire cu un numr n virgul mobil (eventual cu semn);
argumentul asociat trebuie s e un pointer la oat.
E, gEchivalent cu f.
SPotrivire cu o secven de caractere diferite de spaiu alb; argumentul
asociat trebuie s e un pointer la char i zona trebuie s e sucient 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).
CPotrivire cu o secven de caractere de mrime w (dac aceasta este
specicat; prin lips se ia w=1); argumentul asociat trebuie s e un pointer
la char i zona trebuie s e sucient 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 specicat de
caractere acceptate; argumentul asociat trebuie s e un pointer la char i
zona trebuie s e sucient 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 format din caractere aate n (sau care nu se a n)
setul specicat n format; setul este denit de caracterele aate ntre [i].
Setul exclude acele caractere dac primul caracter dup [este ^. Pentru a
include caracterul] n set, acesta trebuie s e primul caracter dup [sau ^;
caracterul] aat n orice alt poziie nchide setul. Caracterul are i el un rol
special: plasat ntre dou alte caractere adaug toate celelalte caractere
aate n intervalul respectiv la set. Pentru a include caracterul acesta
trebuie s e ultimul caracter nainte de]. De exemplu, % [^]0-9-]
semnic setul orice caracter cu excepia], 0 pn la 9 i irul se termin la
apariia unui caracter care nu se a (sau, dac se precizeaz ^, care se a)
n set sau dac se atinge mrimea maxim specicat.
PPotrivire cu o valoare pointer (aa cum se aeaz cu %p n printf);
argumentul asociat trebuie s e un pointer la pointer.
NNu 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 e un pointer la int.
Valori returnate
Funciile returneaz numrul de valori atribuite, care poate 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 un caracter alfabetic pentru o conversie %d. Valoarea
EOF este returnat dac apare un eroare nainte de prima conversie, cum ar
detectarea sfritului de ier. Dac o eroare sau un sfrit de ier apare
dup ce o conversie a nceput, se returneaz numrul de conversii efectuate
cu succes.
11.5. Scriere cu format
Nume
Printf, fprintf, sprintf scriere cu format
Declaraie
Int printf (const char *format,.);
Int fprintf (FILE *ux, const char

*format,.);
Int sprintf (char *str, const char *format,
);
Descriere
Funciile din familia printf genereaz o ieire n concordan cu format
dup cum se descrie mai jos. Funcia printf aeaz ieirea la uxul standard
stdout; fprintf scrie ieirea la ux; sprintf scrie ieirea n irul de caractere str.
Aceste funcii genereaz ieirea sub controlul irului format care
specic cum se convertesc argumentele pentru ieire.
irul de formatare
irul format este un ir de caractere, printre care se pot aa zero sau
mai multe directive: caractere obinuite (diferite de %) care sunt copiate aa
cum sunt n uxul de ieire i specicaii de conversie, ecare dintre ele
rezultnd din ncrcarea a zero sau mai multe argumente. Fiecare specicaie
de conversie este introdus de caracterul % i se termin cu un specicator
de conversie. ntre acestea pot (n aceast ordine) zero sau mai muli
indicatori, o mrime minim a cmpului opional, o precizie opional i un
modicator opional de lungime.
Argumentele trebuie s corespund n ordine cu specicatorii de
conversie. Acestea sunt folosite n ordinea dat, unde ecare caracter * i
ecare specicator de conversie solicit urmtorul argument. Dac
argumentele nu sunt suciente comportamentul programului este
imprevizibil.
Caractere indicatori
Caracterul % este urmat de zero, unul sau mai muli indicatori:
#Valoarea numeric se convertete n format alternativ. Pentru
conversii de tip o, primul caracter al irului de ieire este zero (prin prexare
cu 0 dac valoarea nu este zero). Pentru conversii de tip x i X, o valoare
nenul este prexat cu 0x (sau 0X pentru conversii de tip X). Pentru
conversii de tip e, E, f, F, g i G, rezultatul va conine ntotdeauna punctul
zecimal, chiar dac nu apare partea fracionar (n mod normal punctul
zecimal apare n aceste conversii numai dac exist i partea fracionar).
Pentru conversii de tip g i G zerourile nale nu sunt eliminate aa cum se
procedeaz n mod normal. Pentru alte conversii rezultatul este nedenit.
0Valoarea 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 nedenit.
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 e completat la stnga cu blanc
sau zero. Dac apar indicatorii 0 i mpreun, indicatorul 0 este ignorat.
Sp(spaiu) n cazul unui rezultat al unei conversii cu semn, naintea unui
numr pozitiv sau ir vid se pune un blanc.

+Semnul (+ sau -) este plasat naintea numrului generat de o


conversie cu semn. Implicit semnul este folosit numai pentru numere
negative. Dac apar indicatorii + i Sp mpreun, indicatorul Sp este ignorat.
Limea cmpului
Un ir de cifre zecimale (cu prima cifr nenul) specic o lime
minim pentru cmp. Dac valoarea convertit are mai puine caractere
dect limea specicat, va completat cu spaii la stnga (sau dreapta,
dac s-a specicat aliniere la stnga). n locul unui numr zecimal se poate
folosi * pentru a specica faptul c limea cmpului este dat de argumentul
urmtor, care trebuie s e 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 specica
faptul c precizia este dat de argumentul urmtor, care trebuie s e de tip
int. Dac precizia este dat doar de., sau dac precizia este negativ, atunci
aceasta se consider zero. Precizia d numrul minim de cifre care apar
pentru conversii de tip d, i, o, u, x, X, numrul de cifre care apar dup punctul
zecimal pentru conversii de tip e, E, f, F, numrul maxim de cifre
semnicative pentru conversii de tip g i G, sau numrul maxim de caractere
generate pentru conversii de tip s.
Modicator de lungime
n acest caz prin conversie ntreag nelegem conversie de tip d, i, o,
u, x, X.
HConversia ntreag care urmeaz corespunde unui argument short
sau unsigned short, sau urmtoarea conversie de tip n corespunde unui
argument de tip pointer la short.
LConversia ntreag care urmeaz corespunde unui argument long sau
unsigned long, sau urmtoarea conversie de tip n corespunde unui argument
de tip pointer la long.
LUrmtoarea conversie de tip e, E, f, g sau G corespunde unui
argument long double.
Specicator de conversie
Un caracter care specic tipul conversiei care se va face. Specicatorii
de conversie i semnicaia lor sunt:
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 aat 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 aat cu precizie explicit 0, ieirea este vid.
E, E
Argumentul de tip otant este rotunjit i convertit n stil[-] d. dddedd
unde avem o cifr nainte de punctul zecimal i numrul de cifre dup acesta
este egal cu precizia; dac aceasta lipsete se consider 6; dac precizia este
zero, punctul zecimal nu apare. O conversie de tip E folosete litera E (n loc
de e) pentru a introduce exponentul. Exponentul are ntotdeauna cel puin
dou cifre; dac valoarea este zero, exponentul este 00.
F, F
Argumentul de tip otant este rotunjit i convertit n notaie zecimal n
stil [-] ddd. Ddd, unde numrul de cifre dup punctul zecimal este egal cu
precizia specicat. 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 otant este convertit n stil f sau e (sau E pentru
conversii de tip G). Precizia specic numrul de cifre semnicative. 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 nale sunt eliminate din partea
fracionar a rezultatului; punctul zecimal apare numai dac este urmat de
cel puin o cifr.
CArgumentul de tip int este convertit la unsigned char i se scrie
caracterul rezultat.
SArgumentul de tip const char * este un pointer la un ir de caractere.
Caracterele din ir sunt scrise pn la (fr a include) caracterul terminator
null; dac precizia este specicat, nu se scrie un numr mai mare dect cel
specicat. Dac precizia este dat, nu e nevoie de caracterul null; dac
precizia nu este specicat, sau dac este mai mare dect mrimea irului
irul trebuie s conin un caracter terminator null.
PArgumentul de tip pointer este scris n hexazecimal; formatul este
specic sistemului de calcul.
NNumrul de caractere scrise pn n acest moment este memorat la
argumentul de tip int *. Nu se face nici o conversie.
%Se scrie un caracter %. Nu se face nici o conversie. Specicaia
complet este %.
Valoare returnat
Funciile returneaz numrul de caractere generate (nu se include
caracterul terminator null pentru sprintf).
11.6. Tratarea erorilor

Nume
Perror aeaz un mesaj de eroare sistem
Declaraie
Void perror (const char *s);
#include <ermo. H>
Const char *sys_errlist [];
Int sys_nerr;
Descriere
Rutina perror aeaz un mesaj la ieirea standard de eroare, care
descrie ultima eroare ntlnit la ultimul apel sistem sau funcie de bibliotec.
Mai nti se aeaz argumentul s, apoi virgula i blanc i n nal 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 ermo.
Lista global de erori sys_errlist [] indexat cu ermo poate folosit
pentru a obine mesajul de eroare fr new-line. Ultimul indice de mesaj din
list este sys_nerr-l. 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 ermo indic codul erorii. Aceste
valori pot gsite n <ermo. H>. Funcia perror servete la aarea acestui
cod de eroare ntr-o form lizibil. Dac un apel terminat cu eroare nu este
imediat urmat de un apel perror, valoarea variabilei ermo se poate pierde
dac nu e salvat.
Nume
Clearerr, feof, ferror veric i reseteaz starea
Fluxului
Declaraie
Void clearerr (FILE *ux);
Int feof (FILE *ux);
Int ferror (FILE *ux);
Int leno (FILE *ux);
Descriere
Funcia clearerr terge indicatorii de sfrit de ier i eroare ai uxului.
Funcia feof testeaz indicatorul de sfrit de ier al uxului i
returneaz non-zero dac este setat. Acesta este setat dac o operaie de
citire a detectat sfritul de ier.
Funcia ferror testeaz indicatorul de eroare al uxului 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
ier i eroare, astfel c trebuie apelate funciile feof i ferror pentru a
determina cauza.
Funcia leno examineaz argumentul ux i returneaz descriptorul
asociat de sistemul de operare acestui ux.

Atenie! Este foarte frecvent folosirea incorect a funciei feof pentru


a testa dac s-a ajuns la sfritul ierului. Nu se recomand n nici un caz
acest stil de programare:
#dene LSIR 80
Char lin [LSIR];
FILE *, *fo;
Fi=fopen (nume-ier-lntrare,rt);
Fo=fopen (nume-ier-leire,wt);
While (! Feof () {/* greit! */
Fgets (lin, LSIR, );
Fputs (lin, fo);
}
Fclose (); fclose (fo);
n aceast secven, dac i ultima linie a ierului text de intrare este
terminat cu new-line, aceasta va scris de dou ori n ierul de ieire. De
ce? Dup ce se citete ultima linie nc nu este poziionat indicatorul de
sfrit de ier, deci funcia fgets returneaz succes. La reluarea ciclului se
ncearc un nou fgets i abia acum se depisteaz sfritul de ier, fapt
marcat n zona rezervat uxului . Astfel coninutul tabloului lin rmne
nemodicat i este scris a doua oar n ierul de ieire. Abia la o nou
reluare a ciclului funcia feof ne spune c s-a depistat sfritul de ier.
n acest manual sunt prezentate mai multe programe care efectueaz
diferite prelucrri asupra unor iere text. Pentru simplitate toate programele
presupun c nu apar erori la citire sau la scriere.
11.7. Operaii cu directoare
Funciile de parcurgere a cataloagelor de iere descrise n aceast
seciune (opendir, readdir, closedir) sunt denite de mai multe medii de
programare C (Borland, Watcom, Visual C, GNU Linux), precum i de
standardul POSIX. Aceste funcii sunt descrise n <dirent. H>.
Funciile de redenumire i tergere a unor iere sunt descrise n
<stdio. H>.
Nume
Opendir deschide un director
Declaraie
DIR *opendir (const char *nume);
Descriere
Funcia opendir deschide un ux pentru directorul cu numele nume i
returneaz un pointer la uxul deschis. Fluxul este poziionat pe prima intrare
din director.
Valoare returnat
Funcia returneaz un pointer la ux n caz de succes, sau NULL n caz
de eroare i variabila global ermo indic codul erorii.
Cteva erori posibile
EACCES Acces interzis
ENOTDIRnume nu este un director
Nume

Readdir citete 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 uxul 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 uxul dir.
Valoare returnat
Funcia returneaz 0 n caz de succes sau EOF n caz de eroare.
Nume
Rename redenumete un ier
Remove terge un ier
Declaraie
Int rename (const char *old, const char
*new);
Int remove (const char *name);
Descriere
Funcia rename schimb numele unui ier 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 e diferite, astfel c rename poate
folosit pentru a muta un ier dintr-un director n altul. Nu se permit
specicatori generici (wildcards).
Funcia remove terge ierul specicat prin name.
Valoare returnat
n caz de succes se returneaz 0. n caz de eroare se returneaz EOF i
variabila global ermo indic codul erorii.
11.8. Programe demonstrative
Primele trei programe primesc ca parametri n linia de comand
numele ierelor pe care le vor prelucra. Ultimul program primete ca
parametru n linia de comand numele directorului al crui coninut va
aat.
1) Determinarea mrimii unui ier
#include <stdio. H>
FILE *f;
Int main (int ac, char *av) {

If (ac! =2) {
Fputs (Un argument!, stderr);
Return 1;
}
F = fopen (av [1],rb);
If (! F) {
Perror (Eroare la deschidere);
Return 1;
}
Fseek (f, 0, SEEK_END);
Fprintf (stderr,File %s, size %ld,
Ftell (f);
Fclose (f);
Return 0;
} 2) Copierea unui ier
Funciile fgets i fputs se folosesc pentru uxuri deschise n mod text.
Cum se utilizeaz pentru copierea unui ier text?
#include <stdio. H>
#dene LSIR 80
Char lin [LSIR];
FILE *, *fo;
Int main (int ac, char *av) {
If (ac! =3) {
Fputs (Doua argumente!, stderr);
}
Fi=fopen (av [1],rt); fo=fopen (av [2],wt);
If (! Fi ||! Fo) {
Perror (Eroare la deschidere);
Return 1;
}
While (fgets (lin, LSIR, )
Fputs (lin, fo);
Fclose (); fclose (fo);
Return 0;
}
Funciile fread i fwrite se folosesc pentru uxuri deschise n mod binar.
Cum se utilizeaz pentru copierea unui ier binar?
#include <stdio. H>
#dene LZON 80
Char zon [LZON];
FILE *, *fo;
Int k;
Int main (int ac, char *av) {
If (ac! =3) {
Fputs (Doua argumente!, 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, )
Fwrite (zon, 1, k, fo);
Fclose (); fclose (fo);
Return 0;
} 3) Prelucrarea unui ier text
Programul prezentat n continuare citete un ier text care conine pe
ecare linie un ir de caractere (fr spaii) i trei valori ntregi i aeaz pe
terminal numele pe 12 poziii aliniat la stnga i media aritmetic a celor trei
valori ntregi.
#include <stdio. H>
FILE *;
Char num [10];
Int a, b, c;
Double m;
Int main (int ac, char *av) {
If (ac! =2) {
Fputs (Un argument!, stderr);
Return 1;
}
Fi=fopen (av [1],rt);
If (! Fi) {
Perror (Eroare la deschidere);
Return 1;
}
While (fscanf (,%s %d %d %d,
Num, &a, &b, &c)! =EOF) {
M= (a+b+c)/3.0;
Printf (%-l2s%6.2lf, num, m);
}
Fclose ();
Return 0;
} 4) Aarea 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);
Return 1;

}
Dir = opendir (av [1]);
If (! Dir) {
Perror (Eroare open dir);
Return 1;
}
While (ent=readdir (dir)
Printf (%s, ent->d_name);
Return 0;
} 12. Alte rutine din biblioteca standard
n acest capitol sunt descrise funcii care rezolv probleme legate de
alocarea dinamic a memoriei, sortare i cutare, clasicare, operaii cu
blocuri de memorie i iruri de caractere, funcii matematice.
12.1. Alocarea dinamic a memoriei
Nume
Calloc,malloc, realloc aloc memoria n mod dinamic
Free elibereaz memoria alocat n mod dinamic
Declaraie
#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 tablou de nel elemente, ecare
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 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 schimb 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 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 fost returnat de un
apel precedent malloc, calloc sau realloc.
Valori returnate
Pentru calloc i malloc valoarea returnat este un pointer la memoria
alocat, care este aliniat n mod corespunztor pentru orice tip de variabile,
sau NULL dac nu exist sucient memorie continu.
Funcia free nu returneaz nimic.

Funcia realloc returneaz un pointer la noua zon de memorie alocat,


care este aliniat n mod corespunztor pentru orice tip de variabile i poate
diferit de ptr, sau poate NULL dac nu exist sucient memorie
continu sau dac valoarea size este egal cu 0. Dac realloc eueaz, blocul
original rmne neatins nu este nici eliberat nici mutat.
12.2. Sortare i cutare
Nume
Qsort sorteaz un tablou
Bsearch cutare binar ntr-un tablou sortat
Declaraie
#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 tablou de nel elemente, ecare de mrime
size. Argumentul base indic spre nceputul tabloului.
Elementele tabloului sunt 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 mai mic dect, egal cu, respectiv mai
mare dect al doilea. Dac cele dou elemente comparate sunt egale,
ordinea n tabloul sortat este nedenit.
Funcia bsearch caut ntr-un tablou de nel elemente, ecare de
mrime size, un membru care coincide cu obiectul indicat de key. Argumentul
base indic spre nceputul tabloului.
Coninutul tabloului trebuie s e sortat cresctor n concordan cu
funcia de comparare referit de comp, apelat cu dou argumente care
indic spre obiectele ce se compar. Funcia de comparare trebuie s
returneze un ntreg mai mic dect, egal cu, sau mai mare dect zero dac
primul argument este considerat a mai mic dect, egal cu, respectiv mai
mare dect al doilea.
Valoare returnat
Funcia bsearch returneaz un pointer la un membru al tabloului care
coincide cu obiectul indicat de key, sau NULL dac nu se gsete nici un
membru. Dac exist mai multe elemente care coincid cu key, poate
returnat oricare element cu aceast proprietate.
12.3. Rutine de clasicare
Nume
Isalnum, isalpha, isascii, iscntrl, isdigit,
Isgraph, islower, isprint, ispunct, isspace,
Isupper, isxdigitrutine de clasicare
Tolower-conversie n liter mic

Toupper-conversie n liter mare


Declaraie
#include <ctype. H>
Int isalnum (int c);int islower (int c);
Int isalpha (int c);int isprint (int c);
Int isascii (int c);int ispunct (int c);
Int iscntrl (int c);int isspace (int c);
Int isdigit (int c);int isupper (int c);
Int isgraph (int c);int isxdigit (int c);
Int tolower (int c);int toupper (int c);
Descriere
Primele 12 funcii veric dac c, care trebuie s e o valoare de tip
unsigned char sau EOF, se a n una din clasele de caractere enumerate mai
sus.
Isalnum
Veric dac c este alfanumeric; este echivalent cu (isalpha (c) ||
isdigit (c).
Isalpha
Veric dac c este alfabetic; este echivalent cu (isupper (c) || islower
(c).
Isascii
Veric dac c este o valoare pe 7 bii din setul de caractere ASCII.
Iscntrl
Veric dac c este un caracter de control.
Isdigit
Veric dac c este o cifr (ntre 0 i 9).
Isgraph
Veric dac c este un caracter aabil cu excepia spaiului.
Islower
Veric dac c este o liter mic.
Isprint
Veric dac c este un caracter aabil inclusiv spaiu.
Ispunct
Veric dac c este un caracter diferit de spaiu i non-alfanumeric.
Isspace
Veric dac c este un spaiu alb.
Isupper
Veric dac c este o liter mare.
Isxdigit
Veric dac c este o cifr hexazecimal din setul 0 1 2 3 4 5 6 7 8 9a
b c d e f A B 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 a
n clasa testat i zero n caz contrar.
Valoarea returnat de funciile to. este litera convertit dac caracterul
c este o liter i nedenit n caz contrar.
12.4. Operaii cu blocuri de memorie
Pentru majoritatea funciilor din aceast categorie compilatorul
expandeaz codul acestora folosind instruciuni pe iruri de caractere.
Declaraiile acestor funcii se obin cu
#include <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);
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.
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.
12.5. Operaii cu iruri de caractere
Pentru majoritatea funciilor din aceast categorie compilatorul
expandeaz codul acestora folosind instruciuni pe iruri de caractere.
Declaraiile acestor funcii se obin cu
#include <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, stmcpy copiaz un ir de caractere
Declaraie
Char *strcpy (char *dest, const char *src);
Char *stmcpy (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 e sucient de mare pentru a primi copia.
Funcia stmcpy este similar, cu excepia faptului c nu se copiaz mai
mult de n octei din src. Astfel, dac caracterul terminator null nu se a n
primii n octei din src, rezultatul nu va 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 eliberat
cu free.
Valoare returnat
Funcia returneaz un pointer la irul duplicat, sau NULL dac nu exist
memorie sucient disponibil.
Nume
Strcat, stmcat concateneaz dou iruri
Declaraie
Char *strcat (char *dest, const char *src);
Char *stmcat (char *dest, const char *src,
Unsigned n);
Descriere
Funcia strcat adaug irul src la irul dest suprascriindcaracterul 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 sucient spaiu
pentru a pstra rezultatul.
Funcia stmcat 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.
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 calculeaz lungimea segmentului iniial din s format n
ntregime numai cu caractere din acc.
Funcia strcspn calculeaz 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 a
n acc.
Funcia strcspn returneaz poziia primului caracter din s care se a n
rej.
12.6. Biblioteca matematic 1) Funciile din prima categorie sunt
descrise 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 returnate de rand.
Aceste secvene se repet dac srand se apeleaz cu aceeai valoare seed.
Se obinuiete ca generatorul s e 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 n C: The Art of Scientic 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.
Tot n ierul <stdlib. H> sunt descrise i urmtoarele funcii:
Int abs (int i); valoare absolut
Long labs (long i); valoare absolut
Int atoi (char *s);conversie din ASCII n ntreg
Long atol (char *s); conversie din ASCII n ntreg lung
Double atof (char *s);conversie din ASCII n dubl
Precizie 2) Funciile din a doua categorie sunt descrise n ierul
<math. H>.
Double fabs (double x);valoare absolut
Double oor (double x);parte ntreag inferioar
Double ceil (double x);parte ntreag superioar
Double sqrt (double x);
Double sin (double x);sin (x)
Double cos (double x);cos (x)
Double tan (double x);tg (x)
Double asin (double x);arcsin (x)
Double acos (double x);arccos (x)
Double atan (double x);arctg (x)n [-p/2, p/2]
Double atan2(double y, double x);
Arctg (y/x)n [p, p]
Double exp (double x);ex
Double log (double x);ln (x)
Double pow (double x, double y);xy
Double sinh (double x);sinh (x)
Double cosh (double x);cosh (x)
Double tanh (double x);tgh (x)
Double ldexp (double x, int e);x 2e
Double fmod (double x, double y);x modulo y
Funcia fmod returneaz o valoare f denit 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 atuncif=0 i e=0.
Valoarea returnat estef.
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.
12.7. Programe demonstrative 1) Programul prezentat n continuare
genereaz un ir de n valori ntregi aleatoare n intervalul [0, M-l] pe care le
depune n tabloul X (alocat dinamic) i apoi le sorteaz cresctor. n
continuare se genereaz k valori ntregi aleatoare pe care le caut n tabloul
X. Pentru ecare cutare cu succes se aeaz pe terminal valoarea cutat
i poziia n tablou.
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!, 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+) {
V=rand () %M;
P= (int *) bsearch (&v, X, n, sizeof (int),
Cmp);
If (p)
Printf (Val: %dPos: %d, v, p-X);
}
Free (X);

Return 0;
} 2) S relum al treilea exemplu din capitolul precedent. Se citete un
ier text care conine pe ecare linie un nume (ir de caractere fr spaiu)
i trei valori reale (note). Pentru ecare linie se calculeaz media aritmetic
a celor trei valori i se determin dac elementul este admis (ecare not
este minimum 5) sau respins (cel puin o not este sub 5). n nal se aeaz
liniile n ordinea urmtoare: mai nti elementele admise n ordinea
descresctoare a mediei i apoi elementele respinse n ordine alfabetic dup
nume. Se aeaz 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 Fld selecteaz din
zona referit de pointerul P membrul f. Deoarece pointerul P (care poate
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
: nm, ar, md, denii n cadrul structurii de tip StEl.
Deoarece nu tim de la nceput cte linii are ierul de intrare, suntem
nevoii s folosim urmtoarea strategie. La nceput alocm pentru tabloul El
o zon care s memoreze NA elemente. Pe msur ce aceast zon se
completeaz, la un moment dat numrul de linii citite coincide cu NA. n
acest moment se aloc o zon nou care s poat memora un numr mai
mare de elemente. Desigur, de ecare dat se va actualiza mrimea spaiului
alocat.
#include <stdlib. H>
#include <string. H>
#include <stdio. H>
#dene NA 32
Typedef struct {
Char nm [10], ar;
Float na, nb, ne, md;
} StEl;
#dene Fld (P, f) (StEl *) P)->f
Int comp (const void *A, const void *B) {
Float w;
Int d;
If (d=Fld (A, ar)-Fld (B, ar)
Return d;
If (Fld (A, ar) =A) {
W=Fld (B, md)-Fld (A, md);
If (w>0) return 1;
If (w<0) return -l;
}
Return strcmp (Fld (A, nm), Fld (B, nm);
}
Int main (int ac, char *av) {
Int na, ne, i;
StEl *El;

FILE *;
If (ac! =2) {
Fputs (Un argument!, 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 (,%s %d %d %d,
El [ne]. Nm, &El [ne].na, &El [ne]. Nb,
&El [ne].ne)! =EOF) {
If (El [ne].na>=5) & (El [ne]. Nb>=5) & (El [ne].ne>=5)
El [ne].ar=A;
Else El [ne].ar=R;
El [ne].md= (El [ne].na+El [ne]. Nb+
El [ne].ne)/3.0;
Ne+;
If (ne=na) {
Na+=NA;
El= (StEl *) realloc (El, na*
Sizeof (StEl);
}
}
Fclose ();
Qsort (El, ne, sizeof (StEl), comp);
For (i=0; i<ne; i+)
Printf (%-l2s %c%6.2lf,
El [i]. Nm, El [i].ar, El [i].md);
Free (El);
Return 0;
} 3) Se citete dintr-un ier text o valoare natural n. Urmtoarele linii
conin n cuvinte, ecare cuvnt avnd acelai numr de litere (cel mult 10).
S se aeze cuvintele din ier ordonate alfabetic.
Pentru a memora lista de cuvinte folosim urmtoarea strategie. n loc
s alocm pentru ecare 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 ecrui cuvnt (numrul de litere). De ce (l+1)?Pentru c
trebuie s memorm i caracterul terminator null.
Avantaje: memoria este utilizat mai ecient 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 *;
If (ac! =2) {
Fputs (Un argument!, stderr);
Return 1;
}
Fi=fopen (av [1],rt);
If (! Fi) {
Perror (Eroare la deschidere);
Return 1;
}
Fscanf (,%d %s, n, s);
L=strlen (s);
C= (char *) malloc (l+1)*n);
Strcpy (C, s);
For (i=1; i<n; i+)
Fscanf (,%s, C+(l+1)*i);
Fclose ();
Qsort (C, n, l+1, comp);
For (i=0; i<n; i+)
Printf (%s, C+(l+1)*i);
Free (C);
Return 0;
}

SFRIT

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