Documente Academic
Documente Profesional
Documente Cultură
Manual de Programare C
Manual de Programare C
Roger Bacon
Limbajul C a fost creat la nceputul anilor '70 de ctre Brian W
Kernigham i Dennis M Ritchie de la Bell Laboratories New Jersey, fiind
iniial destinat scrierii unei pri din sistemul de operare Unix. Lucrarea
The C Programming Language a celor doi autori, aprut n mai multe
versiuni, a rmas cartea de referin n domeniu, impunnd un standard
minimal pentru orice implementare.
Caracteristicile distinctive ale limbajului au fost clar definite de la
nceput, ele pstrndu-se n toate dezvoltrile ulterioare:
portabilitate maxim;
structurare;
posibilitatea efecturii operaiilor la nivelul mainii cu pstrarea
caracteristicilor unui limbaj evoluat.
Acest manual este structurat pe 12 capitole astfel nct elementele
limbajului C s fie prezentate ntr-o manier unitar. Primul capitol face o
scurt introducere i prezint patru programe C. Urmtoarele nou capitole
descriu elementele limbajului C. Capitolele unsprezece i doisprezece trec n
revist funciile cele mai des utilizate definite n biblioteca standard,
mpreun cu cteva programe demonstrative. Au fost selectate doar funciile
definite de mai multe standarde (n primul rnd ANSI C), pentru a garanta o
portabilitate ct mai mare.
Acest manual a fost conceput pentru a servi ca document care s poat
fi consultat de programatori n elaborarea proiectelor, i nu pentru a fi
memorat. Manualul nu este o introducere n limbajul C; se presupune c
cititorul este familiarizat cu:
concepte de baz referitoare la programare: variabile, instruciuni de
atribuire, de control al execuiei, apeluri de funcii;
reprezentarea informaiei n calculator a valorilor ntregi, n virgul
mobil, a codurilor ASCII;
operaii de intrare / ieire.
Deoarece avem convingerea c cea mai bun explicaie este un program
funcional, majoritatea exemplelor din acest manual se regsesc n fiiere
surs C care pot fi rulate pe orice mediu de programare C i sub orice sistem
de operare.
Ca o ultim observaie amintim recomandarea fcut de nii creatorii
limbajului: cea mai bun metod de nvare este practica.
__________________________________________________________________________
__________________________________________________________________________
s = 0;
do {
scanf("%d",&n);
s += n;
} while (n!=0);
printf("%d\n",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
fiind o operaie de intrare i a doua o adunare.
Primul argument al funciei scanf - formatul de introducere
"%d" - indic faptul c se ateapt introducerea unei valori ntregi n
format zecimal de la terminal (consol). Al doilea argument indic
unde se va depune n memorie valoarea citit; de aceea este necesar
s se precizeze adresa variabilei n (cu ajutorul operatorului &).
n a doua instruciune la valoarea variabilei s se adun valoarea
variabilei n. Operatorul += are semnificaia adun la.
Aceast secven se repet (do) ct timp (while) valoarea
introdus (n) este nenul. Operatorul != are semnificaia diferit de.
n final funcia printf afieaz pe terminal valoarea variabilei
s n format zecimal.
Al treilea program ateapt de la terminal introducerea unei
valori naturale n, dup care mai ateapt introducerea a n valori reale
(dubl precizie): a0, a1, ..., an1. n continuare se parcurge aceast
list i se determin produsul valorilor strict pozitive. n final
programul afieaz 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\n",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, ..., n1). Formatul de introducere "%lf"
indic faptul c se ateapt introducerea unei valori reale de la
terminal, care va fi depus la locaia de memorie asociat variabilei
ai. n locul construciei &a[i] se poate folosi forma echivalent
a+i.
Pentru a introduce toate valorile ai se efectueaz un ciclu for,
n cadrul cruia variabila i (care controleaz ciclul) ia toate valorile
ntre 0 (inclusiv) i n (exclusiv) cu pasul 1. Trecerea la urmtoarea
valoare a variabilei i se face cu ajutorul operatorului ++.
n continuare variabila p, care va memora produsul valorilor
cerute, se iniializeaz cu 1. Fiecare valoare ai este verificat
(instruciunea if) dac este strict pozitiv i n caz afirmativ este
nmulit cu valoarea p. Operatorul *= are semnificaia nmulete
cu.
Al patrulea program este o ilustrare a unor probleme legate de
capacitatea reprezentrilor valorilor de tip ntreg i virgul mobil.
#include <stdio.h>
__________________________________________________________________________
int main() {
short k,i;
float a,b,c,u,v,w;
i=240; k=i*i;
printf("%hd\n",k);
a=12345679; b=12345678;
c=a*a-b*b;
u=a*a; v=b*b; w=u-v;
printf("%f %f\n",c,w);
if (c==w) return 0;
else return 1;
}
Variabila k, care ar trebui s memoreze valoarea 57600, are tipul
ntreg scurt (short), pentru care domeniul de valori este restrns la
32768 32767. Astfel c valoarea 1110000100000000(2) (n zecimal
57600), n reprezentare ntreag cu semn este de fapt 7936.
Al doilea set de operaii necesit o analiz mai atent; explicaiile
snt valabile pentru programe care ruleaz pe arhitecturi Intel.
Variabila c, care ar trebui s memoreze valoarea 2461357 (rezultatul
corect), va avea valoarea 2461356, deoarece tipul float are
rezervate pentru mantis doar 24 de cifre binare. Rezultatul este
foarte apropiat de cel corect deoarece rezultatele intermediare se
pstreaz n regitrii coprocesorului matematic cu precizie maxim.
Abia la memorare se efectueaz trunchierea, de unde rezult valoarea
afiat.
Cu totul altfel stau lucrurile n cazul celui de al treilea set de
operaii. Aici rezultatele intermediare snt memorate de fiecare dat
cu trunchiere n variabile de tip float. n final se calculeaz i
diferena dintre cele dou valori trunchiate, de unde rezult valoarea
16777216.
nainte de terminare se verific dac valorile c i w snt egale. n
caz afirmativ se comunic sistemului de operare un cod 0 (terminare
normal). n caz contrar se comunic un cod 1 (terminare anormal).
Rulai acest program pe diferite sisteme de calcul i observai
care este rezultatul.
__________________________________________________________________________
__________________________________________________________________________
2.1. Identificatori
Un identificator este o succesiune de litere i cifre dintre care
primul caracter este n mod obligatoriu o liter. Se admit i litere
mari i litere mici dar ele se consider caractere distincte. Liniua de
subliniere _ este considerat ca fiind liter. Deci alfabetul peste care
snt definii identificatorii este urmtorul:
A a,...,z,A,...,Z,0,...,9,_
register
auto
extern
static
struct
union
typedef
if
else
for
while
do
continue
break
const
void
switch
case
default
return
sizeof
__________________________________________________________________________
10
2.3. Constante
n limbajul C exist urmtoarele tipuri de constante: ntreg
(zecimal, octal, hexazecimal), ntreg lung explicit, flotant, caracter,
simbolic.
Constante ntregi
O constant ntreag const dintr-o succesiune de cifre.
O constant octal este o constant ntreag care ncepe cu 0
(cifra zero), i este format cu cifre de la 0 la 7.
O constant hexazecimal este o constant ntreag precedat de
0x sau 0X (cifra 0 i litera x). Cifrele hexazecimale includ literele de
la A la F i de la a la f cu valori de la 10 la 15.
n orice alt caz, constanta ntreag este o constant zecimal.
Exemplu: constanta zecimal 31 poate fi scris ca 037 n octal i
0x1f sau 0X1F n hexazecimal.
O constant ntreag este generat pe un cuvnt (doi sau patru
octei, dac sistemul de calcul este pe 16 sau 32 de bii).
O constant zecimal a crei valoare depete pe cel mai mare
ntreg cu semn reprezentabil pe un cuvnt scurt (16 bii) se consider
de tip long i este generat pe 4 octei.
O constant octal sau hexazecimal care depete pe cel mai
mare ntreg fr semn reprezentabil pe un cuvnt scurt se consider
de asemenea de tip long.
O constant ntreag devine negativ dac i se aplic operatorul
unar de negativare -.
__________________________________________________________________________
11
Constante flotante
O constant flotant const dintr-o parte ntreag, un punct
zecimal, o parte fracionar, litera e sau E i opional, un exponent
care este un ntreg cu semn. Partea ntreag i partea fracionar snt
constituite din cte o succesiune de cifre. ntr-o constant flotant, att
partea ntreag ct i partea fracionar pot lipsi dar nu ambele; de
asemenea poate lipsi punctul zecimal sau litera e i exponentul, dar
nu deodat (i punctul i litera e i exponentul).
Exemplu: 123.456e7 sau 0.12e3
Orice constant flotant se consider a fi n precizie extins.
Constante caracter
O constant caracter const dintr-un singur caracter scris ntre
apostrofuri, de exemplu 'x'. Valoarea unei constante caracter este
valoarea numeric a caracterului, n setul de caractere al
calculatorului. De exemplu n setul de caractere ASCII caracterul
zero sau '0' are valoarea 48 n zecimal, total diferit de valoarea
numeric zero.
Constantele caracter particip la operaiile aritmetice ca i oricare
alte numere. De exemplu, dac variabila c conine valoarea ASCII a
unei cifre, atunci prin instruciunea:
c = c - '0' ;
aceast valoare se transform n valoarea efectiv a cifrei.
Anumite caractere negrafice i caractere grafice ' (apostrof) i \
(backslash) pot fi reprezentate ca i constante caracter cu ajutorul aa
numitor secvene de evitare. Secvenele de evitare ofer de altfel i
un mecanism general pentru reprezentarea caracterelor mai greu de
introdus n calculator i a oricror configuraii de bii. Aceste
secvene de evitare snt:
__________________________________________________________________________
12
\n new-line
\r carriage return
\t tab orizontal
\f form feed
\b backspace
\a semnal sonor
\ddd configuraie de bii (ddd)
\\ backslash
\' apostrof
\" ghilimele
Aceste secvene, dei snt formate din mai multe caractere, ele
reprezint n realitate un singur caracter. Secvena '\ddd' unde ddd
este un ir de 1 pn la 3 cifre octale, genereaz pe un octet valoarea
caracterului dorit sau a configuraiei de bii dorite, date de irul ddd.
Exemplu: secvena '\040' va genera caracterul spaiu.
Un caz special al acestei construcii este secvena '\0' care
indic caracterul NULL, care este caracterul cu valoarea zero. '\0'
este scris deseori n locul lui 0 pentru a sublinia natura de caracter a
unei anumite expresii.
Cnd caracterul care urmeaz dup un backslash nu este unul
dintre cele specificate, backslash-ul este ignorat. Atragem atenia c
toate caracterele setului ASCII snt pozitive, dar o constant caracter
specificat printr-o secven de evitare poate fi i negativ, de
exemplu '\377' are valoarea -1.
Constante simbolice
O constant simbolic este un identificator cu valoare de
constant. Valoarea constantei poate fi orice ir de caractere introdus
prin construcia #define (vezi capitolul 8).
Exemplu: #define MAX 1000
Dup ntlnirea acestei construcii compilatorul va nlocui toate
apariiile constantei simbolice MAX cu valoarea 1000.
Numele constantelor simbolice se scriu de obicei cu litere mari
(fr a fi obligatoriu).
__________________________________________________________________________
13
2.4. iruri
Un ir este o succesiune de caractere scrise ntre ghilimele, de
exemplu "ABCD".
Ghilimelele nu fac parte din ir; ele servesc numai pentru
delimitarea irului. Caracterul " (ghilimele) poate aprea ntr-un ir
dac se utilizeaz secvena de evitare \". n interiorul unui ir pot fi
folosite i alte secvene de evitare pentru constante caracter, de
asemenea poate fi folosit caracterul \ (backslash) la sfritul unui
rnd pentru a da posibilitatea continurii unui ir pe mai multe linii,
situaie n care caracterul \ nsui va fi ignorat.
Pentru irul de caractere se mai folosete denumirea constant ir
sau constant de tip ir.
Cnd un ir apare ntr-un program C, compilatorul creeaz un
masiv de caractere care conine caracterele irului i plaseaz automat
caracterul NULL ('\0') la sfritul irului, astfel ca programele care
opereaz asupra irurilor s poat detecta sfritul acestora. Aceast
reprezentare nseamn c, teoretic, nu exist o limit a lungimii unui
ir, iar programele trebuie s parcurg irul, analizndu-l pentru a-i
determina lungimea. Se admit i iruri de lungime zero.
Tehnic, un ir este un masiv ale crui elemente snt caractere. El
are tipul masiv de caractere i clasa de memorie static (vezi
seciunea 3.1). Un ir este iniializat cu caracterele date (vezi
seciunea 5.4).
La alocare, memoria fizic cerut este cu un octet mai mare dect
numrul de caractere scrise ntre ghilimele, datorit adugrii
automate a caracterului null la sfritul fiecrui ir.
Exemplu. Funcia 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]!='\0')
++i;
__________________________________________________________________________
14
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 fi
clasificai dup diverse criterii. Exist operatori unari, binari i
ternari, operatori aritmetici, logici, operatori pe bii etc.
ntr-un capitol separat vom prezenta clasele de operatori care
corespund la diferite nivele de prioritate.
2.6. Separatori
Un separator este un caracter sau un ir de caractere care separ
unitile lexicale ntr-un program scris n C.
Separatorul cel mai frecvent este aa numitul spaiu alb (blanc)
care conine unul sau mai multe spaii, tab-uri, new-line-uri sau
comentarii.
Aceste construcii snt eliminate n faza de analiza lexical a
compilrii.
Dm mai jos lista separatorilor admii n limbajul C.
( )
{ }
[ ]
__________________________________________________________________________
15
" "
' '
;
/*
*/
__________________________________________________________________________
16
3. Variabile
O variabil este un obiect de programare cruia i se atribuie un
nume.
Variabilele ca i constantele snt elemente de baz cu care
opereaz un program scris n C.
Variabilele se deosebesc dup denumire i pot primi diferite
valori. Numele variabilelor snt identificatori. Numele de variabile se
scriu de obicei cu litere mici (fr a fi obligatoriu).
Variabilele n limbajul C snt caracterizate prin dou atribute:
clas de memorie i tip.
Aceste dou atribute i snt atribuite unei variabile prin
intermediul unei declaraii. Declaraiile listeaz variabilele care
urmeaz a fi folosite, stabilesc clasa de memorie, tipul variabilelor i
eventual valorile iniiale, dar despre declaraii vom discuta n
capitolul 5.
Variabile automatice
Variabilele automatice snt variabile locale fiecrui bloc
(seciunea 6.2) sau funcii (capitolul 7). Ele se declar prin
specificatorul de clas de memorie auto sau implicit prin context. O
variabil care apare n corpul unei funcii sau al unui bloc pentru care
nu s-a fcut nici o declaraie de clas de memorie se consider
implicit de clas auto.
O variabil auto este actualizat la fiecare intrare n bloc i se
distruge n momentul cnd controlul a prsit blocul. Ele nu i rein
valorile de la un apel la altul al funciei sau blocului i trebuie
iniializate la fiecare intrare. Dac nu snt iniializate, conin valori
__________________________________________________________________________
17
Variabile externe
Variabilele externe snt variabile cu caracter global. Ele se
definesc n afara oricrei funcii i pot fi apelate prin nume din
oricare funcie care intr n alctuirea programului.
n declaraia de definiie aceste variabile nu necesit specificarea
nici unei clase de memorie.
La ntlnirea unei definiii de variabil extern compilatorul aloc
i memorie pentru aceast variabil.
ntr-un fiier surs domeniul de definiie i aciune al unei
variabile externe este de la locul de declaraie pn la sfritul
fiierului.
Aceste variabile exist i i pstreaz valorile de-a lungul
execuiei ntregului program.
Pentru ca o funcie s poat utiliza o variabil extern, numele
variabilei trebuie fcut cunoscut funciei printr-o declaraie.
Declaraia poate fi fcut fie explicit prin utilizarea specificatorului
extern, fie implicit prin context.
Dac definiia unei variabile externe apare n fiierul surs
naintea folosirii ei ntr-o funcie particular, atunci nici o declaraie
ulterioar nu este necesar, dar poate fi fcut.
Dac o variabil extern este referit ntr-o funcie nainte ca ea
s fie definit, sau dac este definit ntr-un fiier surs diferit de
fiierul n care este folosit, atunci este obligatorie o declaraie extern
pentru a lega apariiile variabilelor respective.
Dac o variabil extern este definit ntr-un fiier surs diferit de
cel n care ea este referit, atunci o singur declaraie extern dat n
afara oricrei funcii este suficient pentru toate funciile care
urmeaz declaraiei.
Funciile snt considerate n general variabile externe afar de
cazul cnd se specific altfel.
__________________________________________________________________________
18
Variabile statice
Variabilele statice se declar prin specificatorul de clas de
memorie static. Aceste variabile snt la rndul lor de dou feluri:
interne i externe.
Variabilele statice interne snt locale unei funcii i se definesc n
interiorul unei funcii, dar spre deosebire de variabilele auto, ele i
pstreaz valorile tot timpul execuiei programului. Variabilele statice
interne nu snt create i distruse de fiecare dat cnd funcia este
activat sau prsit; ele ofer n cadrul unei funcii o memorie
particular permanent pentru funcia respectiv.
Alte funcii nu au acces la variabilele statice interne proprii unei
funcii.
Ele pot fi declarate i implicit prin context; de exemplu irurile
de caractere care apar n interiorul unei funcii cum ar fi argumentele
funciei printf (vezi capitolul 11) snt variabile statice interne.
Variabilele statice externe se definesc n afara oricrei funcii i
orice funcie are acces la ele. Aceste variabile snt ns globale numai
pentru fiierul surs n care ele au fost definite. Nu snt recunoscute
n alte fiiere.
n concluzie, variabila static este extern dac este definit n
afara oricrei funcii i este static intern dac este definit n
interiorul unei funcii.
n general, funciile snt considerate obiecte externe. Exist ns
i posibilitatea s declarm o funcie de clas static. Aceasta face
ca numele funciei s nu fie recunoscut n afara fiierului n care a
fost declarat.
__________________________________________________________________________
19
Variabile registru
O variabil registru se declar prin specificatorul de clas de
memorie register. Ca i variabilele auto ele snt locale unui
bloc sau funcii i valorile lor se pierd la ieirea din blocul sau funcia
respectiv. Variabilele declarate register indic compilatorului c
variabilele respective vor fi folosite foarte des. Dac este posibil,
variabilele register vor li plasate de ctre compilator n regitrii
rapizi ai calculatorului, ceea ce conduce la programe mai compacte i
mai rapide.
Variabile register pot fi numai variabilele automatice sau
parametrii formali ai unei funcii. Practic exist cteva restricii
asupra variabilelor register care reflect realitatea hardware-ului
de baz. Astfel:
numai cteva variabile din fiecare funcie pot fi pstrate n regitri
(de obicei 2 sau 3); declaraia register este ignorat pentru celelalte
variabile;
numai tipurile de date int, char i pointer snt admise;
nu este posibil referirea la adresa unei variabile register.
Tipul caracter
O variabil de tip caracter se declar prin specificatorul de tip
char. Zona de memorie alocat unei variabile de tip char este de
un octet. Ea este suficient de mare pentru a putea memora orice
caracter al setului de caractere implementate pe calculator.
Dac un caracter din setul de caractere este memorat ntr-o
variabil de tip char, atunci valoarea sa este egal cu codul ntreg al
caracterului respectiv. i alte cantiti pot fi memorate n variabile de
tip char, dar implementarea este dependent de sistemul de calcul.
__________________________________________________________________________
20
Tipul ntreg
Variabilele ntregi pozitive sau negative pot fi declarate prin
specificatorul de tip int. Zona de memorie alocat unei variabile
ntregi poate fi de cel mult trei dimensiuni.
Relaii despre dimensiune snt furnizate de calificatorii short,
long i unsigned, care pot fi aplicai tipului int.
Calificatorul short se refer totdeauna la numrul minim de
octei pe care poate fi reprezentat un ntreg, n cazul nostru 2.
Calificatorul long se refer la numrul maxim de octei pe care
poate fi 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 i 2147483647 (sisteme de calcul pe 32 de bii).
Calificatorul unsigned alturi de declaraia de tip int
determin ca valorile variabilelor astfel declarate s fie considerate
ntregi fr semn.
Numerele de tipul unsigned respect legile aritmeticii modulo 2 n,
unde n este numrul de bii din reprezentarea unei variabile de tip
int. Numerele de tipul unsigned snt totdeauna pozitive.
Declaraiile pentru calificatori snt de forma:
short int x;
long int y;
unsigned int z;
__________________________________________________________________________
21
/* p indic pe x */
/* p indic pe r */
Tipuri derivate
n afar de tipurile aritmetice fundamentale, exist, n principiu, o
clas infinit de tipuri derivate, construite din tipurile fundamentale
n urmtoarele moduri:
__________________________________________________________________________
22
23
Caractere i ntregi
Un caracter poate aprea oriunde unde un ntreg este admis. n
toate cazurile valoarea caracterului este convertit automat ntr-un
ntreg. Deci ntr-o expresie aritmetic tipul char i int pot aprea
mpreun. Aceasta permite o flexibilitate considerabil n anumite
tipuri de transformri de caractere. Un astfel de exemplu este funcia
atoi descris n 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 configuraii
de bii memorate n variabile de tip caracter pot aprea ca negative
prin extensia la tipul int.
Conversia tipului int n char se face cu pierderea biilor de
ordin superior.
ntregii de tip short snt convertii automat la int. Conversia
ntregilor se face cu extensie de semn; ntregii snt totdeauna cantiti
cu semn.
Un ntreg long este convertit la un ntreg short sau char prin
trunchiere la stnga; surplusul de bii de ordin superior se pierde.
Conversii flotante
Toate operaiile aritmetice n virgul mobil se execut n
precizie extins. Conversia de la float la int se face prin
trunchierea prii fracionare. Conversia de la int la float este
acceptat.
__________________________________________________________________________
24
ntregi fr semn
ntr-o expresie n care apar doi operanzi, dintre care unul
unsigned iar cellalt un ntreg de orice alt tip, ntregul cu semn
este convertit n ntreg fr semn i rezultatul este un ntreg fr
semn.
Cnd un int trece n unsigned, valoarea sa este cel mai mic
ntreg fr semn congruent cu ntregul cu semn (modulo 2 16 sau 232).
ntr-o reprezentare la complementul fa de 2 (deci pentru numere
negative), conversia este conceptual, nu exist nici o schimbare
real a configuraiei de bii.
Cnd un ntreg fr semn este convertit la 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 logice
Expresiile relaionale de forma i<j i expresiile logice legate
prin operatorii && i || snt definite ca avnd valoarea 1 dac snt
adevrate i 0 dac snt false.
__________________________________________________________________________
25
Astfel atribuirea:
d = (c>='0') && (c<='9');
l face pe d egal cu 1 dac c este cifr i egal cu 0 n caz contrar.
Conversii explicite
Dac conversiile de pn aici le-am putea considera implicite,
exist i conversii explicite de tipuri pentru orice expresie. Aceste
conversii se fac prin construcia special numit cast de forma:
(nume-tip) expresie
n aceast construcie expresie este convertit la tipul specificat
dup regulile precizate mai sus. Mai precis aceasta este echivalent
cu atribuirea expresiei respective unei variabile de un tip specificat, i
aceast nou variabil este apoi folosit n locul ntregii expresii. De
exemplu, n expresia:
sqrt((double)n)
se convertete n la double nainte de a se transmite funciei sqrt.
Notm ns c, coninutul real al lui n nu este alterat. Operatorul
cast are aceeai preceden ca i oricare operator unar.
Expresia constant
O expresie constant este o expresie care conine numai
constante. Aceste expresii snt evaluate n momentul compilrii i nu
n timpul execuiei; ele pot fi astfel utilizate n orice loc unde sintaxa
cere o constant, ca de exemplu:
#define MAXLINE 1000
char line[MAXLINE+1];
__________________________________________________________________________
26
4. Operatori i expresii
Limbajul C prezint un numr mare de operatori, caracterizai
prin diferite nivele de prioritate sau preceden.
n acest capitol descriem operatorii n ordinea descresctoare a
precedenei lor. Operatorii descrii n acelai paragraf au aceeai
preceden. Vom specifica de fiecare dat dac asociativitatea este la
stnga sau la dreapta.
Expresiile combin variabile i constante pentru a produce valori
noi i le vom introduce pe msur ce vom prezena operatorii.
27
28
29
30
31
32
33
__________________________________________________________________________
34
0
0
0
1
0
1
35
0
0
1
1
1
0
0
0
1
1
1
1
36
37
38
__________________________________________________________________________
39
__________________________________________________________________________
40
Operator
() [] -> .
! ++ -- - (tip) * & sizeof
* / %
+ << >>
< <= > >=
== !=
&
^
|
&&
||
?:
= op=
,
Asociativitate
stnga la dreapta
dreapta la stnga
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
stnga la dreapta
dreapta la stnga
dreapta la stnga
stnga la dreapta
__________________________________________________________________________
41
5. Declaraii
Declaraiile se folosesc pentru a specifica interpretarea pe care
compilatorul trebuie s o dea fiecrui identificator.
Declaraie:
specificator-declaraie lista-declaratoropt;
Specificator-declaraie:
specificator-tip specificator-declaraieopt
specificator-clas-memorie specificator-declaraieopt
Specificator-tip:
char
short
int
long
unsigned
float
double
void
specificator-structur-sau-reuniune
typedef-nume
Specificator-clas-memorie:
auto
static
extern
register
const
typedef
Lista-declarator:
declarator-cu-iniializare
declarator-cu-iniializare, lista-declarator
Declarator-cu-iniializare:
declarator iniializatoropt
Declarator:
__________________________________________________________________________
42
identificator
(declarator)
* declarator
declarator ()
declarator [expresie-constantopt]
Declaraiile listeaz toate variabilele care urmeaz a fi folosite
ntr-un program. O declaraie specific tipul, clasa de memorie i
eventual valorile iniiale pentru una sau mai multe variabile de
acelai tip. Declaraiile se fac sau n afara oricrei funcii sau la
nceputul unei funcii naintea oricrei instruciuni.
Nu orice declaraie rezerv i memorie pentru un anumit
identificator, de aceea deosebim:
declaraia de definiie a unei variabile, care se refer la locul
unde este creat variabila i unde i se aloc memorie;
declaraia de utilizare a unei variabile, care se refer la locul
unde numele variabilei este declarat pentru a anuna proprietile
variabilei care urmeaz a fi folosit.
register
43
unsigned
44
5.3. Declaratori
Lista-declarator care apare ntr-o declaraie este o succesiune de
declaratori separai prin virgule, fiecare dintre ei putnd avea un
iniializator.
Declaratorii din lista-declarator snt identificatorii care trebuie
declarai.
Fiecare declarator este considerat ca o afirmaie care, atunci cnd
apare o construcie de aceeai form cu declaratorul, produce un
obiect de tipul i de clasa de memorie indicat. Fiecare declarator
conine un singur identificator. Gruparea declaratorilor este la fel ca
i la expresii.
Dac declaratorul este un identificator simplu, atunci el are tipul
indicat de specificatorul din declaraie.
Un declarator ntre paranteze este tot un declarator, dar legtura
declaratorilor compleci poate fi alterat de paranteze.
S considerm acum o declaraie de forma:
T D1
unde T este un specificator de tip (ca de exemplu int) i D1 un
declarator. S presupunem c aceast declaraie face ca identificatorul
s aib tipul ...T unde ... este vid dac D1 este un identificator
simplu (aa cum tipul lui x n int x este int). Dac D1 are forma:
*D
atunci tipul identificatorului pe care-l conine acest declarator este
pointer la T.
Dac D1 are forma:
D()
atunci identificatorul pe care-l conine are tipul funcie care
returneaz T.
__________________________________________________________________________
45
46
47
*p = 35;
n a doua variant modificatorul const este folosit mpreun
cu un parametru pointer ntr-o list de parametri ai unei funcii.
Funcia nu poate modifica variabila pe care o indic pointerul:
int printf (const char *format, ...);
5.5. Iniializare
Un declarator poate specifica o valoare iniial pentru
identificatorul care se declar. Iniializatorul este precedat de semnul
= i const dintr-o expresie sau o list de valori incluse n acolade.
Iniializator:
expresie
{list-iniializare}
List-iniializare:
expresie
list-iniializare, list-iniializare
{list-iniializare}
Toate expresiile dintr-un iniializator pentru variabile statice sau
externe trebuie s fie expresii constante (vezi seciunea 3.4) sau
expresii care se reduc la adresa unei variabile declarate anterior,
posibil offset-ul unei expresii constante. Variabilele de clas auto sau
register pot fi iniializate cu expresii oarecare, nu neaprat expresii
constante, care implic constante sau variabile declarate anterior sau
chiar funcii.
n absena iniializrii explicite, variabilele statice i externe snt
iniializate implicit cu valoarea 0. Variabilele auto i register au valori
iniiale nedefinite (reziduale).
Pentru variabilele statice i externe, iniializarea se face o singur
dat, n principiu nainte ca programul s nceap s se execute.
Pentru variabilele auto i register, iniializarea este fcut
la fiecare intrare n funcie sau bloc.
Dac un iniializator se aplic unui scalar (un pointer sau un
obiect de tip aritmetic) el const dintr-o singur expresie, eventual n
__________________________________________________________________________
48
5.6. Nume-tip
n cele expuse mai sus furnizarea unui nume-tip a fost necesar n
dou contexte:
pentru a specifica conversii explicite de tip prin intermediul unui
cast (vezi seciunea 3.4);
ca argument al lui 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:
specificator-tip declarator-abstract
Declarator-abstract:
vid
(declarator-abstract)
*declarator-abstract
declarator-abstract()
declarator-abstract[expresie-constantopt]
Pentru a evita ambiguitatea, n construcia:
(declarator-abstract)
declaratorul abstract se presupune a nu fi vid. Cu aceast restricie,
este posibil s identificm n mod unic locul ntr-un declaratorabstract, unde ar putea aprea un identificator, dac aceast
construcie a fost un declarator ntr-o declaraie. Atunci tipul denumit
este acelai ca i tipul identificatorului ipotetic. De exemplu:
int
int*
__________________________________________________________________________
49
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.
__________________________________________________________________________
50
6. Instruciuni
ntr-un program scris n limbajul C instruciunile se execut
secvenial, n afar de cazul n care se indic altfel.
Instruciunile pot fi scrise cte una pe o linie pentru o lizibilitate
mai bun, dar nu este obligatoriu.
51
declaraie list-declaratori
List-instruciuni:
instruciune
instruciune list-instruciuni
Dac anumii identificatori din lista-declaratori au fost declarai
anterior, atunci declaraia exterioar este salvat pe durata blocului,
dup care i reia sensul su.
Orice iniializare pentru variabile auto i register se
efectueaz la fiecare intrare n bloc. Iniializrile pentru variabilele
static se execut numai o singur dat cnd programul ncepe s
se execute.
Un bloc se termin cu o acolad dreapt care nu este urmat
niciodat de punct i virgul.
52
53
6.5. Instruciunea do
Format:
__________________________________________________________________________
54
do instruciune while
(expresie);
Instruciunea se execut repetat pn cnd valoarea expresiei
devine zero. Testul are loc dup fiecare execuie a instruciunii.
55
56
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':
nc[c-'0']++;
break;
case ' ':
case '\r':
case '\t':
nb++;
break;
default:
na++;
break;
}
printf("cifre: ");
for (i=0; i<10; i++)
printf(" %d",nc[i]);
printf("\nspatii albe: %d, altele: %d\n",
nb,na);
n acest exemplu se parcurg toate caracterele dintr-un ir, se
numr cifrele, spaiile albe i alte caractere i se afieaz aceste
numere nsoite de comentarii.
Instruciunea while este cea care asigur parcurgerea irului
pn la sfrit. Pentru fiecare 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
__________________________________________________________________________
57
for (...) {
...
contin:;
}
do {
...
contin:;
} while (...);
58
59
60
61
62
__________________________________________________________________________
63
64
65
66
s[i++]=c;
if (c=='\n') s[i++]=c;
s[i]='\0';
return i;
}
index(char s[], char t[]) {
/* returneaz poziia din irul s unde ncepe irul t, sau 1 */
int i,j,k;
for (i=0; s[i]!='\0'; i++) {
for (j=i, k=0; t[k]!='\0' &&
s[j]==t[k]; j++, k++)
;
if (t[k]=='\0')
return i;
}
return -1;
}
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 flotant 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]=='\n' ||
/* sare peste spaii albe */
s[i]=='\t'; i++) ;
__________________________________________________________________________
67
sign = 1;
if (s[i]=='+' || s[i]=='-')
sign = (s[i++]=='+') ? 1 : -1;
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;
}
__________________________________________________________________________
68
__________________________________________________________________________
69
70
printf("ALFA");
n care se va tipri chiar textul ALFA i nu constanta 1, deoarece
identificatorul este ncadrat ntre ghilimele.
Aceast facilitate este deosebit de valoroas pentru definirea
constantelor simbolice ca n:
#define TABSIZE 100
int tab[TABSIZE];
deoarece ntr-o eventual modificare a dimensiunii tabelului tab se
va modifica doar o singur linie n fiierul surs. O linie de forma:
#define identif(identif-1,..., identif-n) ir-simboluri
n care nu exist spaiu ntre primul identificator i caracterul ( este
o definiie pentru o macro-operaie cu argumente, n care textul de
nlocuire (ir-simboluri) depinde de modul n care se apeleaz macroul respectiv. Ca un exemplu s definim o macro-operaie numit max
n felul urmtor:
#define max(a,b) ((a)>(b) ? (a) : (b))
atunci linia dintr-un program surs:
x = max(p+q,r+s);
va fi nlocuit cu:
x = ((p+q)>(r+s) ? (p+q) : (r+s));
Aceast macro-definiie furnizeaz o funcie maximum care se
expandeaz n cod, n loc s se realizeze un apel de funcie. Acest
macro va servi pentru orice tip de date, nefiind nevoie de diferite
tipuri de funcii maximum pentru diferite tipuri de date, aa cum este
necesar n cazul funciilor propriu-zise.
Dac se examineaz atent expandarea lui max se pot observa
anumite probleme ce pot genera erori, i anume: expresiile fiind
evaluate de dou ori, n cazul n care ele conin operaii ce genereaz
efecte colaterale (apelurile de funcii, operatorii de incrementare) se
pot obine rezultate total eronate.
De asemenea, trebuie avut mare grij la folosirea parantezelor
pentru a face sigur ordinea evalurii dorite. De exemplu macrooperaia square(x) definit prin:
#define square(x) x*x
care se apeleaz n programul surs sub forma:
z = square(z+1);
__________________________________________________________________________
71
72
73
#include <stdio.h>
#include "numere.h"
static void citire(unsigned *n) {
scanf("%u",n);
if (eprim(*n))
printf("%u e prim\n",*n);
else
printf("%u nu e prim\n",*n);
}
int main() {
unsigned p,q,k;
citire(&p);
citire(&q);
k=cmmdc(p,q);
printf("Cmmdc: %u\n",k);
return 0;
}
Fiierul surs numere.c este 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;
__________________________________________________________________________
74
if (p==0) return q;
else return p;
}
Pentru a obine un program executabil din aceste dou fiiere se
poate utiliza o singur comand de compilare:
Cc princ.c numere.c opiuni-de-compilare
unde Cc este numele compilatorului folosit (exemplu: bcc, gcc).
Opiunile de compilare (atunci cnd snt necesare) snt specifice
mediului de programare folosit.
Proiectele programe de dimensiuni mari snt compuse de cele
mai multe ori din module care se elaboreaz i se pun la punct
separat. Uneori pot fi identificate funcii care prezint un interes
general mai mare, i care pot fi utilizate n elaborarea unor tipuri de
aplicaii foarte diverse. Acestea snt dezvoltate separat i, dup ce au
fost puse la punct n totalitate, se depun ntr-o bibliotec de funcii n
format obiect. n momentul n care acestea snt incluse n proiecte nu
se mai pierde timp cu compilarea modulelor surs. n schimb
modulele care le apeleaz au nevoie de modul cum snt descrise
aceste funcii, i acesta se pstreaz n fiiere header.
__________________________________________________________________________
75
9. Pointeri i masive
Un pointer este o variabil care conine adresa unei alte variabile.
Pointerii snt foarte mult utilizai n programe scrise n C, pe de o
parte pentru c uneori snt unicul mijloc de a exprima un calcul, iar
pe de alt parte pentru c ofer posibilitatea scrierii unui program mai
compact i mai eficient dect ar putea fi obinut prin alte ci.
76
/* greit */
__________________________________________________________________________
77
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 fi:
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;
}
78
79
80
81
return NULL;
82
83
84
85
char *p;
p = alloc(strlen(s)+1);
if (p!=NULL)
strcpy(p,s);
return p;
}
86
87
*pmonth = i;
*pday = yearday;
}
Deoarece aceast ultim funcie returneaz dou valori,
argumentele lun i zi vor fi 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 fi:
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 d1 - dimensional de rangul
j*k*...*p ale crui elemente, fiecare, snt masive d2 dimensionale de rang k*...*p ale crui elemente, fiecare, snt
masive d3 - dimensionale .a.m.d. Oricare dintre expresiile a[i],
a[i][j]..., a[i][j]... [p] pot aprea n expresii. Prima are
tipul masiv, ultima are tipul int, de exemplu, dac masivul este de
tipul int. Vom mai reveni asupra acestei probleme cu detalii.
88
Dac memorm liniile textului una dup alta ntr-un masiv lung
de caractere (gestionat de funcia alloc), atunci fiecare linie poate
fi accesibil cu ajutorul unui pointer la primul ei caracter. Pointerii
tuturor liniilor, la rndul lor, pot fi memorai sub forma unui masiv.
Atunci dou linii de text pot fi comparate transmind pointerii lor
funciei strcmp. Dac dou linii care nu respect ordinea trebuie s
fie schimbate, se schimb doar pointerii lor din masivul de pointeri i
nu textul efectiv al liniilor.
Procesul de sortare l vom realiza n trei pai:
1) se citesc toate liniile textului de la intrare;
2) se sorteaz liniile n ordine lexicografic;
3) se tipresc liniile sortate n noua ordine.
Vom scrie programul prin funciile sale, fiecare funcie realiznd
unul din cei trei pai de mai sus. O rutin principal va controla cele
trei funcii. Ea are urmtorul cod:
#define 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\n");
}
Cele 3 funcii care realizeaz ntregul proces snt: readlines,
sort i writelines.
Rutina de intrare readlines trebuie s memoreze caracterele
fiecrei linii i s construiasc un masiv de pointeri la liniile citite.
Trebuie, de asemenea, s numere liniile din textul de la intrare,
deoarece aceast informaie este necesar n procesul de sortare i de
__________________________________________________________________________
89
90
int i;
for (i=0; i<nlines; i++)
printf("%s\n",lineptr[i]);
}
Declaraia nou care apare n aceste programe este:
char *lineptr[LINES];
care indic faptul c lineptr este un masiv de LINES elemente,
fiecare element al masivului fiind 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 fi tratat ca un pointer (vezi
seciunea 9.3) i atunci funcia writelines mai poate fi scris i
astfel:
writelines(char *lineptr[], int nlines) {
while (--nlines>=0)
printf("%s\n",*lineptr++);
}
n funcia printf, lineptr indic iniial prima linie de
imprimat; fiecare incrementare avanseaz pe *lineptr la
urmtoarea linie de imprimat, n timp ce nlines se micoreaz
dup fiecare tiprire a unei linii cu 1.
Funcia care realizeaz sortarea efectiv a liniilor se bazeaz pe
algoritmul de njumtire i are urmtorul cod:
#define NULL 0
#define LINES 100
/* nr maxim de linii de sortat */
sort(char *v[], int n) {
/* sorteaz irurile v0, v1, ... vn-1 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) {
__________________________________________________________________________
91
if (strcmp(v[j],v[j+gap])<=0)
break;
temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}
}
Deoarece fiecare element al masivului v (care este de fapt
masivul lineptr) este un pointer la primul caracter al unei linii,
variabila temp va fi i ea un pointer la un caracter, deci operaiile de
atribuire din ciclu dup variabila j snt admise i ele realizeaz
reinversarea pointerilor la linii dac ele nu snt n ordinea cerut.
S reinem deci urmtoarele lucruri legate de masive i pointeri.
De cte ori apare ntr-o expresie un identificator de tip masiv el este
convertit ntr-un pointer la primul element al masivului. Prin
definiie, operatorul de indexare [] este interpretat astfel nct
E1[E2] este identic cu *((E1)+(E2)). Dac E1 este un masiv, iar
E2 un ntreg, atunci E1[E2] se refer la elementul de indice E2 al
masivului E1.
O regul corespunztoare se aplic i masivelor multidimensionale. Dac E1 este un masiv d-dimensional, de rangul
i*j*...*k, atunci ori de cte ori e1 apare ntr-o expresie, e1 va fi
convertit ntr-un pointer la un masiv d1 - dimensional de rangul
j*...*k, ale crui elemente snt masive. Dac operatorul * se
aplic acestui pointer, rezultatul este masivul d1 - dimensional, care
se va converti imediat ntr-un pointer la un masiv d2 - dimensional
.a.m.d. Raionamentul se poate aplica n mod inductiv pn cnd, n
final, ca urmare a aplicrii operatorului * se obine ca rezultat un
ntreg, de exemplu, dac masivul a fost declarat de tipul int.
S considerm, de exemplu, masivul:
int x[3][5];
__________________________________________________________________________
92
93
94
4) Declaraia:
int y[4][3] = {
{1},{2,},{3,},{4}
};
iniializeaz masivul y[0] cu (1,0,0), masivul y[1] cu (2,0,0),
masivul y[2] cu (3,0,0) i masivul y[4] cu (4,0,0).
5) Declaraia:
static char msg[] = "Eroare de sintaxa";
iniializeaz elementele masivului de caractere msg cu caracterele
succesive ale irului dat.
n ceea ce privete iniializarea unui masiv de pointeri s
considerm urmtorul exemplu.
Fie funcia 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 fiecare din ele pe care apoi i introduce n
masivul name. Deci name[i] va conine un pointer la irul de
__________________________________________________________________________
95
96
97
__________________________________________________________________________
98
99
100
number = 0;
while (--argc>0 && (*++argv)[0]=='-')
for (s=argv[0]+1; *s!='\0'; s++)
switch(*s) {
case 'x': except = 1; break;
case 'n': number = 1; break;
default:
printf
("find: optiune ilegala %c\n",
*s);
argc = 0;
break;
}
if (argc!=1)
printf
("Nu exista argumente sau schema\n");
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 fie 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 snt 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.
__________________________________________________________________________
101
102
103
{
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");
}
n apelul funciei sort, argumentele strcmp, numcmp i
swap snt adresele funciilor respective. Deoarece ele au fost
declarate funcii care returneaz un ntreg, operatorul & nu este
necesar s precead numele funciilor, compilatorul fiind cel care
gestioneaz transmiterea adreselor funciilor.
Funcia sort care aranjeaz liniile n ordinea cresctoare
se va modifica astfel:
sort(char *v[], int n, int (*comp)(),
int (*exch)()) { /* sorteaz v0, v1, ... , vn1 */
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 snt pointeri la funcii care returneaz
un ntreg (primul set de paranteze este necesar).
if (comp(v[j],v[j+gap])<=0)
__________________________________________________________________________
104
__________________________________________________________________________
105
106
107
108
109
(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), fiind din acest punct de vedere
foarte apropiai. Astfel, fiind 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 fi folosite pentru a
modifica ordinea operatorilor dat de precedena. Astfel:
(++p)->x
incrementeaz mai nti pe p i apoi acceseaz elementul x, din
structura nou pointat.
n expresia (p++)->x se acceseaz mai nti x, apoi se
incrementeaz pointerul p.
n mod analog, *p->y indic coninutul adresei pe care o indic
y. Expresia *p->y++ acceseaz mai nti ceea ce indic y i apoi
incrementeaz pe y. Expresia (*p->y)++ incrementeaz ceea ce
indic y. Expresia *p++->y acceseaz ceea ce indic y i apoi
incrementeaz pointerul p.
110
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 fiind un masiv de
perechi. Atunci, declaraia de structur:
struct key {
char *keyword;
int keycount;
} keytab[NKEYS];
definete 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.
Definiia masivului keytab poate fi 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 definit. Iniializarea
structurilor este o operaie analoag cu iniializarea unui masiv n
sensul c definiia este urmat de o list de iniializatori nchii n
acolade.
Atunci iniializarea masivului de structuri keytab va fi
urmtoarea:
struct key {
char * keyword;
int keycount;
} keytab[] = {
"break",0,
"case",0,
__________________________________________________________________________
111
"char",0,
/* ... */
"while",0};
Iniializatorii snt perechi care corespund la membrii structurii.
Iniializarea ar putea fi fcut i incluznd iniializatorii fiecrei
structuri din masiv n acolade ca n:
{"break",0},{"case",0}....
dar parantezele interioare nu snt necesare dac iniializatorii snt
variabile simple sau iruri de caractere i dac toi iniializatorii snt
prezeni.
Compilatorul va calcula, pe baza iniializatorilor, dimensiunea
masivului de structuri keytab motiv pentru care, la iniializare, nu
este necesar indicarea dimensiunii masivului.
Programul de numrare a cuvintelor cheie ncepe cu definirea
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
fie 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 1.
#define 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))
<0)
__________________________________________________________________________
112
high = mid - 1;
else
if (cond>0) low = mid + 1;
else
return mid;
}
return -1;
}
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))
>=0)
keytab[n].keycount++;
for (n=0; n<NKEYS; n++)
if (keytab[n].keycount>0)
printf("%4d %s\n",
keytab[n].keycount,
keytab[n].keyword);
}
nainte de a scrie funcia getword este suficient s spunem c
ea returneaz constanta simbolic LETTER de fiecare dat cnd
gsete un cuvnt n textul de intrare i copiaz cuvntul n primul ei
argument.
Cantitatea NKEYS este numrul cuvintelor cheie din 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 modificrilor. 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
__________________________________________________________________________
113
__________________________________________________________________________
114
115
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\n",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-1
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 modificare semnificativ n funcia binary. Calculul
elementului mijlociu nu se mai poate face simplu prin:
mid = (low + high) / 2
__________________________________________________________________________
116
117
118
119
120
}
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 fiecare
nod se imprim sub-arborele lui stng, adic toate cuvintele mai mici
dect cuvntul curent, apoi cuvntul curent i la sfrit sub-arborele
drept, adic toate cuvintele mai mari dect cuvntul curent. Rutina
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\n",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 fiecare 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 suficient de bine recursivitatea,
desenai-v un arbore i imprimai-l cu ajutorul rutinei treeprint,
avnd grij s memorai fiecare ieire din tree i treeprint.
O observaie legat de acest exemplu: dac arborele este
nebalansat, adic cuvintele nu sosesc n ordine aleatoare din punct
__________________________________________________________________________
121
122
123
__________________________________________________________________________
124
125
126
do {
getword(num); getword(def);
if ((np=lookup(num))==NULL)
printf("New name\n");
else
printf("Old definition: %s\n",
np->def);
install(num,def);
} while (1);
}
10.7. Cmpuri
Un cmp se definete ca fiind o mulime de bii consecutivi dintrun cuvnt sau ntreg. Adic din motive de economie a spaiului de
memorie, este util mpachetarea unor obiecte ntr-un singur cuvnt
main. Un caz frecvent de acest tip este utilizarea unui set de flaguri,
fiecare pe un bit, pentru tabela de simboluri a unui compilator.
Fiecare simbol dintr-un program are anumite informaii asociate
lui, cum snt de exemplu, clasa de memorie, tipul, dac este sau nu
cuvnt cheie .a.m.d. Cel mai compact mod de a codifica aceste
informaii este folosirea unui set de flaguri, de cte un bit, ntr-un
singur ntreg sau caracter.
Modul cel mai uzual pentru a face acest lucru este de a defini un
set de mti, fiecare masc fiind corespunztoare poziiei bitului m
interiorul caracterului sau cuvntului. De exemplu:
#define KEYWORD 01
#define EXTERNAL 02
#define STATIC 04
definesc mtile KEYWORD, EXTERNAL i STATIC care se refer la
biii 0, 1 i respectiv 2 din caracter sau cuvnt. Atunci accesarea
acestor bii se realizeaz cu ajutorul operaiilor de deplasare, mascare
i complementare, descrii ntr-un capitol anterior. Numerele trebuie
s fie puteri ale lui 2.
Expresii de forma:
__________________________________________________________________________
127
__________________________________________________________________________
128
10.8. Reuniuni
O reuniune este o variabil care poate conine, la momente
diferite, obiecte de diferite tipuri i dimensiuni; compilatorul este cel
care ine evidena dimensiunilor i aliniamentului.
Reuniunile ofer posibilitatea ca mai multe tipuri diferite de date
s fie tratate ntr-o singur zon de memorie, fr a folosi n program
vreo informaie dependent de main.
S relum exemplul tabelei de simboluri a unui compilator,
presupunnd c constantele pot fi de tip int, float sau iruri de
caractere.
__________________________________________________________________________
129
130
131
132
10.10. Typedef
Limbajul C ofer o facilitate numit typedef pentru a crea noi
nume de tipuri de date. Specificatorul de tip typedef-nume are
sintaxa:
typedef-nume:
declarator
ntr-o declaraie implicnd typedef fiecare identificator care
apare ca parte a unui declarator devine sintactic echivalent cu
cuvntul cheie rezervat pentru tipul asociat cu identificatorul. De
exemplu, declaraia:
typedef int LENGTH;
l face pe LENGTH sinonim cu int. Tipul LENGTH poate fi 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 fi 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
__________________________________________________________________________
133
/* pointer la text */
/* numr apariii */
/* descendent stng */
/* descendent drept */
134
__________________________________________________________________________
135
136
__________________________________________________________________________
137
138
Nume
fclose - nchide un flux
Declaraie
int fclose( FILE *flux);
Descriere
Funcia fclose nchide fiierul asociat fluxului flux. Dac
flux a fost deschis pentru ieire, orice date aflate n zone tampon
snt scrise n fiier n prealabil cu un apel fflush.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global errno indic codul erorii.
Nume
tmpfile - creeaz un fiier temporar
Declaraie
FILE *tmpfile();
Descriere
Funcia tmpfile genereaz un nume unic de fiier temporar.
Acesta este deschis n mod binar pentru scriere / citire ("wb+").
Fiierul va fi ters automat la nchidere sau la terminarea
programului.
Valoare returnat
Funcia returneaz un descriptor de flux n caz de succes, sau
NULL dac nu poate fi generat un nume unic de fiier sau dac
__________________________________________________________________________
139
Nume
fflush - foreaz scrierea n flux
Declaraie
int fflush(FILE *flux);
Descriere
Funcia fflush foreaz o scriere a tuturor datelor aflate n zone
tampon ale fluxului flux. Fluxul rmne deschis.
Valori returnate
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global errno indic codul erorii.
Nume
fseek, ftell, rewind - repoziioneaz un flux
Declaraie
int fseek(FILE *flux, long offset,
int reper);
long ftell(FILE *flux);
void rewind(FILE *flux);
Descriere
Funcia fseek seteaz indicatorul de poziie pentru fiierul
asociat fluxului flux. Noua poziie, dat n octei, se obine adunnd
offset octei la poziia specificat de reper. Dac reper este
SEEK_SET, SEEK_CUR, sau SEEK_END, offset este relativ la
nceputul fiierului, poziia curent a indicatorului, respectiv sfritul
fiierului. Funcia fseek terge indicatorul de sfrit de fiier.
Funcia ftell obine valoarea curent a indicatorului de poziie
pentru fiierul asociat fluxului flux.
__________________________________________________________________________
140
__________________________________________________________________________
141
Nume
fputs - scrie un ir de caractere ntr-un flux text
Declaraie
int fputs(const char *s, FILE *flux);
Descriere
Funcia fputs scrie irul s n flux fr caracterul terminator
null.
Apeluri ale acestei funcii pot fi combinate cu orice apeluri ale
altor funcii de ieire din bibliotec (fprintf, de exemplu) pentru
un acelai flux de ieire.
Valori returnate
Funcia returneaz o valoare non-negativ n caz de succes, sau
EOF n caz de eroare.
Nume
fread, fwrite - intrri / ieiri pentru fluxuri binare
Declaraie
unsigned fread(void *ptr, unsigned size,
unsigned nel, FILE *flux);
unsigned fwrite(const void *ptr, unsigned
size, unsigned nel, FILE *flux);
Descriere
Funcia fread citete nel elemente, fiecare avnd mrimea
size octei, din fluxul indicat de flux, i le memoreaz n zona
indicat de ptr.
Funcia fwrite scrie nel elemente, fiecare avnd mrimea
size octei, din fluxul indicat de flux, pe care le ia din zona
indicat de ptr.
Valori returnate
Funciile returneaz numrul de elemente citite sau scrise cu
succes (i nu numrul de caractere). Dac apare o eroare sau se
__________________________________________________________________________
142
__________________________________________________________________________
143
Conversii
Dup caracterul % care introduce o conversie poate urma un
numr de caractere indicatori, dup cum urmeaz:
*
__________________________________________________________________________
144
e,g Echivalent cu f.
s
__________________________________________________________________________
145
Valori returnate
Funciile returneaz numrul de valori atribuite, care poate fi mai
mic dect numrul de argumente pointer, sau chiar zero, n cazul n
care apar nepotriviri ntre format i irul de intrare. Zero indic faptul
c, chiar dac avem un ir de intrare disponibil, nu s-a efectuat nici o
conversie (i atribuire); aceast situaie apare atunci cnd un caracter
din irul de intrare este invalid, cum ar fi un caracter alfabetic pentru
o conversie %d. Valoarea EOF este returnat dac apare un eroare
nainte de prima conversie, cum ar fi detectarea sfritului de fiier.
Dac o eroare sau un sfrit de fiier apare dup ce o conversie a
nceput, se returneaz numrul de conversii efectuate cu succes.
__________________________________________________________________________
146
147
Caractere indicatori
Caracterul % este urmat de zero, unul sau mai muli indicatori:
#
__________________________________________________________________________
148
Limea cmpului
Un ir de cifre zecimale (cu prima cifr nenul) specific o lime
minim pentru cmp. Dac valoarea convertit are mai puine
caractere dect limea specificat, va fi completat cu spaii la stnga
(sau dreapta, dac s-a specificat aliniere la stnga). n locul unui
numr zecimal se poate folosi * pentru a specifica faptul c limea
cmpului este dat de argumentul urmtor, care trebuie s fie de tip
int. O valoare negativ pentru lime este considerat un indicator urmat de o valoare pozitiv pentru lime. n nici un caz nu se va
trunchia cmpul; dac rezultatul conversiei este mai mare dect
limea cmpului, cmpul este expandat pentru a conine rezultatul
conversiei.
Precizia
Precizia (opional) este dat de caracterul . urmat de un ir de
cifre zecimale. n locul irului de cifre zecimale se poate scrie *
pentru a specifica faptul c precizia este dat de argumentul urmtor,
care trebuie s fie 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 semnificative
pentru conversii de tip g i G, sau numrul maxim de caractere
generate pentru conversii de tip s.
Modificator de lungime
n acest caz prin conversie ntreag nelegem conversie de tip d,
i, o, u, x, X.
h
__________________________________________________________________________
149
Specificator de conversie
Un caracter care specific tipul conversiei care se va face.
Specificatorii de conversie i semnificaia lor snt:
d,i
Argumentul de tip int este convertit la notaia zecimal cu
semn. Precizia, dac este dat, d numrul minim de cifre care
trebuie s apar; dac valoarea convertit necesit mai puine
cifre, aceasta este completat la stnga cu zerouri. Precizia
implicit este 1. Dac valoarea 0 este afiat cu precizie explicit
0, ieirea este vid.
o,u,x,X
Argumentul de tip unsigned este convertit la notaie octal
fr semn (o), zecimal fr semn (u), sau hexazecimal fr
semn (x i X). Literele abcdef se folosesc pentru conversii de
tip x; literele ABCDEF pentru conversii de tip X. Precizia, dac
este dat, d numrul minim de cifre care trebuie s apar; dac
valoarea convertit necesit mai puine cifre, aceasta este
completat la stnga cu zerouri. Precizia implicit este 1. Dac
valoarea 0 este afiat cu precizie explicit 0, ieirea este vid.
e,E
Argumentul de tip flotant este rotunjit i convertit n stil
[-]d.dddedd unde avem o cifr nainte de punctul zecimal i
numrul de cifre dup acesta este egal cu precizia; dac aceasta
lipsete se consider 6; dac precizia este zero, punctul zecimal
nu apare. O conversie de tip E folosete litera E (n loc de e)
pentru a introduce exponentul. Exponentul are ntotdeauna cel
puin dou cifre; dac valoarea este zero, exponentul este 00.
f,F
Argumentul de tip flotant este rotunjit i convertit n notaie
zecimal n stil [-]ddd.ddd, unde numrul de cifre dup punctul
zecimal este egal cu precizia specificat. Dac precizia lipsete se
__________________________________________________________________________
150
Valoare returnat
Funciile returneaz numrul de caractere generate (nu se include
caracterul terminator null pentru sprintf).
__________________________________________________________________________
151
Nume
clearerr, feof, ferror - verific i reseteaz starea
fluxului
Declaraie
void clearerr(FILE *flux);
__________________________________________________________________________
152
153
Nume
opendir - deschide un director
Declaraie
DIR *opendir(const char *nume);
Descriere
Funcia opendir deschide un flux pentru directorul cu numele
nume, i returneaz un pointer la fluxul deschis. Fluxul este
poziionat pe prima intrare din director.
Valoare returnat
Funcia returneaz un pointer la flux n caz de succes, sau NULL
n caz de eroare i variabila global errno indic codul erorii.
__________________________________________________________________________
154
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
fluxul dir. Returneaz NULL dac s-a depistat sfritul de director
sau dac a aprut o eroare.
Structura de tip dirent conine un cmp char d_name[].
Utilizarea altor cmpuri din structur reduce portabilitatea
programelor.
Valoare returnat
Funcia returneaz un pointer la o structur de tip dirent, sau
NULL dac s-a depistat sfritul de director sau dac a aprut o
eroare.
Nume
closedir - nchide un director
Declaraie
int closedir(DIR *dir);
Descriere
Funcia closedir nchide fluxul dir.
Valoare returnat
Funcia returneaz 0 n caz de succes sau EOF n caz de eroare.
__________________________________________________________________________
155
Nume
rename - redenumete un fiier
remove - terge un fiier
Declaraie
int rename(const char *old, const char
*new);
int remove(const char *name);
Descriere
Funcia rename schimb numele unui fiier din old n new.
Dac a fost precizat un periferic n new, acesta trebuie s coincid cu
cel din old. Directoarele din old i new pot s fie diferite, astfel c
rename poate fi folosit pentru a muta un fiier dintr-un director n
altul. Nu se permit specificatori generici (wildcards).
Funcia remove terge fiierul specificat prin name.
Valoare returnat
n caz de succes se returneaz 0. n caz de eroare se returneaz
EOF i variabila global errno indic codul erorii.
156
f = fopen(av[1],"rb");
if (!f) {
perror("Eroare la deschidere");
return 1;
}
fseek(f,0,SEEK_END);
fprintf(stderr,"File %s, size %ld\n",
ftell(f));
fclose(f);
return 0;
}
2) Copierea unui fiier
Funciile fgets i fputs se folosesc pentru fluxuri deschise n
mod text. Cum se utilizeaz pentru copierea unui fiier text?
#include <stdio.h>
#define LSIR 80
char lin[LSIR];
FILE *fi, *fo;
int main(int ac, char **av) {
if (ac!=3) {
fputs("Doua argumente!\n",stderr);
}
fi=fopen(av[1],"rt"); fo=fopen(av[2],"wt");
if (!fi || !fo) {
perror("Eroare la deschidere");
return 1;
}
while (fgets(lin,LSIR,fi))
fputs(lin,fo);
fclose(fi); fclose(fo);
return 0;
}
Funciile fread i fwrite se folosesc pentru fluxuri deschise
n mod binar. Cum se utilizeaz pentru copierea unui fiier binar?
__________________________________________________________________________
157
#include <stdio.h>
#define LZON 80
char zon[LZON];
FILE *fi, *fo;
int k;
int main(int ac, char **av) {
if (ac!=3) {
fputs("Doua argumente!\n",stderr);
return 1;
}
fi=fopen(av[1],"rb");
fo=fopen(av[2],"wb");
if (!fi || !fo) {
perror("Eroare la deschidere");
return 1;
}
while (k=fread(zon,1,LZON,fi))
fwrite(zon,1,k,fo);
fclose(fi); fclose(fo);
return 0;
}
3) Prelucrarea unui fiier text
Programul prezentat n continuare citete un fiier text care
conine pe fiecare linie un ir de caractere (fr spaii) i trei valori
ntregi, i afieaz pe terminal numele pe 12 poziii aliniat la stnga i
media aritmetic a celor trei valori ntregi.
#include <stdio.h>
FILE *fi;
char num[10];
int a,b,c;
double m;
int main(int ac, char **av) {
if (ac!=2) {
fputs("Un argument!\n",stderr);
__________________________________________________________________________
158
return 1;
}
fi=fopen(av[1],"rt");
if (!fi) {
perror("Eroare la deschidere");
return 1;
}
while (fscanf(fi,"%s %d %d %d",
num,&a,&b,&c)!=EOF) {
m=(a+b+c)/3.0;
printf("%-12s%6.2lf\n",num,m);
}
fclose(fi);
return 0;
}
4) Afiarea coninutului unui director
#include <dirent.h>
#include <stdio.h>
DIR *dir;
struct dirent *ent;
int main(int ac, char **av) {
if (ac!=2) {
printf("Un parametru\n");
return 1;
}
dir = opendir(av[1]);
if (!dir) {
perror("Eroare open dir");
return 1;
}
while (ent=readdir(dir))
printf("%s\n",ent->d_name);
return 0;
}
__________________________________________________________________________
159
160
care este eventual alocat este neiniializat. Dac ptr este NULL
apelul este echivalent cu malloc(size); dac size este egal cu
zero apelul este echivalent cu free(ptr). Cu excepia cazului cnd
ptr este NULL, acesta trebuie s fi fost returnat de un apel precedent
malloc, calloc sau realloc.
Valori returnate
Pentru calloc i malloc valoarea returnat este un pointer la
memoria alocat, care este aliniat n mod corespunztor pentru orice
tip de variabile, sau NULL dac nu exist suficient memorie
continu.
Funcia free nu returneaz nimic.
Funcia realloc returneaz un pointer la noua zon de
memorie alocat, care este aliniat n mod corespunztor pentru orice
tip de variabile, i poate fi diferit de ptr, sau poate fi NULL dac nu
exist suficient memorie continu sau dac valoarea size este
egal cu 0. Dac realloc eueaz, blocul original rmne neatins nu este nici eliberat nici mutat.
161
162
Declaraie
#include <ctype.h>
int isalnum(int c);
int isalpha(int c);
int isascii(int c);
int iscntrl(int c);
int isdigit(int c);
int isgraph(int c);
int
int
int
int
int
int
islower(int c);
isprint(int c);
ispunct(int c);
isspace(int c);
isupper(int c);
isxdigit(int c);
Descriere
Primele 12 funcii verific dac c, care trebuie s fie o valoare de
tip unsigned char sau EOF, se afl n una din clasele de caractere
enumerate mai sus.
isalnum
Verific dac c este alfanumeric;
(isalpha(c) || isdigit(c)).
este
echivalent
cu
isalpha
Verific dac c este alfabetic; este echivalent cu (isupper(c)
|| islower(c)).
isascii
Verific dac c este o valoare pe 7 bii din setul de caractere
ASCII.
iscntrl
Verific dac c este un caracter de control.
isdigit
Verific dac c este o cifr (ntre 0 i 9).
isgraph
Verific dac c este un caracter afiabil cu excepia spaiului.
islower
Verific dac c este o liter mic.
__________________________________________________________________________
163
isprint
Verific dac c este un caracter afiabil inclusiv spaiu.
ispunct
Verific dac c este un caracter diferit de spaiu i nonalfanumeric.
isspace
Verific dac c este un spaiu alb.
isupper
Verific dac c este o liter mare.
isxdigit
Verific dac c este o cifr hexazecimal din setul 0 1 2 3 4 5 6 7
8 9 a b c d e f 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 afl n clasa testat, i zero n caz contrar.
Valoarea returnat de funciile to... este litera convertit dac
caracterul c este o liter, i nedefinit n caz contrar.
__________________________________________________________________________
164
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
__________________________________________________________________________
165
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.
Nume
strlen - calculeaz lungimea unui ir
Declaraie
unsigned strlen(const char *s);
Descriere
__________________________________________________________________________
166
Nume
strcpy, strncpy - copiaz un ir de caractere
Declaraie
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src,
unsigned n);
Descriere
Funcia strcpy copiaz irul indicat de src (inclusiv
caracterul terminator null) n zona indicat de dest. irurile nu
trebuie s se suprapun, i n plus zona dest trebuie s fie suficient
de mare pentru a primi copia.
Funcia strncpy este similar, cu excepia faptului c nu se
copiaz mai mult de n octei din src. Astfel, dac caracterul
terminator null nu se afl n primii n octei din src, rezultatul nu va
fi terminat cu null. n cazul n care lungimea lui src este mai mic
dect n, restul octeilor din dest primesc valoarea null.
Valoare returnat
Funciile returneaz un pointer la irul dest.
Nume
strdup - duplic un ir
Declaraie
char *strdup(const char *s);
Descriere
Funcia strdup returneaz un pointer la un nou ir care este un
duplicat al irului s. Memoria pentru noul ir se obine cu malloc,
i poate fi eliberat cu free.
__________________________________________________________________________
167
Valoare returnat
Funcia returneaz un pointer la irul duplicat, sau NULL dac nu
exist memorie suficient disponibil.
Nume
strcat, strncat - concateneaz dou iruri
Declaraie
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src,
unsigned n);
Descriere
Funcia strcat adaug irul src la irul dest suprascriind
caracterul null de la sfritul lui dest, i la sfrit adaug un caracter
terminator null. irurile nu trebuie s se suprapun, i n plus irul
dest trebuie s aib suficient spaiu pentru a pstra rezultatul.
Funcia strncat este similar, cu excepia faptului c numai
primele n caractere din src se adaug la dest.
Valoare returnat
Funciile returneaz un pointer la irul rezultat dest.
Nume
strcmp - compar dou iruri de caractere
Declaraie
int strcmp(const char *s1, const char
*s2);
Descriere
Funcia strcmp compar cele dou iruri s1 i s2.
Valoare returnat
Funcia returneaz un ntreg mai mic dect, egal cu, sau mai mare
dect zero dac s1 este mai mic dect, coincide, respectiv este mai
mare dect s2.
__________________________________________________________________________
168
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
__________________________________________________________________________
169
*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 afl n acc.
Funcia strcspn returneaz poziia primului caracter din s care
se afl n rej.
Nume
rand, srand - generarea numerelor pseudo-aleatoare
Declaraie
int rand(void);
void srand(unsigned int seed);
Descriere
Funcia rand returneaz un ntreg pseudo-aleator ntre 0 i
RAND_MAX (pentru majoritatea mediilor de programare C aceast
constant este egal cu valoarea maxim cu semn reprezentabil pe
un cuvnt al sistemului de calcul).
Funcia srand iniializeaz generatorul cu valoarea seed
pentru o nou secven de valori ntregi pseudo-aleatoare care vor fi
returnate de rand. Aceste secvene se repet dac srand se apeleaz
cu aceeai valoare seed.
Se obinuiete ca generatorul s fie iniializat cu o valoare dat
de ceasul sistemului de calcul, ca n exemplul de mai jos:
__________________________________________________________________________
170
#include <time.h>
srand(time(NULL));
Valoare returnat
Funcia rand returneaz o valoare ntre 0 i RAND_MAX.
Observaie
n lucrarea Numerical Recipes in C: The Art of Scientific
Computing - William H Press, Brian P Flannery, Saul A Teukolsky,
William T Vetterling / New York: Cambridge University Press, 1990
(1st ed, p 207), se face urmtorul comentariu:
"Dac dorii s generai o valoare aleatoare ntreag ntre 1 i 10,
se recomand s folosii secvena
j=1+(int)(10.0*rand()/(RAND_MAX+1.0));
i nu o secven de tipul
j=1+(int)(1000000.0*rand())%10;
care folosete biii de rang inferior."
Tot n fiierul <stdlib.h> snt 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 snt descrise n fiierul
<math.h>.
double
double
double
double
double
double
double
double
__________________________________________________________________________
171
__________________________________________________________________________
172
173
free(X);
return 0;
}
2) S relum al treilea exemplu din capitolul precedent. Se citete
un fiier text care conine pe fiecare linie un nume (ir de caractere
fr spaiu) i trei valori reale (note). Pentru fiecare linie se
calculeaz media aritmetic a celor trei valori i se determin dac
elementul este admis (fiecare not este minimum 5) sau respins (cel
puin o not este sub 5). n final se afieaz liniile n ordinea
urmtoare: mai nti elementele admise n ordinea descresctoare a
mediei, i apoi elementele respinse n ordine alfabetic dup nume.
Se afieaz doar numele, situaia (A/R) i media.
n acest exemplu punem n eviden o modalitate comod de
selectare a membrilor unei structuri cu ajutorul macrourilor. Macroul
Fld selecteaz din zona referit de pointerul P membrul f. Deoarece
pointerul P (care poate fi argumentul A sau B al funciei comp) refer
o zon de tip void, este necesar mai nti un cast pentru a preciza
tipul concret al acesteia. Membrul f poate fi: nm, ar, md, definii n
cadrul structurii de tip StEl.
Deoarece nu tim de la nceput cte linii are fiierul de intrare,
sntem nevoii s folosim urmtoarea strategie. La nceput alocm
pentru tabloul El o zon care s memoreze NA elemente. Pe msur
ce aceast zon se completeaz, la un moment dat numrul de linii
citite coincide cu NA. n acest moment se aloc o zon nou care s
poat memora un numr mai mare de elemente. Desigur, de fiecare
dat se va actualiza mrimea spaiului alocat.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define NA 32
typedef struct {
char nm[10], ar;
float na, nb, nc, md;
} StEl;
__________________________________________________________________________
174
175
ne++;
if (ne==na) {
na+=NA;
El=(StEl *)realloc(El,na*
sizeof(StEl));
}
}
fclose(fi);
qsort(El,ne,sizeof(StEl),comp);
for (i=0; i<ne; i++)
printf("%-12s %c%6.2lf\n",
El[i].nm,El[i].ar,El[i].md);
free(El);
return 0;
}
3) Se citete dintr-un fiier text o valoare natural n. Urmtoarele
linii conin n cuvinte, fiecare cuvnt avnd acelai numr de litere (cel
mult 10). S se afieze cuvintele din fiier ordonate alfabetic.
Pentru a memora lista de cuvinte folosim urmtoarea strategie. n
loc s alocm pentru fiecare cuvnt (ir de caractere) citit o zon nou
de memorie, alocm de la nceput o zon n care s putem memora
toate cuvintele din list. Aceast zon va avea mrimea de (l+1)*n
octei, unde l este lungimea fiecrui cuvnt (numrul de litere). De ce
(l+1)? pentru c trebuie s memorm i caracterul terminator null.
Avantaje: memoria este utilizat mai eficient dac se aloc de la
nceput o zon contigu de dimensiune mai mare, i se reduce foarte
mult fragmentarea memoriei.
#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];
__________________________________________________________________________
176
int n,l,i;
FILE *fi;
if (ac!=2) {
fputs("Un argument!\n",stderr);
return 1;
}
fi=fopen(av[1],"rt");
if (!fi) {
perror("Eroare la deschidere");
return 1;
}
fscanf(fi,"%d %s",n,s);
l=strlen(s);
C=(char *)malloc((l+1)*n);
Strcpy(C,s);
for (i=1; i<n; i++)
fscanf(fi,"%s",C+(l+1)*i);
fclose(fi);
qsort(C,n,l+1,comp);
for (i=0; i<n; i++)
printf("%s\n",C+(l+1)*i);
free(C);
return 0;
}
__________________________________________________________________________
177
Bibliografie
Brian W Kernigham, Dennis M Ritchie - The C Programming Language
Prentice-Hall Software Series, 1978
- Limbajul C; manual de programare
Institutul de tehnic de calcul, Cluj-Napoca 1984
Herbert Schildt - Manual C complet
Editura Teora, 1998
Manuale electronice
http://www.programmingtutorials.com/c.html
Marshall Brain - Introduction to C Programming
http://devcentral.iftech.com/learning/tutorials/c-cpp/c/
Steve Summit - Introductory C Programming Class Notes
http://www.eskimo.com/~scs/cclass/cclass.html
Steve Summit - Intermediate C Programming Class Notes
http://www.eskimo.com/~scs/cclass/cclass.html
Brian Brown - C Programming
http://www.cit.ac.nz/smac/cprogram/onlinet.htm
Brian Brown - An Introduction to C Programming
http://www.cit.ac.nz/smac/cprogram/default.htm
__________________________________________________________________________
178
Cuprins
1. Generaliti asupra limbajului C . . . . . . . . . . . . . . . . . . . 4
1.1. Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2. Primele programe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3. Meta-limbajul i setul de caractere . . . . . . . . . . . . . . . . . . . . 9
3. Variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.1. Clase de memorie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2. Tipuri de variabile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.3. Obiecte i valori-stnga . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.4. Conversii de tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4. Operatori i expresii . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.1. Expresii primare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
4.2. Operatori unari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.3. Operatori multiplicativi . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4. Operatori aditivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.5. Operatori de deplasare . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.6. Operatori relaionali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
4.7. Operatori de egalitate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.8. Operatorul I pe bii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.9. Operatorul SAU-exclusiv pe bii . . . . . . . . . . . . . . . . . . . . 35
4.10. Operatorul SAU-inclusiv pe bii . . . . . . . . . . . . . . . . . . . . 36
4.11. Operatorul I-logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.12. Operatorul SAU-logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.13. Operatorul condiional . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.14. Operatori de atribuire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
__________________________________________________________________________
179
5. Declaraii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.1. Specificatori de clas de memorie . . . . . . . . . . . . . . . . . . . 43
5.2. Specificatori de tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
5.3. Declaratori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.4. Modificatorul const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.5. Iniializare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
5.6. Nume-tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6. Instruciuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.1. Instruciunea expresie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
6.2. Instruciunea compus sau blocul . . . . . . . . . . . . . . . . . . . . 51
6.3. Instruciunea condiional . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.4. Instruciunea while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.5. Instruciunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
6.6. Instruciunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6.7. Instruciunea switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.8. Instruciunea break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.9. Instruciunea continue . . . . . . . . . . . . . . . . . . . . . . . . . . 58
6.10. Instruciunea return . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
6.11. Instruciunea vid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
180
9. Pointeri i masive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
9.1. Pointeri i adrese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
9.2 Pointeri i argumente de funcii . . . . . . . . . . . . . . . . . . . . . . 77
9.3. Pointeri i masive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
9.4. Aritmetica de adrese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
9.5. Pointeri la caracter i funcii . . . . . . . . . . . . . . . . . . . . . . . . 83
9.6. Masive multidimensionale . . . . . . . . . . . . . . . . . . . . . . . . . 86
9.7. Masive de pointeri i pointeri la pointeri . . . . . . . . . . . . . . 88
9.8. Iniializarea masivelor i masivelor de pointeri . . . . . . . . . 93
9.9. Masive de pointeri i masive multidimensionale . . . . . . . . 96
9.10. Argumentele unei linii de comand . . . . . . . . . . . . . . . . . 97
9.11. Pointeri la funcii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
181
__________________________________________________________________________
182